東京工業大学
デジタル創作同好会

2017年11月14日 | メンバーブログ

Stateパターンを用いた状態の管理

Osa_Pyon

こんにちは.traPAdventCalendar2017の11月14日担当のOsa_Pyonです.

はじめに

ゲームを制作する際にプレイヤーの状態をどうやって記述,管理するかという課題は常に発生します.この記事では状態の記述,管理の方法として状態ごとにクラスを定義するStateパターンという手法を紹介していきます.

ゲームにおけるプレイヤーの状態

例としてマリオを考えてみます.マリオが持つ状態を列挙してみると

こんな感じになりますね.それぞれの状態から状態への遷移図はこの様になります.
state-1

switchで書いてみた

前述の状態遷移をswitch文を使って実装しました.言語はc#です.

public class Mario {

    //状態
    enum State{Idle,Run,InAir,HipDrop};
    State state = State.Idle;
    
    //毎フレーム実行される
    void Update() {
        switch (state) {
            case State.Idle:
                if(/*ジャンプ入力*/) {
                    state = State.InAir;
                    //ジャンプ処理
                    Jump();
                    break;
                }else if(/*移動入力*/) {
                    state = state.Run;
                    break;
                }
                break;
            
            case State.Run:
                if(/*ジャンプ入力*/) {
                    state = State.InAir;
                    //ジャンプ動作
                    Jump();
                    break;
                }else if(/*移動入力*/) {
                    //走るという処理
                    Run();
                    break;
                }else{
                    state = State.Idle;
                    break;
                }
                break;
                
            case State.InAir:
                if(/*ヒップドロップ入力*/) {
                    state = State.HipDrop;
                    break;
                }else if(/*地上にいる*/) {
                    state = State.Idle;
                    break;
                }
                break;
                
            case State.HipDrop:
                if(/*地上にいる*/) {
                    state = State.Idle;
                }else{
                    //ヒップドロップの処理
                    HipDrop();
                    break;
                }
                break;
            }
        }
    }
}

このコードでは実感し難いですが状態数が10や20とかになって状態による分岐がUpdate以外の場所でも出現した上で,振る舞いを変更したり状態を増やしたりする時,全ての分岐をチェックし書き直さなくてはなりません.いやです.

Stateパターンを用いて書き直す

Stateパターンで上のコードを書き直すと次のようになります.

interface State {
    State Execute();
}
public class StateProcessor {
    State state;
    
    public StateProcessor() {
        state = new Idle();
    }
    
    //毎フレーム呼び出される
    void Update() {
        state = state.Execute();
    }
}
public class Idle : State {
    public State Execute() {
        if(/*ジャンプ入力*/) {
            //ジャンプ処理
            Jump();
            return new InAir();
        }else if(/*移動入力*/){
            return new Run();
        }else{
            return this;
        }
    }
}
public class Run : State {
    public State Execute() {
        if(/*ジャンプ入力*/) {
            //ジャンプ動作
            Jump();
            return new InAir();
        }else if(/*移動入力*/) {
            //走るという処理
            Run();
            return this;
        }else{
            return new Idle();
        }
    }
}
public class InAir : State {
    public State Execute() {
        if(/*ヒップドロップ入力*/) {
            return new HipDrop();
        }else if(/*地上にいる*/) {
            return new Idle();
        }else{
            return this;
        }
    }
}
public class HipDrop : State {
    public State Execute() {
        if(/*地上にいる*/) {
            return new Idle();
        }else{
            //ヒップドロップの処理
            HipDrop();
            return this;
        }
    }
}

この様にすることで,例えばHipDropからの遷移を追加する時やHipDropの挙動を変更する時にHipDropクラスのみをいじれば良くなりました(書き換え前だとコード全体をチェックしなくてはいけません).うれしい.ちなみに状態遷移の仕方は色々な派閥があるらしいです.

まとめ

この手法は僕が現在制作に関わっているゲームで管理が爆発した時に出会ったデザインパターンです.今回解説した手法以外にも状況に応じた様々な先人の知恵が存在するのでそれらをゴリゴリ使っていきましょう.
明日はPolyさんの記事です.

参考

Stateパターン TECHSCORE

この記事を書いた人
Osa_Pyon

この記事をシェア

このエントリーをはてなブックマークに追加

関連する記事

2017年12月13日
私が英弱な理由を考えてみただけ
dain
2017年12月13日
チズケ破壊論
whiteonion
2017年12月11日
面白いカードゲームの話
mtikusk

活動の紹介

カテゴリ

タグ