はじめに
はじめまして、OX ENGINEER STUDIO所属クライアントエンジニアの新山と申します。
ゲーム開発において、キャラクターや敵の行動制御は非常に重要な要素です。
本記事では、Unityを使用してステートマシン実装を行い、2回に分けて解説していきます。
これからステートマシンを導入したい方や、既存の設計を見直したい方にとって参考になれば幸いです。
StateMachineとは
ステートマシンは、システムの状態とその遷移を明確に定義することで、複雑な挙動を論理的に管理する設計手法。
ステートの例としては「索敵」「追跡」「攻撃」など。
どんな時に使う?
Update文がこんな感じになってしまうこと、ないですか?
void Update()
{
// 待機状態
if(is_idle){
if(Find(player)){
// 見つけたら追跡フラグを立てる
isChase=true;
isIdel=false;
}
}
// 追跡状態
else if(is_chase){
// 見えているなら追いかける
if(Find(player)){
Chase(player);
}
// 見失いフラグを立てる
else{
isChase=false;
isLost=true;
timer=0.0f;
}
}
// 見失い状態
else if(is_lost){
// 周囲を歩いて探してみる
SearchNear();
// 見えているなら追跡に戻す
if(Find(player)){
isChase=true;
isLost=false;
}
else{
timer+=time.deltatime;
// 時間が経ったら諦める
if(lostTimerLimit<=timer){
isIdle=true;
isLost=false;
}
}
}
}
ありがちですよね。動くには動くのですが、いくつかの問題があります。
例えば…
〇isIdleやisLostのようなフラグが多く、ステートの数だけ増えていく
・ステートの遷移時に関係するフラグの制御をしなければならず、これはミスを誘発しやすい。
〇単純にアップデートが長い
・これに加えて他のしょるも入ると見づらくなってしまう。
これを、ステートを使って解決していきましょう。
EnumとSwitchと関数化
といってもまず何をするのか。大規模な工事の前にまずは小規模な変更からやっていきましょう。
1. Enumの定義
Enum
public enum EnemyStateID
{
Idle = 0,
Chase,
Lost,
}
EnemyStateID currentState;
上記のようにステートをEnumとして定義します。
そして、そのEnumを現在のステートとして一つ、メンバで持つようにします。
2. 各状態の関数化
その後、各ステート内での挙動をまとめた物を関数化します。
例えば、以下のように関数化します。
関数化
void IdleState(){
// もしプレイヤーを見つけたら
if(Search(player)){
// ステートを追跡に設定
currentState = EnemyStateID.Chase;
}
}
3.Updateで分岐させて呼び出し
Update文
//今のステートによって関数の呼び分けをする
switch (currentState)
{
// 待機
case EnemyStateID.Idle:
IdleState();
break;
// 追跡
case EnemyStateID.Chase:
ChaseState();
break;
//見失い
case EnemyStateID.Lost:
LostState();
break;
}
まとめ
このように関数化してSwitchで分けるだけでも、いくつかのフラグを削除し、
ステート毎のデバッグや変更が簡単になったのではないでしょうか?
個人的にはシンプルなAIや、多くの状態を持たないUIならこれだけでも良いかな、と思います。
とはいえ、もっと複雑で多くの状態を持つクラスを組むこともあるでしょう。
ですので次回のブログではステートのクラス化とステートマシンの実装についてご紹介していきます。