スポンサーリンク
設計・アーキテクチャ

Unityでステートマシンを活用!ゲーム開発を効率化する方法

設計・アーキテクチャ

はじめに

Unityでキャラクター制御やAIを作っていると、最初はシンプルだったはずのコードが、気づけば if文やswitch文だらけ になっていませんか?

「ジャンプ中は攻撃できない」「ダメージ中は移動させたくない」
そんな条件を足していくうちに、フラグが増え、分岐が増え、
どこを直せばいいのか分からないコードになってしまう……。

私自身も、Unityを触り始めた頃は「とりあえずifで分岐すれば動くからOK」と思っていました。
でも、アクションやAIが少し複雑になった瞬間、その書き方が一気に限界を迎えます。

そんなときに役立つのが、ステートマシン(State Machine / FSM)という考え方です。

ステートマシンを使うと、

  • 「今キャラがどんな状態なのか」が明確になる
  • 状態ごとの処理を整理できる
  • あとから機能を追加しても壊れにくくなる

といったメリットがあり、プレイヤー制御・敵AI・ゲーム全体の状態管理まで、幅広く応用できます。

ただし、いきなり難しい設計を目指す必要はありません。
ECSやDIのような大規模アーキテクチャに進む前に、「今の書き方を一段よくする」選択肢としてFSMはとてもちょうどいい存在です。

この記事では、

  • ステートマシン(FSM)とは何か
  • Unityでよくある状態管理の失敗例
  • enumから始めるシンプルなFSM実装
  • クラス分割による実践的な設計
  • 「どこまでFSMを使うべきか」の判断基準

といった内容を、初心者〜中級者の方でもイメージしやすいように解説していきます。

「今のコード、そろそろ限界かも……」と感じているなら、
ぜひこの先を読み進めてみてください 🙂




結論

先に結論からお伝えします。

Unityでの状態管理において、ステートマシン(FSM)は「if文の代替テクニック」ではなく、「設計を安定させるための考え方」です。

  • 状態が排他的(同時に1つだけ成り立つ)な処理はFSMと相性がいい
  • いきなり難しい実装をせず、enum+switchから始めるのが安全
  • 処理が育ってきたら、Stateクラス分割に進むと破綻しにくい

逆に言うと、すべてをFSMで管理しようとするのはNGです。

状態がほとんど増えない処理や、一時的な条件分岐までFSMにしてしまうと、
コードはきれいになるどころか、むしろ分かりづらくなってしまいます。

大切なのは、

  • 「この処理は本当に“状態”なのか?」
  • 「将来、状態や分岐が増えそうか?」

を考えながら、必要なところにだけFSMを使うことです。

この記事ではこのあと、

  • そもそもステートマシンとは何なのか
  • なぜif文管理が破綻しやすいのか
  • Unityでの現実的なFSM実装ステップ

を順番に見ていきます。

「FSMが自分のプロジェクトに合うのかどうか」を判断できるようになることを、
この記事のゴールにして読み進めてもらえるとうれしいです。




ステートマシン(FSM)とは何か【基礎理解】

FSMの基本構造(状態・遷移・入力)

ステートマシン(Finite State Machine / FSM)は、
「今どんな状態か」を1つだけ持ち、その状態が条件によって切り替わるという考え方です。

ポイントは、「同時に複数の状態を持たない」こと。
キャラクターは今この瞬間、必ず1つの状態にいるという前提で設計します。

FSMは、次の3つの要素で構成されます。

  • 状態(State):今何をしているか(待機・移動・攻撃など)
  • 遷移(Transition):別の状態へ切り替わるルール
  • 入力・イベント(Input / Event):遷移のきっかけ(キー入力・衝突・時間経過など)

たとえばプレイヤーキャラなら、

  • 待機状態で移動キーが押された → 移動状態へ
  • 移動中にジャンプボタンが押された → ジャンプ状態へ
  • 着地した → 待機状態へ

というように、状態と状態のつながりとして挙動を考えます。

なぜゲーム開発と相性がいいのか

ゲームの処理は、よく考えると「状態の集合体」です。

プレイヤー、敵AI、UI、ゲーム進行など、ほとんどの要素が
「今は〇〇中」「次は△△になる」という形で動いています。

ところが、これをそのまま if 文で書き始めると、

  • 状態の組み合わせをすべて条件で表現する必要がある
  • どの条件がどの挙動に関係しているのか分かりにくい
  • あとから追加した条件が、既存の挙動を壊しやすい

といった問題が起きやすくなります。

FSMを使うと、

  • 「今はこの状態だから、この処理だけを考えればいい」
  • 状態遷移の条件がはっきりする
  • 関係ない状態のコードを触らずに済む

という形で、考える範囲を自然に絞れるのが大きなメリットです。

この「思考の整理」ができるようになると、
コード量以上に、修正や拡張のストレスが一気に減ります




Unityでよくある「状態管理の破綻例」

ステートマシンの話に入る前に、
まずは Unityで本当によく見る「つらくなる状態管理」 を整理しておきましょう。

ここに心当たりがある人ほど、FSMの効果を実感しやすいはずです。

boolフラグが増え続ける「フラグ地獄」

初心者〜中級者の方がまず陥りやすいのが、
bool変数で状態を管理し始めてしまうパターンです。


bool isMoving;
bool isJumping;
bool isAttacking;
bool isDamaged;

最初は分かりやすいのですが、処理が増えてくると、

  • ジャンプ中は攻撃できる?できない?
  • ダメージ中に移動入力が来たらどうする?
  • 複数のboolが同時にtrueになっていいの?

といった 状態の組み合わせ問題 が一気に押し寄せます。

結果として、

  • ありえない状態が同時に成立する
  • 特定の操作だけバグる
  • 修正するたびに別の場所が壊れる

という、かなり危険な状態になりがちです。

この問題をもう少し掘り下げたい方は、
こちらの記事も参考になります。

switch文が肥大化していくパターン

「boolは危ないと聞いたから」と、次に選ばれやすいのが
enum+switch文での状態管理です。

これは方向性としては正しく、実際かなり改善されます。

ただし、状態が増えてくると、こんな問題が出てきます。

  • switch文が1ファイルに集中して巨大化する
  • 状態ごとの処理が長くなり、見通しが悪くなる
  • 「この状態だけ特別な変数」が増えていく

結果として、

「状態は整理されているはずなのに、修正が怖い」

という、別の形のストレスが生まれます。

ここまで来ると、

  • 状態ごとの責務を分けたい
  • 関係ない状態のコードを触りたくない

と感じ始めるはずです。

この「違和感」こそが、
FSMを 次の段階(クラス分割)へ進める合図 になります。




FSMの実装パターン①:enum + switch(最初の一歩)

ここまでで、「bool管理はつらい」「if文が増えると壊れやすい」という感覚が なんとなく見えてきたと思います。

そこでまず試してほしいのが、enum と switch 文を使ったシンプルなFSMです。

これはいきなり難しい設計に進むのではなく、
考え方だけFSMに寄せるための、とても安全なステップになります。

enumで「状態」を一意にする

まずは状態を enum で定義します。


public enum PlayerState
{
    Idle,
    Move,
    Jump,
    Attack
}

そして、現在の状態を1つだけ持つようにします。


PlayerState currentState = PlayerState.Idle;

この時点で、
「ジャンプ中かつ待機中」のような矛盾状態は 構造的に起こらなくなります。

Updateで状態ごとの処理を分岐する

次に、Updateなどで状態ごとの処理を書きます。


void Update()
{
    switch (currentState)
    {
        case PlayerState.Idle:
            UpdateIdle();
            break;

        case PlayerState.Move:
            UpdateMove();
            break;

        case PlayerState.Jump:
            UpdateJump();
            break;

        case PlayerState.Attack:
            UpdateAttack();
            break;
    }
}

処理をメソッドに分けるだけでも、

  • 今どの状態の処理を書いているかが分かりやすい
  • 状態ごとにロジックを整理できる

といった効果があります。

enum FSMのメリットと限界

この方法のメリットは、とても分かりやすいことです。

  • FSMの考え方をコードで体感できる
  • 既存コードから移行しやすい
  • 小規模なら十分実用的

一方で、プロジェクトが育ってくると、限界も見えてきます。

  • switch文がどんどん長くなる
  • 状態専用の変数がクラスに溜まっていく
  • 状態ごとの責務が完全には分離できない

つまり、

「状態は整理できたけど、クラス自体はまだ重たい」

という状態です。

この段階まで来たら、
FSMを 「設計として完成させる」 方法に進む価値があります。

次の章では、
状態そのものをクラスとして分離するFSM(Stateパターン)を見ていきましょう。




FSMの実装パターン②:Stateパターン(クラス分割)

enum+switch のFSMで「考え方」はかなり整理されましたが、
次に出てくるのが 「1つのクラスが重たくなってきた問題」 です。

ここで登場するのが、StateパターンによるFSM(クラス分割FSM)です。

この方法では、
「状態=処理のまとまり」そのものをクラスとして分離します。

クラス分割FSMの基本構造

まず、すべての状態が共通で持つインターフェース(または基底クラス)を用意します。


public interface IPlayerState
{
    void Enter();
    void Update();
    void Exit();
}

次に、それぞれの状態をクラスとして実装します。


public class IdleState : IPlayerState
{
    public void Enter()
    {
        // 待機状態に入ったときの処理
    }

    public void Update()
    {
        // 待機中の処理
    }

    public void Exit()
    {
        // 待機状態を抜けるときの処理
    }
}

ジャンプ・攻撃・移動も同じようにクラスを分けていきます。

状態を管理する「コンテキスト」クラス

状態を切り替える役割を持つのが、いわゆる コンテキスト です。


public class PlayerStateMachine
{
    IPlayerState currentState;

    public void ChangeState(IPlayerState newState)
    {
        currentState?.Exit();
        currentState = newState;
        currentState.Enter();
    }

    public void Update()
    {
        currentState?.Update();
    }
}

ここで重要なのは、

  • 状態遷移の責務が一か所に集まる
  • 各Stateクラスは「自分の状態」だけを考えればいい

という点です。

enum FSMと比べて、

  • 関係ない状態のコードを一切触らずに済む
  • 状態ごとの処理が自然に分離される
  • ファイル単位で見通しがよくなる

といったメリットがあります。

実装イメージを一気に掴みたい人向け

「仕組みは分かったけど、
実際のプロジェクトでどう組まれているか見てみたい」

そんな方には、FSM設計の実例として、
完成度の高いテンプレートを見るのもかなり勉強になります。

FSM AI Template(Invector)

この手のアセットは、

  • いきなり導入して使う
  • 仕組みを理解せずにブラックボックス化する

よりも、
FSMを自分で一度書いたあとに「答え合わせ」として見るのがおすすめです。

「なるほど、こう分けるのか」という発見が必ずあります。




Animator・AI・GameStateへの応用例

ここまでで、FSMの基本構造と実装イメージはつかめてきたと思います。

実はステートマシンは、
プレイヤー制御だけのテクニックではありません。

Unityではすでに、
FSMの考え方がいろいろな場所で使われています。

Animatorは「ビジュアルなステートマシン」

UnityのAnimator(Mecanim)は、
FSMを視覚的に扱える仕組みそのものです。

アニメーションクリップが「状態」、
遷移条件(bool / trigger / float)が「遷移ルール」に相当します。

さらに StateMachineBehaviour を使えば、

  • 状態に入った瞬間の処理
  • 状態中の更新処理
  • 状態を抜けたときの処理

をコード側で分離できます。

これは、
先ほど紹介した Stateパターンとほぼ同じ発想です。

Animatorを使っていて「遷移が分からなくなってきた」と感じたら、
それはFSM設計を見直すサインでもあります。

敵AIへの応用(FSMとビヘイビアツリー)

敵AIは、FSMが最もよく使われる分野のひとつです。

  • 待機
  • 索敵
  • 追跡
  • 攻撃

といった行動は、
状態として非常にきれいに分けられます。

ただし、行動条件が複雑になってくると、
FSMだけでは管理がつらくなるケースもあります。

その場合によく使われるのが、
ビヘイビアツリー(Behavior Tree)です。

FSMとビヘイビアツリーの使い分けについては、
こちらの記事で詳しく解説しています。

実装を効率化したい場合は、
実績のあるツールを使うのも1つの選択肢です。

Behavior Designer(Behavior Tree)

GameState管理への応用

FSMは、
ゲーム全体の状態管理(GameState)にも非常に向いています。

  • タイトル
  • プレイ中
  • ポーズ
  • リザルト

これらはすべて、
「今ゲームがどの状態にあるか」という排他的な状態です。

GameStateをFSMで管理すると、

  • 入力の有効・無効
  • UIの表示切り替え
  • Time.timeScaleの制御

状態単位でまとめて制御できます。

GameState設計については、
以下の記事でより踏み込んだ解説をしています。




「どこまでFSMを使うべきか」の判断基準

FSMはとても便利ですが、
「使えば使うほど良くなる万能設計」ではありません。

ここを見誤ると、

  • 設計が過剰に複雑になる
  • 読むだけで疲れるコードになる
  • 修正コストが逆に上がる

という本末転倒な状態になってしまいます。

この章では、
FSMを使うべきケース/使わなくていいケースを整理します。

FSMが向いているケース

次の条件に当てはまる処理は、FSMと非常に相性がいいです。

  • 状態が排他的(同時に1つだけ成り立つ)
  • 状態の数が今後増えそう
  • 状態ごとに振る舞いが大きく変わる
  • バグが出ると修正コストが高い

典型例としては、

  • プレイヤーの行動状態
  • 敵AIの行動フェーズ
  • ゲーム全体の進行状態(GameState)

などがあります。

これらは後から仕様変更が入りやすく、
FSMで構造を固めておく価値が高い部分です。

FSMを使わなくていいケース

一方で、次のような処理はFSMにしないほうがシンプルです。

  • 状態が2〜3個から増えない
  • 一時的・瞬間的な条件分岐
  • 単発イベントの制御

たとえば、

  • ボタンを押した瞬間の処理
  • 一度きりの演出
  • フラグON/OFFで十分な制御

までFSMにしてしまうと、

「設計は立派だけど、読むのがつらいコード」

になりがちです。

並行ステートという考え方

FSMでよくある失敗が、
「全部を1つのFSMで管理しようとする」ことです。

たとえば、

  • 移動状態(待機・移動・ジャンプ)
  • 装備状態(素手・剣・弓)

これらは、
本来は独立した状態です。

無理に1つのFSMにまとめると、
状態数が爆発してしまいます。

こういう場合は、

  • 移動用FSM
  • 装備用FSM

のように、
複数のFSMを並行して動かすほうが安全です。

実装を効率化したい場合の選択肢

「判断基準は分かったけど、
毎回FSMを1から書くのは大変」

という場合は、
FSM専用の仕組みが用意されたツールを使うのも現実的です。

Finite State Machine System

ただし、ここでも大切なのは、

「仕組みを理解した上で使う」ことです。

FSMの判断基準が分かっていれば、
ツールを使う/使わないの選択も、ずっと冷静にできるようになります。




よくある誤解・失敗例

ステートマシン(FSM)は便利な設計ですが、
導入の仕方を間違えると、かえってコードを分かりにくくしてしまうことがあります。

ここでは、実際によく見かける誤解・失敗パターンを整理しておきます。

FSM=難しい設計だと思い込んでしまう

「FSMって設計パターンでしょ?難しそう……」
こう感じてしまい、最初から避けてしまう人は意外と多いです。

でも実際は、

  • enumで状態を1つにまとめる
  • 状態ごとに処理を分けて考える

この時点ですでにFSMの考え方は始まっています。

いきなりクラス分割までやる必要はありません。
「状態を意識する」だけでも、設計は確実に改善します。

すべてをFSMで管理しようとしてしまう

FSMを知った直後にやりがちなのが、
「この処理もFSMで管理しよう」と何でも当てはめてしまうことです。

結果として、

  • 状態の数が必要以上に増える
  • 状態遷移が追えなくなる
  • 単純な処理が逆に分かりにくくなる

という状況に陥ります。

FSMは「状態が排他的に切り替わる処理」に使うもの。
一時的なフラグ制御や単発イベントまでFSMにする必要はありません。

責務分離ができていないままクラス分割する

Stateパターンを導入したものの、

  • Stateクラスが巨大になっている
  • 他のStateの処理に直接触っている

というケースもよく見かけます。

これは、
「状態ごとに責務を分ける」という意識が弱いままクラス分割したことが原因です。

FSMは、状態を分けること自体が目的ではなく、
「変更の影響範囲を狭くする」ための設計です。

責務分離の考え方がまだ曖昧な場合は、
こちらの記事もあわせて読むと理解が深まります。




まとめ

この記事では、Unityにおけるステートマシン(FSM)の考え方と、
現実的な導入ステップについて解説してきました。

  • if文やbool管理は、規模が大きくなると破綻しやすい
  • FSMは「状態」を軸に処理を整理する設計手法
  • enum+switchから始めて、必要に応じてクラス分割へ進む
  • すべてをFSMにせず、使いどころを見極めることが大切

FSMは、
コードを賢く見せるためのテクニックではありません。

「あとから直しやすいか」「仕様変更に耐えられるか」
そのための土台を作る設計です。

今のコードに少しでも違和感を感じているなら、
まずは enum で状態をまとめるところから、ぜひ試してみてください。

きっと、修正や拡張が前よりずっと楽になるはずです 🙂


参考文献・参考リンク


よくある質問(FAQ)

Q
FSMとStateパターンは同じものですか?
A

考え方は近いですが、完全に同じではありません。
FSMは「状態と遷移のモデル」、Stateパターンはそれをオブジェクト指向で実装するための設計パターンです。

Q
Animatorだけ使っていればFSMは不要ですか?
A

アニメーション制御だけならAnimatorで十分な場合も多いです。
ただし、入力制御・AI・GameStateなど、アニメーション以外の状態管理にはFSMの考え方が役立ちます。

Q
ECSやDIに進む前にFSMは学ぶべきですか?
A

個人的には「はい」です。
FSMで状態と責務を整理できるようになると、ECSやDIの考え方も理解しやすくなります。

※当サイトはアフィリエイト広告を利用しています。リンクを経由して商品を購入された場合、当サイトに報酬が発生することがあります。

※本記事に記載しているAmazon商品情報(価格、在庫状況、割引、配送条件など)は、執筆時点のAmazon.co.jp上の情報に基づいています。
最新の価格・在庫・配送条件などの詳細は、Amazonの商品ページをご確認ください。

スポンサーリンク