4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

🦀ひとりRustとBevyでゲーム開発🕊️Advent Calendar 2024

Day 11

【Rustのまほう2】#11 ステート

Last updated at Posted at 2024-12-10

私がBevy Engineでかなり重要だと思っている『ステート』について解説していきます。

ステート

Bevyにおけるステート(State)とは、ざっくりいうと「ゲームがいまどの画面を開いているか」、つまり最初のタイトル画面なのか、ゲーム本編が始まっているのか、コンフィグ画面なのか、などを表すものです。

たとえば、タイトル画面でスペースキーを押したらゲーム本編に進むという仕組みだったとしましょう。それでゲーム本編に入ったとき、もう一度スペースキーを押したらまたゲーム本編の初期化の処理が実行されてしまうことになります。頑張ってフラグで切り替えてゲーム本編ではそのシステムが動作しないようにしたとしても、システム自体は動作しているのでクエリが実行されてしまい、ゲーム本編には不要な処理が何度も走ってしまう可能性があります。

ここでステートを使うと、特定の画面でのみ動作するシステムを定義できます。

ステートの定義

次は実際に私が書いたステートの定義です。

#[derive(States, Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum GameState {
    #[default]
    Setup,

    /// タイトル画面
    MainMenu,

    /// 通常のプレイ画面
    InGame,

    /// ワープ中を表すステート
    Warp,

    // 名前入力画面
    NameInput,
}

ゲームを起動すると、最初はデフォルトのSetupステートになります。Setupでは画像などの読み込みを行い、読み込みが完了するとMainMenuステートに遷移するようになっています。そしてタイトル画面で画面をクリックするとゲーム本編のInGameステートに遷移します。

なお、グローバルな状態を管理する機能という意味では、ステートと似たようなものに『リソース』という機能もあります。リソースとステートの違いは、ステートはシステムの切り替えに使うもので、リソースはそれ以外の汎用的なグローバル状態だということです。

システムの実行条件

先ほど定義したステートを使ってシステムの実行条件をしてするには、run_ifin_stateを使います。

app.add_systems(
    FixedUpdate,
    update_camera.run_if(in_state(GameState::InGame))
);

こうすると、update_cameraシステムは、ゲームがInGameステートにあるときだけ実行されるようになります。実際の開発を始めると、add_systemsのほとんどでこのrun_ifでステートを指定することになります。このようにステートは実はゲーム全体に影響しますので、なるべく開発の初期に定義しておいたほうがいいと思います。

ステートの切り替え

ステートを切り替えるには、Bevy組み込みのNextStateリソースを参照します(リソースについてはこのシリーズでまだちゃんと説明していませんが、汎用のグローバルな状態のことです)。次のようにResMutシステムパラメータを使ってリソースを取り出し、setで次のステートを指定します。

fn on_click(
    ...
    mut next_state: ResMut<NextState<GameState>>,
) {
    ...

    next_state.set(GameState::InGame);

    ...
}

これで、この次のフレームからはGameState::InGameのステートになります。たStateScopedが動作して、不要になったエンティティが自動的に削除されます。

StateScoped

ステートのもうひとつ重要な機能に、StateScopedがあります。これはコンポーネントの一種なのですが、別のステートに切り替わったときに自動的にそのエンティティ全体をワールドから削除することができます。たとえば、私のゲームではプレイヤーキャラクターに次のようにStateScopedコンポーネントを追加しています。

commands.spawn((
    StateScoped(GameState::InGame),
    ...
});

これで、タイトル画面に戻ったときにはプレイヤーキャラクターのエンティティは自動的に削除されます。手動でエンティティを削除するのはかなり面倒ですし、うっかり削除し損ねるとタイトル画面に戻ったのにプレイヤーキャラクターのスプライトが画面に残ったりします。そのため実際にはほとんどすべてのエンティティにこのStateScopedを追加することになります。この意味でもステートは最初に定義しておいたほうがいいということです。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?