状態遷移の壁
状態遷移て難しすぎだろ!
いまだに答えが見つからない。
switch~case文では醜いコードになると、どんな本にも書いてある。
確かに、状態が増えるたびにコードが増えるし、その行数はとてつもないものになる。
今はそれを防ぐために、クラス内関数ポインタを使って、switch~case文を1~2行で済ませている。
が、関数ポインタは型が一致してないと代入できないので、
派生クラスの関数を、親クラスの型の関数ポインタに代入できない問題がある。
なので、派生して新しい状態を増やすことができないのである。
たとえ近接攻撃が一種類しか無いキャラクターであっても、
近接攻撃が一番多いキャラクターのために、関数を持たなければならない。
これがすごい煩わしい。
本やネットでは、デザインパターンのステートパターンを使っている例が多い。
状態一つ一つをクラスにしたもの。
自分のは状態一つ一つを関数単位にして、関数ポインタに代入して多態性を出しているけど、
ステートパターンは関数がクラスになっただけな感じ。
仮想クラスを定義し、状態一つ一つをその仮想クラスから派生。
キャラクターは仮想クラスのポインタを持ち、派生した状態クラスのアドレスをそのポインタに代入し、仮想関数による多態性で状態遷移を行う感じ。
この場合だと、キャラクターは仮想クラスのポインタを持つだけになるので、
自分はどんな状態をもっているのかを気にしなくてもいい設計になる。
これはこれで良いと思うんだけど、
キャラクターの具体的な振る舞いは、ステートクラス側に書くことになる。
そうしたら、キャラクタークラスのカプセル化を破らない振る舞いとなってしまう。
そういう設計考えてなかったから、すごい悩ましいなー。
こっちの方がオブジェクト指向的に正しいんだと思うけど。
今日は本を買った。
「実例で学ぶゲームAIプログラミング」という本。
この中に、有限ステートマシンという項目が、
この状態遷移の悩みを解決してくれそうだったので。
ネットで試し読みが出来てそう思い、購入したんだけどね。
たしかに、この本のステートマシンクラスはすごいと思った。
だけど・・・
だけど・・・・
厳密にはAIの考え方の状態遷移だったぜ・・・。
キャラクターが歩きから移動、追跡から逃亡という状態遷移を行うためのクラス設計だった。
肝心の、移動のときはキャラクターはどう振舞うかのコードはどこに書いてるんだ!!と思ったら、
普通にキャラクタークラス内に、移動する関数が書いてあって、ステートクラス側でそれを呼び出しているだけだった・・・。
そしたら、ステートクラスで呼び出せるという事は、移動する関数がpublicって事じゃん。
なら、普通に今のキャラクタークラスの設計と同じじゃねぇか。
んー、でもこういう設計が大事かな。
処理をしてもらうクラスに振る舞いをやってもらう。
そのときのステートクラスは、一時変数とか使って具体的に動かすんじゃなくて、
処理を行うキャラクタークラスの、メンバ関数を呼び出して、振る舞いを行う感じ。
ステートクラスはただ、キャラクタークラスのメンバ関数を適切な順番で呼び出すだけ。
でもそれだと、今の自分の設計とさほど変わらないんだよなー。
関数ポインタで呼び出すんじゃなくて、ステートクラスが呼び出すだけに過ぎない。
自分は、移動や攻撃などの具体的な振る舞いの関数は、privateで閉じて欲しい。
キャラクタークラスのインスタンスを持つ外のクラスからでも移動関数とかが呼び出せてしまうのが何かいただけない。
ゲームシーンの、大元の状態遷移じゃなくて、
キャラクター内の細かい状態遷移は閉じていて欲しいんだよなー。
キャラクタークラスのインスタンスを持つほうは、
ただキャラクタークラスのUpdate関数を呼び出すだけ。
そういう設計が良い。
ただ、Updateを呼び出すだけで状態遷移ができているのは、今の設計でも出来ている。
だけどもUpdate以外にも、普通に移動関数とかが呼び出せるので、まだ甘い。
Update関数しか呼び出せないという設計が望ましいんだけど。
・・・
しかし、状態遷移の関数は閉じていて、
どう状態が変わるのかはキャラクタークラス内で決まるという設計がいいんだけど、
死亡状態とかダメージ状態とかはクラスの外から変えられるような設計がいいんだよなー。
そういう状態にする専用のメンバ関数を公開するのもありかもしれない。
SetDeath関数とか。
それよりも、SetMode関数を公開して、外から自由に状態を変えられる設計の方が汎用性がある。
それがいいんだけど、そうなると、派生先で新しい状態が増えている場合に対応が難しい気がする。
んーややこしいなー。
結局、自分は何がしたいのか・・・?
今は攻撃の状態の追加で詰んでいる状況。
でも移動に関しては、キャラクターで状態が増えたりせずに統一している仕様だから、今のままでいいかもしれない。
攻撃の状態遷移だけは、ステートパターンを適用させるべきか・・・?
いや、攻撃に使うタイマー用の変数や処理ステップ用の変数が移動のものと使い分けるのがややこしいという問題の解決にはならない。
その問題はその問題で考えるべきか。
やばいな、弾の挙動に始まり、状態遷移やクラス設計で悩んでいる・・・。
いろいろな問題点が一気に浮き彫りになったことで問題点の多さで混乱している上に、
解決したい問題自体がクソややこしくて、その問題単体でも混乱するという最悪の状況だぞ。
とりあえず、寝よ。
« 成人式いってきました | トップページ | 状態の知らせ »
コメント