iT邦幫忙

2021 iThome 鐵人賽

DAY 29
0
Software Development

Rust的多方面運用系列 第 29

[Day29] Bevy 遊戲引擎 (Part 3) 收工

好 那今天就是專案的收尾了
我先預告一下明天會把一些我從開始學習 Rust 之後陸續得到的學習資源
也就是套件庫 以及 教學 Blog
OK 我們繼續昨天的內容


昨天我們做出了能夠上下左右改變方向的玩家
那麼其實如果要改變位置只要改一小段昨天的代碼改就好了
那今天要做的是 讓箭頭消失 和 記分板
首先先講 讓箭頭消失好了 這個比較簡單
一樣 我就只講一個方向 因為大同小異

fn despawns_monster_up(
    mut commands: Commands,
    mut arrow_positions: Query<(Entity, &mut Transform, &UpArrow), Without<crate::characters::Main>>,
    character: Query<(Entity, &Transform, &crate::characters::Main), Without<UpArrow>>,
    mut score: ResMut<ScoreBoard>,
){
    let (_entity, transform, _character) = character.single().expect("");
    let path = transform.rotation.w;
    for (entity, transform, _arrow) in arrow_positions.iter_mut() {
        let posx = transform.translation.x;
        let posy = transform.translation.y;
        if -100. <= posy && posy <= 0. && path == (-0.70710677)
        {
            score.add_score();
            commands.entity(entity).despawn();
        }
        if posy >= 0. && posx == 0. {
            commands.entity(entity).despawn();
        }
    }
}

可以看到說 我引入了他的角色跟箭頭
因為我們需要角色的轉角跟箭頭的位置
而之中可以看到我們抓了 rotation.w 這個是我在 Character 的 movement 裡面測出來的

if key_input.just_pressed(KeyCode::W) {
            transform.rotation = Quat::from_rotation_z(0.5 * PI);
            println!("{}", transform.rotation);
}

後面三個方向也是

那麼當轉向的時候就會輸出這些
之後再去看官方的 docs 就能得到角度了
而中間 path 不是使用 PI * -0.5
是因為會有浮點數誤差 所以就改成以輸出的數值為主
然後我是設定說 當到原點時 消失 當方向一致 然後物件在下方 100. 的位置時 消失並且加分
那麼中間那行 add_score 就是計分板的部份了


這邊我定義出了

pub struct ScoreBoard {
    score: usize,
}

impl ScoreBoard {
    pub fn add_score(&mut self) {
        self.score += 1;
    }
    pub fn get_score(&self) -> usize {
        self.score
    }
}

上面是記分板的功能
獲取分數跟增加分數
當然 讀者也能自行增加
那麼下面這邊比較複雜

pub fn score_ui(
    mut commands: Commands,
    asset_server: ResMut<AssetServer>,
    mut color_materials: ResMut<Assets<ColorMaterial>>,)
{
    let font: Handle<Font> = asset_server.load("font.ttf");
    let material = color_materials.add(Color::NONE.into());
    commands
        .spawn_bundle(NodeBundle {
            style: Style {
                position_type: PositionType::Absolute,
                position: Rect {
                    left: Val::Px(10.),
                    top: Val::Px(10.),
                    ..Default::default()
                },
                ..Default::default()
            },
            material: material.clone(),
            ..Default::default()
        })
        .with_children(|parent| {
            parent
                .spawn_bundle(TextBundle {
                    text: Text::with_section(
                        "Score: 0",
                        TextStyle {
                            font_size: 40.0,
                            font: font.clone(),
                            color: Color::rgb(0.9, 0.9, 0.9),
                        },
                        Default::default(),
                    ),
                    ..Default::default()
                })
                .insert(ScoreText);
        });
}

因為我們要做出整個 UI 界面
所以我們要定義出 這個 UI 的位置 然後字體之類等等
而上方的設定是位於左上角
字體是我隨便在 Google 上抓的
然後可以看到說 下面的 text 是我們要的輸出 而 Score: 0 是預設值

pub struct ScoreText;
pub fn update_score_text(score: Res<ScoreBoard>, mut query: Query<(&mut Text, &ScoreText)>) {
    if !score.is_changed() {
        return;
    }
    for (mut text, _marker) in query.iter_mut() {
        text.sections[0].value = format!(
            "Score: {}",
            score.get_score(),
        );
    }
}

那 ScoreText 我定義在下面
而這個 function 的用途就是確認有沒有更動 如果有就去找他現在的值


最後就是初始化的部份了

fn main() {
    App::build()
        .insert_resource(WindowDescriptor {
            title: "owo!".to_string(),
            width: 800.,
            height: 600.,
            ..Default::default()
        })
        .insert_resource(ClearColor(Color::rgb(1., 0.4, 0.4)))
        .init_resource::<score::ScoreBoard>()
        .add_startup_stage("game_setup", SystemStage::single(characters::spawn_main.system()).with_system(score::score_ui.system()))
        .add_startup_system(setup.system())
        .add_plugins(DefaultPlugins)
        .add_plugin(characters::CharacterPlugin)
        //.add_plugin(score::UI)
        .add_plugin(arrow::ArrowsPlugin)
        .add_system(score::update_score_text.system())
        .run();
}

我的 main 函數長這樣
那麼可以看到除了之前的 character 的 Plugin 還有 Arrow 的 Plugin 以外

.init_resource::<score::ScoreBoard>()
        .add_startup_stage("game_setup", SystemStage::single(characters::spawn_main.system()).with_system(score::score_ui.system()))

這行格外的重要
這是讓他的資源去進行初始化,且在同一時機
game_setup 是我自己取的 沒什麼重要性
最後成品會長這樣

Source
OK 剩下一天
各位晚安


上一篇
[Day 28] Bevy 遊戲引擎 (Part 2)
下一篇
[Day30] 套件統整 && 簡單的完賽感言
系列文
Rust的多方面運用30

尚未有邦友留言

立即登入留言