iT邦幫忙

2021 iThome 鐵人賽

DAY 27
0
Software Development

Rust的多方面運用系列 第 27

[Day 27] Bevy 遊戲引擎 (Part 1)

昨天大概講完 Rocket 的運用了
所以接下來就來介紹其他東西吧
這次要講的是 Game Engine
目前 Rust 裡面比較大的遊戲引擎有兩個

  • RG3D
  • Bevy

這次我會介紹 Bevy 因為他比較簡單易用
而 RG3D 他可以應付比較大的場景
也有編輯器
整體來說目前 RG3D 的前景是良好的
而 Bevy 也正在快速發展中
Bevy 是以 ECS 存儲和管理所有數據
簡單來說就是在資料庫中。不同數據類型以表的「列」,並且可以存在包含實體的「行」。
比如說給予一個玩家結構血量 還有攻擊力跟防禦值 這樣的概念

那本篇使用版本是 Bevy 0.5.0
且本次不會介紹 Example 直衝專案
那專案的部份我是直接去撰寫的所以可能進度上比較緩慢 可能 30 天內寫不完
但是我盡量
讓我們開始吧!
首先起手式

$ cargo add bevy

就這樣 不用添加其他東西了 應該目前也只會用到這個
我想做的是類似於 Undertale 對決 Undyne 時的小遊戲
但是簡化很多 就單純箭頭飄過去而已

大概這種感覺
好OK 開始ㄅ
今天會做的是 創建會移動的物件

初始化

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)))
        .run();
}

首先會長這樣
就是我們的 main function
那這邊主要是創建一個視窗
並且我們給他 長寬 標題以及顏色

fn setup(mut commands: Commands) {
    commands
        .spawn_bundle(OrthographicCameraBundle::new_2d())
        .commands()
        .spawn_bundle(UiCameraBundle::default());
}

然後這邊是設置 camera 的部份
因為我們是 2D 遊戲所以就直接設置一個 2D 場景


材質

那麼由於我們 ECS 的特性所以在我們要使用材質的時候也要對物件歸類

pub struct ArrowMaterial {
    up: Handle<ColorMaterial>,
    down: Handle<ColorMaterial>,
    left: Handle<ColorMaterial>,
    right: Handle<ColorMaterial>,
}

其實不一定要用 pub 但是我只是怕 Debug 麻煩

impl FromWorld for ArrowMaterial {
    fn from_world(world: &mut World) -> Self {
        let world = world.cell();
        let mut material = world.get_resource_mut::<Assets<ColorMaterial>>().unwrap();
        let asset_server = world.get_resource::<AssetServer>().unwrap();
        let up = asset_server.load("up.png");
        let down = asset_server.load("down.png");
        let left = asset_server.load("left.png");
        let right = asset_server.load("right.png");
        ArrowMaterial {
            up: material.add(up.into()),
            down: material.add(down.into()),
            left: material.add(left.into()),
            right: material.add(right.into()),
        }
    }
}

至於這邊的部份可以看到說我讓他去讀取了圖片資源
而資源位置會位於 assets 資料夾中

也就是現在檔案目錄應該要長這樣
這邊的部份沒有什麼技術成份 直接抄就好了
雖然當初也是想很久 第一次寫的時候

物件

pub fn spawn_arrow_up(mut commands: Commands,
                      materials: Res<ArrowMaterial>,
                      time: Res<Time>,
                      mut timer: ResMut<SpawnTimer>) {
    if timer.0.tick(time.delta()).just_finished() {
        let transform = Transform::from_translation(Vec3::new(0., -450., 1.));
        commands
            .spawn_bundle(SpriteBundle {
                material: materials.up.clone(),
                sprite: Sprite::new(Vec2::new(100., 100.)),
                transform,
                ..Default::default()
            })
            .insert(UpArrow);
    }
}

那麼既然講完材質 固然要講一下如何讓物件出現在場景上
而這邊我讓他去讀秒 也就是會隨著時間生成

pub struct SpawnTimer(pub Timer);

所以記得要定義好 Struct
這邊可以看到說我使用了一個 transform
而也代表了物件的 x, y, z
至於 z 的部份由於我們是 2D 遊戲所以不會有任何影響
然後材質記得要用 clone 的新增
而 sprite 是大小的意思 可以自己設定

移動物件

fn up_arrows(time: Res<Time>, mut query: Query<(&mut Transform, &UpArrow)>) {
    for (mut transform, _arrow) in query.iter_mut() {
        transform.translation.y += time.delta_seconds() * 200.;
    }
}

而這邊的部份就比較簡單了
就是隨著時間去更改他的位置
而也因為他時間是使用 float 所以看起來會是緩慢移動的感覺
後面的 200. 是速度
然後上面的 for 迴圈是 tuple 是因為他包含了兩個項目
總之大概這樣


upd

早上起來才發現有地方少講
基本上要使用弄好的 function
就只要在 main function 裡面 在 App build 那邊的最下面使用 .add_system(<函數名稱>.system())
要注意的是 函數名稱不包含 ()
明天我們來講另一個統整的方法

而最後運行會長這樣

而其他四個方向就由讀者自行操作了
今天大概這樣 打 BNT 手有點痛


上一篇
[Day26] 用 Rocket 做一個圖書館門禁後端 (Part 3)
下一篇
[Day 28] Bevy 遊戲引擎 (Part 2)
系列文
Rust的多方面運用30

尚未有邦友留言

立即登入留言