Behind Coleus

大学生なのに特別大学で楽しいことが一 つもない。虐待と男子校病のせいにしてみる。→日本脱出しました 今ジャカルタにいます。→ジャカルタから帰ってきました。

住民税非課税世帯給付金申し込んだ

youtu.be

今回もbevy engineの練習していきましょうね。住民税非課税世帯の給付金の申し込みは、10分でできた。 キャッシュカードと、マイナンバーカードの写真を送る必要があった。

 

目的

プロジェクトファイルを細かく分割して、管理をしやすくする。

①srcフォルダーの中で、main.rsを、systems.rs resources.rs components.rs events.rsに分けて同じように実行できるようにする。

②その後、srcフォルダーの中で、main.rsの他に、enemy star player scoreのフォルダーに階層わけし、それぞれのエンティティに関連する、コンポーネントやシステム、イベント、リソースをファイルで①のように分けて記述する。それぞれのフォルダーでプラグインを作成し、mainにプラグインを追加して、同様に動くようにする。

 

目的①に対する取り組み

①に対して

まず、メイン関数以外のシステムを、system.rsにカットアンドペーストし、をリソースのderive宣言やコンポーネント、イベントのderive宣言をそれぞれresources.ts components.rs events.rsにカットアンドペーストした。

その後、main.rs に以下の様な文を追加した。

mod systems;
mod components;
mod resources;
mod events;
use crate::systems::*;
use crate::resources::*;
use crate::events::*;

また、

resources.rs components.rs events.rs の文頭に、

use::bevy::prelude::*;

を宣言し、

systemsには、bevyの宣言の他に、

use bevy::{prelude::*, window::PrimaryWindow, app::AppExit, };
use rand::random;

use crate::components::*;
use crate::events::GameOver;
use crate::resources::*;

components や、events、resources.rsの読み込みを行った。

その後、出たエラーを消すために、systems.rs やresources.rsに足りないpub constをカットアンドペースト張り付けた。

この工程の意味を説明すると、main関数においてmodキーワード を使って、モジュールの定義をしました。モジュールの定義をすると、useキーワードでスコープ内に持ち込むことが出来ます。use はパスの指定をする必要があるのですが、crateは同じファイル内、つまりsrc内という意味で、そのあとから、1段階潜るのが、crate::の後の文であるという事である。この場合、拡張子は必要ないようだ。

検証

プレイヤー、スター、エネミー、スコア全て問題なく機能しているため、目的は達成できている。

目的への取り組み

次に、Star, Enemy, Player, Score毎にファイルを分けプログラムを管理する方法を試してみる。

まずは、コマンドプロンプトを利用して、フォルダーを作成した。

# enemyディレクトリ内にファイルを作成
mkdir enemy
New-Item -Path "enemy\component.rs" -ItemType File -Force
New-Item -Path "enemy\systems.rs" -ItemType File -Force
New-Item -Path "enemy\events.rs" -ItemType File -Force
New-Item -Path "enemy\main.rs" -ItemType File -Force
New-Item -Path "enemy\resources.rs" -ItemType File -Force

# starディレクトリ内にファイルを作成
mkdir star
New-Item -Path "star\component.rs" -ItemType File -Force
New-Item -Path "star\systems.rs" -ItemType File -Force
New-Item -Path "star\events.rs" -ItemType File -Force
New-Item -Path "star\main.rs" -ItemType File -Force
New-Item -Path "star\resources.rs" -ItemType File -Force

# scoreディレクトリ内にファイルを作成
mkdir score
New-Item -Path "score\component.rs" -ItemType File -Force
New-Item -Path "score\systems.rs" -ItemType File -Force
New-Item -Path "score\events.rs" -ItemType File -Force
New-Item -Path "score\main.rs" -ItemType File -Force
New-Item -Path "score\resources.rs" -ItemType File -Force

# playerディレクトリ内にファイルを作成
mkdir player
New-Item -Path "player\component.rs" -ItemType File -Force
New-Item -Path "player\systems.rs" -ItemType File -Force
New-Item -Path "player\events.rs" -ItemType File -Force
New-Item -Path "player\main.rs" -ItemType File -Force
New-Item -Path "player\resources.rs" -ItemType File -Force

このコードcd bevy-ball-game/srcにしたのち打ち込んで、作成した。

最終形はこのような形である。

参考動画が、crate::player::main.rsではなく、mod.rsになっていたので、直した。

まずは、crate::components.rsからコンポーネントの宣言を、各フォルダー内の、components.rsに移した。同様に、イベント、リソースについてもフォルダの分類ごとに入れた。GameOverイベントは、Playerフォルダーのイベントに入れた。
その後、それらの、crate::components.rs, events.rs, resources.rsを削除した。

crate::systems.rsの中身は、spawn_camera handle_gameover exit_gameシステムをcrate::main.rsに移動し、enemy_hit_playerと、player_hit_starをplayer::sytemsに移動した以外は、システムの名前についているものをそれぞれシステムに移動した。

そして、メイン関数にある、systems.rsに送った関数をそれぞれ、mod.rsにコピペした。

use bevy::prelude::*;
 
pub struct EnemyPlugin;

impl Plugin for EnemyPlugin{
    fn build(&self, app: &mut App){
        app.init_resource::<EnemySpawnTimer>()
        .add_systems(Startup, spawn_enemies)
        .add_systems(Update,  (enemy_movement, update_enemy_direction, confine_enemy_movement).chain())
        .add_systems(Update, (spawn_enemies_over_time, tick_enemy_spawn_timer))
    }
}

そして、このようにして、プラグインを作り、システムをまとめた。

use bevy::prelude::*;

pub struct PlayerPlugin;

impl Plugin for PlayerPlugin {
    fn build(&self, app: &mut App) {
        app.add_event::<GameOver>()
            .add_systems(Startup, spawn_player)
            .add_systems(Update, (player_movement, confine_player_movement).chain())
            .add_systems(Update, (player_hit_star, enemy_hit_player))
    }
}
use bevy::prelude::*;

pub struct ScorePlugin;

impl Plugin for ScorePlugin {
    fn build(&self, app: &mut App) {
        app.init_resource::<Score>()
            .init_resource::<StarSpawnTimer>()
            .init_resource::<HighScores>()
            .add_systems(Update, update_score)
    }
}
use bevy::prelude::*;

pub struct StarPlugin;

impl Plugin for StarPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Startup, spawn_stars)
        .add_systems(Update, (tick_star_spawn_timer, spawn_stars_over_time))
        .add_systems(Update, (update_high_scores, high_scores_updated).chain())
    }
}

main関数に残したspawn_camera, handle_game_over, exit_gameは、crate::systems.rsに残した。

 

様々なものが移動されたので、使われてないファイルなどを削除する。
enemyファイルとstarファイルのevent.rs、playerファイルのresources.rs、scoreファイルのevent.rs, component.rsを削除した。


最終的になファイルツリーは以上の画像のようになった。

また、crate::main.rsのメイン関数に、全てのプラグインを導入した。

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(PlayerPlugin)
        .add_plugins(EnemyPlugin)
        .add_plugins(ScorePlugin)
        .add_plugins(StarPlugin)
        .add_systems(Startup, spawn_camera)
        .add_systems(Update, (handle_game_over, exit_game))
        .run();
}

これにより、原理上は全て動作するはずなので、あとは、インポートをしっかりやればいいだけである。

まず、crate::systemsの中で、crate::player::events::GameOverが参照できないというエラーが出た。

まず、useキーワードで先ほどのパスを打ち込む。

すると、eventsが見つからなかったとエラーが出る。基本的にはファイルの階層が下がると読み込めないので、player::mod.rsの中で、pub mod events;と打ち込んで、eventsを公開状態にした。

すると、読み込めるようになってエラーが解消された。

また、後の話になるが、resourcesの中にある要素が見つからなかったというエラーが消えないことがあった。その問題は、resourcesのコードがコピペは済んでいるものの、保存されていなかった。という状態であった。このような問題に注意しよう。

そのあとは、各フォルダーのインポートをやっていった。

use bevy::prelude::*;
mod component;
mod resources;
mod systems;

use resources::*;
use systems::*;
pub const ENEMY_SPAWN_TIME: f32 = 5.0;
pub const NUMBER_OF_ENEMIES: usize = 3;
pub const ENEMY_SPEED: f32 = 200.0;
pub const ENEMY_SIZE: f32 = 130.0;
pub const PLAYER_SIZE: f32 = 130.0;

これは、enemy::modの宣言である。

そして、

use bevy::prelude::*;
use bevy::window::PrimaryWindow;
use rand::random;

use super::component::Enemy;
use super::resources::*;
use super::{ENEMY_SIZE, NUMBER_OF_ENEMIES, ENEMY_SPEED};

enemy::systemsには、このような宣言をした。

基本的には、enemy, score, starは依存関係が薄いので、そこから、やりました。

その場合は、基本的にファイルをpubにする必要はありません。score starは同様にやりました。ファイル内の要素を使いたいときは、super、外部の要素を使いたいときは、crateからパスを書いていきました。

use super::component::Player;
use super::events::GameOver;
use super::{PLAYER_SIZE, PLAYER_SPEED};
use crate::enemy::ENEMY_SIZE;
use crate::enemy::component::Enemy;
use crate::score::resources::Score;
use crate::star::STAR_SIZE;
use crate::star::component::Star;

player::systemsが結構必要なものを要求してきたので、それを頑張って、要求先の要素がある場所と同じ階層のmod.rsに、pub modやpub constとして公開していけば問題なく作成することが出来ました。


 

まとめると

  1. mod.rsやmain.rsで宣言した同じフォルダー内のファイルは、use::名前で呼び出せる。
  2. mod.rsやmain.rs以外の場所でファイルをスコープに入れたいときは、super::名前で呼び出す。
  3. 別の階層の中のファイルは、crate::パスで呼び出す。また、その階層の、mod.rsで、pub modを使って公開宣言をしておく。
  4. 複数要素を呼び出すときは::*;を忘れない。
  5. pub constを呼び出すときは、ファイル名から直接指定する。
  6. 依存先のファイルを保存することを忘れない。

検証

以上の画像は、プログラムをビルドしたのちに、エスケープキーを押した時のターミナルの画像である。

これにより、スコア記録(ScorePlugin)、敵とのヒット、(つまり、PlayerPluginとEnemyPlugin)が起動していることが分かる。

以上の画像は、プログラムをビルドして、約10秒後の画像である。プレイヤーの得点は約20画面上のスターは21個つまり、41個発生している。これは、10秒後にそんざいするスターの総数というのにふさわしい。

pub const NUMBER_OF_STARS: usize = 10;
pub const STAR_SPAWN_TIME: f32 = 0.3;
 

これであることから、star spawn over time関数が動作している事がわかる。また、カメラの初期設定も問題なく行われている。つまり、StarPluginとcrate::systemsも動作している。よってこの、プログラムの分割は正常に行われたと言える。

よって検証成功。