ゲームの状態管理テクニック?


24

まず、シーン管理について言及していません。ゲームの状態は、ユーザー入力を有効にするかどうか、特定のアクターを一時的に無効にする必要があるかどうかなど、ゲーム内のあらゆる種類の状態として大まかに定義しています。

具体的な例として、古典的なバトルチェスのゲームだとしましょう。他のプレイヤーの駒を取りに行くと、短いバトルシーケンスが再生されます。このシーケンスの間、プレーヤーはピースを移動することを許可されません。それでは、このような状態遷移をどのように追跡しますか?有限状態マシンですか?単純なブールチェック?後者は、この種の状態の変化が非常に少ないゲームでのみうまく機能するようです。

有限状態マシンを使用してこれを処理する多くの簡単な方法を考えることができますが、すぐに手に負えなくなることもわかります。ゲームの状態/遷移を追跡するよりエレガントな方法があるかどうかだけに興味があります。


gamedev.stackexchange.com/questions/1783/game-state-stackおよびgamedev.stackexchange.com/questions/2423/…をチェックアウトしましたか?それはすべて同じ概念を飛び回るようなものですが、ゲームの状態を表すステートマシンよりも優れたものは考えられません。
michael.bartnett

回答:


18

私はかつてあなたの問題を非常にエレガントに解決する記事に出会いました。これは基本的なFSM実装であり、メインループで呼び出されます。この回答の残りの部分では、記事の基本的な概要を説明しました。

基本的なゲームの状態は次のようになります。

class CGameState
{
    public:
        // Setup and destroy the state
        void Init();
        void Cleanup();

        // Used when temporarily transitioning to another state
        void Pause();
        void Resume();

        // The three important actions within a game loop
        void HandleEvents();
        void Update();
        void Draw();
};

各ゲームの状態は、このインターフェイスの実装によって表されます。バトルチェスの例では、これは次の状態を意味します。

  • イントロアニメーション
  • メインメニュー
  • チェスボードセットアップアニメーション
  • プレイヤー移動入力
  • プレイヤー移動アニメーション
  • 相手の動きのアニメーション
  • 一時停止メニュー
  • ゲーム終了画面

状態は、状態エンジンで管理されます。

class CGameEngine
{
    public:
        // Creating and destroying the state machine
        void Init();
        void Cleanup();

        // Transit between states
        void ChangeState(CGameState* state);
        void PushState(CGameState* state);
        void PopState();

        // The three important actions within a game loop
        // (these will be handled by the top state in the stack)
        void HandleEvents();
        void Update();
        void Draw();

        // ...
};

各状態には、ある時点でCGameEngineへのポインタが必要なので、状態自体が新しい状態に入るかどうかを決定できることに注意してください。この記事では、CGameEngineをHandleEvents、Update、Drawのパラメーターとして渡すことを提案しています。

最終的に、メインループは状態エンジンのみを処理します。

int main ( int argc, char *argv[] )
{
    CGameEngine game;

    // initialize the engine
    game.Init( "Engine Test v1.0" );

    // load the intro
    game.ChangeState( CIntroState::Instance() );

    // main loop
    while ( game.Running() )
    {
        game.HandleEvents();
        game.Update();
        game.Draw();
    }

    // cleanup the engine
    game.Cleanup();
    return 0;
}

17
クラスのC?えー しかし、それは良い記事です-+1。
共産主義のダック

私が集めることができるものから、これは質問が明示的に尋ねるのではなく、一種のことです。確かにできるように、この方法で処理できなかったというわけではありませんが、入力を一時的に無効にしたいだけなら、CGameStateの新しいサブクラスを派生させるのは、やりすぎでメンテナンスが悪いと思います別のサブクラスと99%同一であること。
キロタン

これは、コードの結合方法に大きく依存すると思います。ピースと目的地の選択(主にUIインジケーターと入力処理)の明確な分離と、その目的地へのチェスの駒のアニメーション(他の駒が邪魔にならない、動きと相互作用するボード全体のアニメーション)を想像できますピースなど)、状態を同一から遠ざけます。これにより、責任が分離され、メンテナンスが容易になり、再利用性も可能になります(紹介デモ、再生モード)。これは、FSMを使用するのが面倒である必要がないことを示す質問にも答えていると思います。
ゴースト

これは本当に素晴らしい、ありがとう。あなたがした重要な点は、最新のコメントにありました:「FSMを使用することは面倒である必要はありません。」FSMを使用すると、switchステートメントを使用する必要があると誤って想像していましたが、必ずしもそうではありません。もう1つの重要な確認事項は、各状態にゲームエンジンへの参照が必要であることです。そうでなければ、これがどのように機能するのか疑問に思いました。
バルゴニアン

2

この種のことを可能な限り簡単な方法で処理することから始めます。

bool isPieceMoving;

次に、関連する場所にそのブールフラグに対するチェックを追加します。

後でこれよりも特別なケースが必要になった場合、それだけで、より良いものにリファクタリングします。通常、3つのアプローチがあります。

  • サブステートを表す排他的なフラグを列挙型にリファクタリングします。例えば。enum { PRE_MOVE, MOVE, POST_MOVE }必要に応じてトランジションを追加します。その後、ブールフラグをチェックするために使用したこの列挙型をチェックできます。これは簡単な変更ですが、チェックする必要のあるものの数を減らしたり、switchステートメントを使用して動作を効果的に管理できるようにするなどです。
  • 必要に応じて個々のサブシステムをオフにします。バトルシーケンス中の唯一の違いがピースを移動できないことである場合pieceSelectionManager->disable()は、シーケンスの開始時に呼び出すか、類似することができますpieceSelectionManager->enable()。本質的にはまだフラグがありますが、現在はフラグが制御するオブジェクトの近くに格納されるため、ゲームコードで余分な状態を維持する必要はありません。
  • 前の部分は、PieceSelectionManagerの存在を意味します。より一般的には、ゲームの状態と動作の一部を、一貫した方法で全体の状態のサブセットを処理する小さなオブジェクトに分解できます。これらの各オブジェクトには、その動作を決定する独自の状態がいくつかありますが、他のオブジェクトから分離されているため管理が容易です。ゲームステートオブジェクトまたはメインループを疑似グローバルのダンプグラウンドにしたいという衝動に抵抗し、その要因を取り除きます!

一般的に言えば、特別な場合のサブステートに関してはこれ以上進む必要はないので、「すぐに手に負えなくなる」というリスクはないと思います。


1
はい、州を全面的に検討することと、必要に応じてbool / enumsを使用することの間に線があると思います。しかし、私の傾向を知っていれば、ほとんどすべての州を独自のクラスにすることになりそうです。
バルゴニアン

クラスは他のクラスよりも正しいように聞こえますが、それは主観的であることを忘れないでください。他の言語構成要素でより簡単に表現できるものに対して、あまりにも多くの小さなクラスを作成し始めると、コードの意図がわかりにくくなる可能性があります。
カイロタン

1

http://www.ai-junkie.com/architecture/state_driven/tut_state1.htmlは、ゲームの状態管理に関するすばらしいチュートリアルです!ゲームエンティティまたは上記のようなメニューシステムのいずれかに使用できます。

彼はState Design Patternについて教え始めてから、aを実装しState Machine続け、さらにそれをさらに拡張します。とても良い読み物です!コンセプト全体がどのように機能し、それを新しいタイプの問題にどのように適用するかをしっかりと理解できます!


1

両方ともスケーラブルではないため、この目的のためにステートマシンとブール値を使用しないようにします。状態の数が増えると、両方が混乱に変わります。

私は通常、ゲームプレイを一連のアクションと結果として設計します。ゲームの状態は、個別に定義する必要なく自然に発生します。

たとえば、プレーヤー入力を無効にする場合:ユーザー入力ハンドラーと、ゲーム内で入力が無効であることを視覚的に示すものがあるため、それらを1つのオブジェクトまたはコンポーネントにする必要があります。したがって、入力を無効にするには、オブジェクト全体を無効にします。ステートマシンでそれらを同期するか、ブールインジケータに反応します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.