ゲーム実装で身に付くプログラミング 3

「Rust」の「Bevy」でデスクトップゲーム「Color」を開発してみよう

第3回の今回はプログラミング言語「Rust」の「Bevy」クレートを使ってデスクトップゲーム「Color」を作る解説をします。

大西 武 (オオニシ タケシ)

6:30

はじめに

この連載では、さまざまなプログラミング言語やライブラリで同じゲームを開発することで、容易にそのプログラミング言語に入門します。

今回はプログラミング言語の「Rust」を使って、次のURLのようなColorゲームを作ります。こちらからアセットをダウンロードしておいてください。

Rustの文法については、「「TAURI」+「Rust」ではじめるデスクトップアプリ開発」の第3回第4回を参考にしてください。

Colorゲームについて

Colorゲームの内容は第1回でも解説した通り、ゲームを開始したら、赤緑青の色ブロックをクリックします。すると背景色にその色が混ぜられるので、どんどんクリックして真っ白な背景にしていこうというゲームです。

プログラミング言語について

第3回の今回はプログラミング言語Rustで「Bevy」クレートを使ってデスクトップアプリを開発します。Rustは事前にメモリ管理を徹底していて堅牢にエラーの出にくいアプリを開発できるプログラミング言語です。しかもコンパイル言語なのでプログラミング言語「C/C++」のように処理が高速です。

BevyのECSについて

Bevyは2Dや3Dゲームなどのアプリを作るためのクレート(Rustの追加機能)で、次の表のように「ECS(Entity Component System)」という仕組みで成り立っています。具体的にはサンプルコードで理解してください。Bevyはプログラミング言語Rustを使いこなせなければ、ちょっと理解が難しいかもしれません。

【ECS(Entity Component System)

Entity一意のIDを持つ、Componentの実在物
ComponentEntityに付与される成分
System用意したデータの中から任意のデータを引数にして処理を行う関数

開発のために必要となる環境の準備

今回必要となるRustと「Visual Studio Code」を準備します。「Visual Studio Code」は第1回でも解説しました。

Rustの準備

Rustのセットアップについては、「「TAURI」+「Rust」ではじめるデスクトップアプリ開発」の第1回を参考にしてください。

Visual Studio Codeの準備

こちらのURLからVisual Studio Codeをダウンロードしてインストールしてください。セットアップの手順は省略します(第1回を参照)。

Rustプロジェクトを作成しよう

次のコマンドを実行して「colorBevy」プロジェクトを新規作成します。それから次の階層図のように画像の「images」フォルダは「assets」フォルダに入れます。今回もサウンドはなしです。

・Rustプロジェクト新規作成コマンド

$ cargo new colorBevy

・階層図

colorBevyフォルダ
┠aeests(アセットはこのフォルダが参照される)
┃┗imagesフォルダ(画像フォルダ)
┃ ┠0.pngファイル(赤の色ブロック画像)
┃ ┠1.pngファイル(緑の色ブロック画像)
┃ ┠2.pngファイル(青の色ブロック画像)
┃ ┠GameOver.pngファイル(ゲームオーバー画像)
┃ ┠StageClear.pngファイル(ステージクリア画像)
┃ ┗Title.pngファイル(タイトル画像)
┠src(ソースフォルダ)
┃┗main.rsファイル(メインファイル)
┠target(ビルド生成フォルダ)
┠.gitignoreファイル
┠Cargo.lockファイル
┗Cargo.tomlファイル(設定ファイル)

次の設定ファイル「Cargo.toml」とサンプルコード「main.rs」ファイルをコーディングして、次のコマンドを実行してターミナルで起動すると図1のように真っ白なウィンドウが表示されます。

・実行コマンド

$ cargo run

・設定ファイル「Cargo.toml」

[package]
name = "colorBevy"
version = "0.1.0"
edition = "2021"
[dependencies]
bevy = "0.18.0"
rand = "0.10.0"

・サンプルコード「main.rs」ファイル

// クレート
use bevy::{
 prelude::*,
 color::palettes::basic::*,
};
// メイン
fn main() {
 App::new()
   .add_plugins(DefaultPlugins.set(WindowPlugin {
     primary_window: Some(Window {
       title: "Color".into(),
       ..default()
     }),
     ..default()
   }))
   .insert_resource(ClearColor(Color::from(WHITE)))
   .add_systems(Startup, setup)
   .run();
}
// 初期化
fn setup(mut commands: Commands) {
 commands.spawn(Camera2d);
}

【サンプルコードの解説】
「use」でクレートを読み込みやすくします。
「main」関数では「App」構造体の「new」コンストラクタでプラグインを追加し、主ウィンドウ表示を作成します。あわせてクリアカラー(背景色)を白に設定するリソースを挿入し、Startup(起動時)に「setup」関数を読み込みます。
「setup」関数で2D用のカメラをスポーン(発生させる)します。

タイトル画面を表示する

スタートアップ時にタイトル画像を読み込んで、図2のようにスポーンして表示します。ここでは定数、ゲームステート、リソース、「ColorItem」構造体、「ColorType」列挙型、「TitleVisual」構造体は宣言だけしておいて後で使いますが、解説しやすいようにここでコーディングしておきます。

・サンプルコード「main.rs」ファイル

// クレート
use bevy::{
 prelude::*,
 window::PrimaryWindow,
 color::palettes::basic::*,
};
use rand::RngExt;
// 定数
const SPRITE_SIZE: f32 = 50.0;
const BOUNDS_MARGIN: f32 = 100.0;
// 状態定義
#[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash, States)]
enum GameState {
 #[default]
 Title,
 MainLoop,
 GameOver,
 StageClear,
}
// リソース
#[derive(Resource)]
struct GameData {
 stage: usize,
 rgb_counts: Vec3,
 timer: Timer,
}
impl Default for GameData {
 fn default() -> Self {
   Self {
     stage: 2,
     rgb_counts: Vec3::ZERO,
     timer: Timer::from_seconds(1.5, TimerMode::Once),
   }
 }
}
// コンポーネント
#[derive(Component)]
struct ColorItem {
 color_type: ColorType,
 velocity: Vec2,
}
// 色タイプ
enum ColorType {
 Red,
 Green,
 Blue,
}
#[derive(Component)]
struct TitleVisual;
// メイン
fn main() {
 App::new()
   .add_plugins(DefaultPlugins.set(WindowPlugin {
     primary_window: Some(Window {
       title: "Color".into(),
       ..default()
     }),
     ..default()
   }))
   .init_state::<GameState>()
   .init_resource::<GameData>()
   .insert_resource(ClearColor(Color::from(WHITE)))
   .add_systems(Startup, setup)
   .run();
}
// 初期化
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
 commands.spawn(Camera2d);
 commands.spawn((
   Sprite::from_image(asset_server.load("images/Title.png")),
   Transform::from_xyz(0.0, 0.0, 10.0),
   TitleVisual,
 ));
}

【サンプルコードの解説】
「const」で定数を宣言します。
GameState列挙型でタイトル画面やメインループやゲームオーバーやステージクリアを宣言します。
リソース(資源)で面のstageプロパティ、背景色のrgb_countsプロパティ、タイマーのtimerプロパティをGameData構造体に宣言し、デフォルトの「default」メソッドを宣言します。
色ブロック1個ずつのColorItem構造体を宣言します。
Red、Green、Blueの列挙子を宣言します。
TitleVisual構造体を宣言します。
main関数のApp構造体のコンストラクタでGameStateを状態初期化し、GameDataをリソース初期化します。
setup関数で「images/Title.png」をアセットからスポーンします。add_systemsメソッドでECSのSystemに追加した関数には、用意した任意のComponentを引数として好きなだけ入れることができます。

【コラム】会計パズル

経理の計算をパズルのように学べる遊びを考えました。会社経営が学べる戦略マネジメントゲームみたいに遊べたらいいです。会計事務所の会計処理はAIが対処するようになるらしいですが、各クライアントの会社は今まで通り経理のおばちゃんレベルではないでしょうか?楽しく経理を学べるようにできたらいいです。実際加減の計算ぐらいなので謎解きみたいに小学生の知識でも解けるようにしたらどうでしょうか?

この記事をシェアしてください

人気記事トップ10

人気記事ランキングをもっと見る

企画広告も役立つ情報バッチリ! Sponsored