ページ 11

ステートデザインパターン(FSM)の各ステートの管理

Posted: 2014年12月17日(水) 18:21
by こぐこ
「実例で学ぶゲームAIプログラミング」という本を参考にしながら
敵の状態遷移をステートデザインパターンで管理しています。
また、各ステート(行動)はSingleton パターンで管理しています。

そこで思ったのですが、
Singleton パターンという事で1つしか存在しないプレイヤーならまだしも
何十、何百と複数存在するであろう敵オブジェクトがアクセスする各ステートには
各敵オブジェクト固有に使いたい変数はもちろん持てません。
そこで敵オブジェクトのローカルに変数を持たす場合、
そこに毎度アクセスするためのコストが必要になってくると考えました。

また1番の問題は、Singleton パターンですのでステートが増えれば増えるほど、
それが1度でも使われると使用可能メモリが消費されてしまいます。
ゲーム序盤だけに出てくる敵といった場合、ステートはそこ以外では必要がないので
後半になってくるとそのような別種の敵が積み重なってメモリ不足なんて事も有り得るのではないかと...

ならSingleton パターンで管理せずステートを毎度new/deleteした方が良いのかと思えてきました。
しかし、newはコストが高いとよくお聞きするので悩んでしまいます。


何が正解かはゲーム次第なのでしょうが...
これよりこうしたら良いという案が御座いましたら、
また、上記で誤った見解が御座いましたら、
是非教えて頂きたく、今回投稿させていただきました。
どうぞよろしくお願いします。

Re: ステートデザインパターン(FSM)の各ステートの管理

Posted: 2014年12月17日(水) 18:41
by h2so5
そもそもStateパターンとSingletonパターンをなぜ組み合わせているのでしょうか。
Singleton パターンの使いどころがわからないのですが。

Re: ステートデザインパターン(FSM)の各ステートの管理

Posted: 2014年12月17日(水) 19:09
by こぐこ
h2so5さん解答有難うございます。

本を読みながら、なるほどと理解して私なりに実装したものですが
(まだプレイヤーのみの実装なので、そこからの待機ステートのみ引用させて頂きます)
ステート

コード:

//****************************************
//	待機
//****************************************
class PlayerWait : public State< cPlayer >
{
private:
	PlayerWait(){}
	PlayerWait( const PlayerWait& );
	PlayerWait& operator=( const PlayerWait& );
public:
	~PlayerWait(){}
	static PlayerWait* GetInstance()
	{
		static PlayerWait instance;
		return &instance;
	}
	void Enter( cPlayer* owner );
	void Execute( cPlayer* owner );
	void Exit( cPlayer* owner );
};
#define p_Stop ( PlayerWait::GetInstance() )
基底ステートクラス

コード:

template< class T >
class State
{
public:
	virtual ~State(){}
	virtual void Enter(	T* owner ){ owner; }
	virtual void Execute( T* owner ){ owner; }
	virtual void Exit( T* owner ){ owner; }
};
プレイーヤークラス

コード:

class cPlayer 
{
private:
	State< cPlayer >*	state;
public:
	//	ステート切り替え
	void ChangeState( State< cPlayer >* newState )
	{
		if( state ){ state->Exit( this ); }
		state = newState;
		if( state ){ state->Enter( this ); }
	}
}
切り替え例

コード:

void PlayerRun::Execute( cPlayer* owner )
{
	//	待機へ
	owner->ChangeState( p_Stop );
}
Singletonパターンを利用するの理由は私の解釈ですと、
複数の同種敵オブジェクトが頻繁にステートを切り替えるので
new/deleteよりSingletonで既にあるものを使用したほうが
コストの面で優れているからだと考えています。
どうでしょうか...

Re: ステートデザインパターン(FSM)の各ステートの管理

Posted: 2014年12月17日(水) 19:59
by h2so5
Singletonパターンを使うべき場面ではありません。
アロケーションのコストが問題になるとしてもメモリプールを使うべきです。

それに、実測していないのにコストやメモリ使用量を気にするのは無意味です。
ゲームの進行に支障をきたすほどのコストでしょうか?

Re: ステートデザインパターン(FSM)の各ステートの管理

Posted: 2014年12月17日(水) 20:23
by こぐこ
h2so5さん解答有難うございます。
h2so5 さんが書きました:Singletonパターンを使うべき場面ではありません。
アロケーションのコストが問題になるとしてもメモリプールを使うべきです。
使うべき場面では無い理由とは、先ほど私が懸念したことでしょうか、
それともSingletonパターンそのものを多用すべきでなないので、ということでしょうか。
また代替案としては先ほど申し上げた、ステートクラスをnew/deleteの方法でよろしいでしょうか。
h2so5 さんが書きました:それに、実測していないのにコストやメモリ使用量を気にするのは無意味です。
ゲームの進行に支障をきたすほどのコストでしょうか?
支障をきたす、きたさないというよりかは
どちらの方が効率的か、設計として合理的かというのを知りたかったのです。
それこそ実測してからというお話という事でしょうか?

Re: ステートデザインパターン(FSM)の各ステートの管理

Posted: 2014年12月17日(水) 21:07
by nullptr
速度効率と設計の問題は切り分けて考えて下さい。設計を学ぶのであれば、速度など気にすべきではありません。設計を理解した上で、速度を改善する、という手順を踏むのが望ましいと思います。

Singletonが速度向上の為のデザインパターンでないことも頭に入れて下さい。デザインパターンは多く存在するものですが、設計に関わるデザインパターンは速度とは関係ありません。

Re: ステートデザインパターン(FSM)の各ステートの管理

Posted: 2014年12月17日(水) 21:25
by h2so5
こぐこ さんが書きました: 使うべき場面では無い理由とは、先ほど私が懸念したことでしょうか、
それともSingletonパターンそのものを多用すべきでなないので、ということでしょうか。
両方です。
こぐこ さんが書きました: また代替案としては先ほど申し上げた、ステートクラスをnew/deleteの方法でよろしいでしょうか。
それでも良いですし、オブジェクトごとにステートを持つという方法もあります。
こぐこ さんが書きました: 支障をきたす、きたさないというよりかは
どちらの方が効率的か、設計として合理的かというのを知りたかったのです。
それこそ実測してからというお話という事でしょうか?
少なくともこの場面でSingletonを使うのは設計上の合理性はありませんし、結局のところオブジェクトごとの記憶領域が必要というならば効率的でもありません。

Re: ステートデザインパターン(FSM)の各ステートの管理

Posted: 2014年12月22日(月) 20:30
by こぐこ
お返事が遅れてしまい申し訳ありません。
h2so5さん、nullptrさん解答有難うございます。

無い知識で理解せず設計に固執しすぎたのですかね...
動くかどうかより他人に評価されるためのプログラミングをしているので
より綺麗に、より効率化をできる限りで意識してたのですが、2つはトレードオフで成り立ってる場合もあるのですね。
Singletonは多用すべきではないと思いつつ簡潔に書けるし...と思ってましたが、やはり多用すべきではありませんね。
引数にnewして渡すことにしました。

皆様ご教示ありがとうございました。