[状態遷移]1フレーム待たずにすぐに次の状態のUpdate()をしたい(C++)

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
moba
記事: 82
登録日時: 9年前

[状態遷移]1フレーム待たずにすぐに次の状態のUpdate()をしたい(C++)

#1

投稿記事 by moba » 9年前

お世話になります_(._.)_

例にはC++を使っています。
状態を変えた時に、もう一度呼ばれるまで待たずに、
すぐに次の状態のUpdate()をしたいです。
switch文とStateパターン? で考えたいと思います。

もしも何かを読めば分かるようでしたら、リンクを貼っていただければ幸いです。
このようなことを質問せずに自分で調べられるようになりたいですが、どうするべきだったのでしょうか。

(1)switch文の場合

対応例
1. goto文で分岐の頭に戻る
2. while(1)で分岐をくくって、whileの最後にはbreakを入れる。
  すぐに次の状態を実行する場合は、continueで分岐の頭に戻る

1を使うのが妥当かと思ったのですが、どうなのでしょうか。
他の良い方法はありますか。

switch文を使った状態遷移の元型のイメージ
► スポイラーを表示
(2)Stateパターン? の場合

色々な説明があって、Stateパターンがどのようなものか分かっていません。すみません。
以下のようなものを考えています。

コード:

class State
{
public:
	virtual State* Update(); // この中で return this; すれば状態は変わらない
};
これを使う側はこのようなイメージです(例が適切でなかったらすみません)

コード:

class Inventory
{
private:
	State *mState;
public:
	Inventory() : mState(0){
		mState = new Opening();
	}
	void Update()
	{
		auto newOne = mState->Update();
		if( newOne != mState){
			delete mState;
			mState = newOne;
			// #######################################
			// ここでnewOne->Update()をしたい場合がある
			// #######################################
		}
	}
};
コードの中の、#に挟まれたを実現するには、どうすればいいでしょうか。
僕が考えたのは、State *State::Update( bool* )とすることで、
擬似的にStateの返し値を2つにして、その場で実行するならtrueを返す方法です。

よろしくお願いします。

hide

Re: [状態遷移]1フレーム待たずにすぐに次の状態のUpdate()をしたい(C++)

#2

投稿記事 by hide » 9年前

それは次のstateを生成したときのコンストラクタや初期化用のメソッド等でやるべきでは?

YuO
記事: 947
登録日時: 14年前
住所: 東京都世田谷区

Re: [状態遷移]1フレーム待たずにすぐに次の状態のUpdate()をしたい(C++)

#3

投稿記事 by YuO » 9年前

どういう作りかわからないのですが,Updateって「Modelを」更新するんですよね。
描画のUpdateというのであれば,そもそも責務がおかしい,となります。

1は実装の好みでしょうけれど,私は後者を選択しますかね。
2に関してはUpdateを無限ループにしてnewOne == mStateならbreakすればよいかと。

moba
記事: 82
登録日時: 9年前

Re: [状態遷移]1フレーム待たずにすぐに次の状態のUpdate()をしたい(C++)

#4

投稿記事 by moba » 9年前

ご回答ありがとうございました_(._.)_

なぜか猛烈に不安になったのですが、
今回の件では、どちらも動けばそれで十分だったのかなと思いました。
不安でも、もう少し寝かせてから質問するべきだったかもしれません。
見て分かりやすいのでYuOさんの方法で行こうと思います。

> YuOさん
たぶん伝わったと思いますが、InventoryはInventoryMenuのつもりでした。
Opening状態のUpdate内では、ウィンドウをアニメーションさせるための更新を含むつもりでした。

責務について、今の何がどうまずいか教えていただけませんか。
描画の更新と状態の更新で関数を分けるべきなのでしょうか。
なんだかobserverパターンが思い浮かびます。

それと、Modelという言葉が何を表わすか分かりませんでした。
Modelの更新は状態の更新だと思ってもいいですか。

YuO
記事: 947
登録日時: 14年前
住所: 東京都世田谷区

Re: [状態遷移]1フレーム待たずにすぐに次の状態のUpdate()をしたい(C++)

#5

投稿記事 by YuO » 9年前

moba さんが書きました:責務について、今の何がどうまずいか教えていただけませんか。
描画の更新と状態の更新で関数を分けるべきなのでしょうか。
なんだかobserverパターンが思い浮かびます。
オブジェクト指向でよく言われる、関心の分離、責務の分割、という話になるのですが……。

とりあえず、私の頭の中にあったのは今は亡きXNA Frameworkにおける、UpdateとDrawという2つのメソッドです。

moba さんが書きました:それと、Modelという言葉が何を表わすか分かりませんでした。
Modelの更新は状態の更新だと思ってもいいですか。
MVCのModelという言葉が前提にあるのですが、基本的にModelは「表示と入力に関わること以外全て」です。

基本的なGUIアプリケーションアーキテクチャの一つに、MVC (Model-View-Controller)というものがあります。
これは、
  • Controllerはユーザーの入力を受け取ってModelの該当するメソッドを呼び出す。
  • ViewはModelの変更を監視してModelの状態を描画する。
  • ModelはControllerから受け取った入力を元に自身の状態を変更する。
という責務分担を行います。
ViewによるModelの監視に、通常はObserverパターンを用いますが、ゲームなどの一定時間ごとに表示を更新する場合は表示更新の度に読みに行く形でもよいです。

元々の目的はPresentation-Domain Separation (PDS) (JP) です。
MVCでいえば、View&ControllerとModelの分離になります。
この分担において、ViewはModelの内部実装を知る必要はないですし、ModelはViewがどういう描画を行うかを知る必要がありません。
極端な話、アプリケーションによってはCUIとGUIである程度Modelを共有できます。
# ゲームだと、将棋などはわかりやすくModel共有が可能です。

利点はMVCやらPDSやらで検索かけてもらうとよいのですが、Modelの部分単体でゲームを再現できれば、ViewをCUIに差し替えてもよいわけです。
世の中、GUIとCUIではデバッグ難易度に明確な差があるため、CUIでデバッグできるはそれだけで利点になるかと思います。
GUIの入力 (ControllerのModel呼び出し部分) をログに取っておいて、後からCUIで再現、も可能になります。
# 真面目な回答ではModelがUnit Test可能になるとか色々あるのですが……。


さて、描画する部分は明らかにPresentationに属します。
そして、Stateの部分は明らかにDomain (Model) に属します。
この2つを同じメソッドでやってしまうと、PDSが出来ていない状態になります。
これでは、描画した結果がおかしかった場合に、いきなりUpdate関数自体の動作を調べないといけなくなります。
しかし、UpdateとDrawが分離しているのであれば、Update後の状態が正しいかどうか調べ、正しくないのであればDraw関数を一度見る必要が無くなりますし、正しいのであればUpdate関数を見る必要はなくなります。


そして最後に一番分かりやすい (PDSとは無関係な) 「分離すべき理由」を。
Update関数の内部で描画をやってしまうと、今回の場合、不要になるStateでの描画をしてしまったり、ということが起きかねません。
つまり、次のStateを返すより前に描画をしてしまうと、それは本来不要な描画になります。
UpdateとDrawを分離しておけば、Update関数の中で描画系に触らない限り、必ずDraw関数は一度しか呼ばれないので不要な描画はなくなります。

moba
記事: 82
登録日時: 9年前

Re: [状態遷移]1フレーム待たずにすぐに次の状態のUpdate()をしたい(C++)

#6

投稿記事 by moba » 9年前

> YuOさん

ご丁寧なご回答をありがとうございました。理解のために背景知識まで教えていただいてとても助かります。
MVC等の言葉を知られただけでも、今後の手掛かりになりそうです。

実は、Inventory::Update()とInventory::Draw()で関数を分けるつもりでした。Draw()を書いていなかったので、意図が伝わらなかったのではないかと思います。すみませんでした。
State::Update()が複数の更新を担当したのは カプセル化 (責任の)委譲のつもりでした。
ですが、よく考えると山ほど問題が出てきたので、ここは引っ込んでじっくり考えてみます。

ありがとうございました_(._.)_

閉鎖

“C言語何でも質問掲示板” へ戻る