三大処理について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Stat

三大処理について

#1

投稿記事 by Stat » 12年前

以下のようなループは基本的には一カ所だけに記述すべき、というのは分かるのですが、
むしろ複数箇所に記述すべき状況とはどんなものがあるのでしょうか?

コード:

while ( ProcessMessage() == 0 ) {
  ClearDrawScreen() ;
  何か処理 ;
  ScreenFlip() ;
}
自分は以下のように、文章表示時の入力待ちに上記のループを記述してしまっているのですが、
設計として良くないのでしょうか?

コード:

void waitMessage() {
  while ( true ) {
    ClearScreen() ;
    if ( ボタン入力 ) {
      break ;
    }
    draw() ;
    ScreenFlip() ;
  }
}

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 三大処理について

#2

投稿記事 by softya(ソフト屋) » 12年前

設計として良くないですね。
このwaitMessage中はウィンドウのXで終了できないと思いますが、どうでしょうか?

> むしろ複数箇所に記述すべき状況とはどんなものがあるのでしょうか?
無いです。ちゃんと動かすのが面倒ですし、バグの温床です。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

box
記事: 2002
登録日時: 15年前

Re: 三大処理について

#3

投稿記事 by box » 12年前

で、三大処理についての(他の)ご質問は何ですか?
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

Stat

Re: 三大処理について

#4

投稿記事 by Stat » 12年前

説明不足も甚だしいのですが、以下のような処理で文章の表示待ちをしています。
以前にRPGを一作完成させ、最終的にバグも特に無くなったのですが
やはり三大処理が複数存在する設計には問題があるのではないかと思ったので質問させていただきました。
かなり端折っているので意味不明なところがあったらすみません。

コード:

//--------------------------------------------------------------------------------------------------------------------------------------------------------------
// ● メインループ ( ゲームの根幹 )
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
void SceneBase::mainLoop() {
	while ( ProcessMessage() == 0 ) {
		ClearDrawScreen() ;
		Input::update() ;
		SceneParent* next = mScene->update() ; // mSceneはSceneParent型(各シーンクラスの基底クラス)のメンバ
		if ( next != mScene ) {
			delete mScene ;
			mScene = next ;
		}
		ScreenFlip() ;
	}
}
// ↓ 以下、SceneParentの派生クラス
//----------------------------------------------------------------------
// ● update() のオーバーライド
//----------------------------------------------------------------------
SceneParent* SceneSkill::update() {
       SceneParent* next = this ;
       if ( mWindowActorSelect.mActive ) {
                updateActorSelect() ;
       }else if ( mWindowSkillSelect.mActive ) {
                updateSkillSelect() ;
       }else if ( mNextScene == "SceneMenu" ) {
                next = new SceneMenu() ;
       }
       draw() ; // キャラクターや上記ウィンドウなどの描画
       return next ; // SceneBase::mainLoop()に返す
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
// ● アクター選択
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
void SceneSkill::updateActorSelect() {
	if ( キャンセルボタン入力 ) {
               mNextScene = "SceneMenu" ;
	}else if ( 決定ボタン入力 ) {
               mWindowActorSelect.mActive = false ;
               mWindowSkillSelect.mActive = true ;
	}
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
// ● スキル選択
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
void SceneSkill::updateSkillSelect() {
	if ( キャンセルボタン入力 ) {
               mWindowSkillSelect.mActive = false ;
               mWindowActorSelect.mActive = true ;
	}else if ( 決定ボタン入力 ) {
               mWindowMessage.mTexts.push_back( "回復呪文を唱えた。" ) ;
               waitMessage() ;
               GamePartry::mMembers[ 0 ]->mHp += 30 ;
               mWindowMessage.mTexts.push_back( "傷が回復した。" ) ;
               waitMessage() ;
	}
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
// ● メッセージ待機
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
void waitMessage() {
        while ( ProcessMessage() == 0 ) {
        ClearDrawScreen() ;
        Input::update() ;
        draw() ;
        if ( 文章が最後まで表示されている場合 ) {
              if ( Input::trigger( Input::ANY ) ) {
                      break ;
              }
        }
        ScreenFlip() ;
	}
}
メッセージ表示の箇所で流れを止めた方が処理の流れを追いやすいと判断したため waitMessage()を導入していました。
このupdate()およびwaitMessage()の設計はRPGツクールVXのRGSSの設計を参考にしています。
もちろん、RGSSの内部処理が不明なため上記のコードとRGSSのコードが等価かどうかは分かりませんが・・・

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 三大処理について

#5

投稿記事 by softya(ソフト屋) » 12年前

この処理だと、終了ボタンで終了できないのも問題として、draw() ;がいろんな階層から呼ばれるというプログラムの親子関係が崩壊しかねない事をしています。
バグの原因に成るかと聞かれれば、やっかいなバグの原因になりうるとしか言えないです。
それと複数のコントロールを同時に行うことが困難です。
例えば、シナリオでアニメーションのコントロールとメッセージのコントロールが同時並行で行いたい場合どうすればよいでしょう?
この形で無理矢理やって困難を感じませんか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

Stat

Re: 三大処理について

#6

投稿記事 by Stat » 12年前

終了ボタンとは×ボタンのことですよね?上記のコードでは終了しないのですか…。実際作ったソフトでは普通に終了するので
何かを端折ったせいかも知れません。
メッセージ処理と同時にイベント移動etc.をさせるといった状況はゲームの仕様上必要なかったため考慮していませんでしたが
確かに仰る通りだと思います。
update() をはじめとして根本的なフレーム管理の設計が間違っているのでしょうか?
定石のようなものがあれば是非教えていただきたいです。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 三大処理について

#7

投稿記事 by softya(ソフト屋) » 12年前

>終了ボタンとは×ボタンのことですよね?上記のコードでは終了しないのですか…。実際作ったソフトでは普通に終了するので何かを端折ったせいかも知れません。

たぶん雪崩式に終了はすると思いますが、途中で手違いで描画してしまうとエラー終了する可能性があります。
なんとか無事終わっているという程度の認識が正しいかと思います。
現状のコードで、ちゃんと終われる確認するのが大変だと思いますよ。

> update() をはじめとして根本的なフレーム管理の設計が間違っているのでしょうか?

waitMessage() ;と言うのはメッセージ処理における1つの状態です。つまり本来はメッセージ処理だけに閉じているべきものです。
現在のコードだとメッセージ処理がwaitMessage() ;で全体を制御する逆転を起こしてますので、オブジェクト指向のカプセル化が破綻していることに成るわけです。

処理ですが、全てメインループを起点としてupdateやdrawの処理は呼び出すようにして、メッセージ処理のupdate側でwaitステートならキーを待つように状態を遷移させて管理します。
これだと、メッセージ処理のupdate処理内に閉じて状態を管理できるので、他の処理に影響を与えません。
つまり、アニメーション処理なども独立して行えるということです。

処理的には単純なシーケンシャルじゃないのでややこしく見えると思いますが、元来ゲームは同時に複数の状態が遷移するマルチタスクなので、単純なシーケンシャル処理ではないこちらのほうが実は融通がききます。致命的なタイミング・バグになる可能性もこちらのほうが少ないです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

Stat

Re: 三大処理について

#8

投稿記事 by Stat » 12年前

御指南ありがとうございます。

となると、SceneSkill::update()は例えば以下のようにすべきなのでしょうか?
この場合、上記のHPの回復などの処理は
①の位置に記述することになるのでしょうか。

コード:

//----------------------------------------------------------------------
// ● update() のオーバーライド
//----------------------------------------------------------------------
SceneParent* SceneSkill::update() {
       SceneParent* next = this ;
       mWindowMessage().update() ;
       if ( メッセージ表示中 ) {
             ① ;
       }else if ( mWindowActorSelect.mActive ) {
                updateActorSelect() ;
       }else if ( mWindowSkillSelect.mActive ) {
                updateSkillSelect() ;
       }else if ( mNextScene == "SceneMenu" ) {
                next = new SceneMenu() ;
       }
       draw() ; // キャラクターや上記ウィンドウなどの描画
       return next ; // SceneBase::mainLoop()に返す
}

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 三大処理について

#9

投稿記事 by softya(ソフト屋) » 12年前

そもそもupdate()でdraw()するのが間違いです。
更新処理と描画は分離されるべきです。
理由は、制御が複雑になりタイミングが掴みづらくなるからです。

【補足】
更新と描画に関して補足しておきます。
更新処理で要求される処理の順番と、描画処理で要求される重ね合わせの順番が違うということ良くあります。
分離していない場合、移動してから描画しなきゃとか、描画したけどダメージで死亡フラグ立ったから消さなきゃとか複雑になりがちな処理を考えなくてよくなります。
分離されていれば、描画するときに指示された重ねあわせ順番で、死亡じゃなきゃ描画すればよいのです。

それと必要のない局所的な遷移が全体に影響を及ぼす様なコードを書かないで下さい。
「メッセージ表示中」と言う狭い範囲の状態遷移は狭い範囲の単位に押し込めます。
つまり、メッセージやメニューのクラスが有るのなら、そのクラスのupdate内で全部を処理しましょう。
※ そのクラスの中でメッセージ処理を別のクラスに委譲するのなら問題は無いかと思います。

「新・C言語 ~ゲームプログラミングの館~ [DXライブラリ]」
http://dixq.net/g/
ここの「ゲームプログラミング設計」と「メニュー画面の作り方」を参考にして下さい。

>となると、SceneSkill::update()は例えば以下のようにすべきなのでしょうか?
>この場合、上記のHPの回復などの処理は


ちょっと勝手にクラス化してしまいますが、こんな感じでしょうか。

コード:

  	switch( Window.state() ) {
	case WINDOW_ACTOR_SELECT:
		mWindowActorSelect.update();
		break;
		
	case WINDOW_SKILL_SELECT:
		mWindowSkillSelect.update();
		break;
		
	case WINDOW_SKILL_ACTION:
		mWindowSkillAction.update();//メッセージの制御はこの中
		break;
		
	default:
		if ( mNextScene == "SceneMenu" ) {
				next = new SceneMenu() ;
	   }
	}
C++的に書きたい場合は、タスク化やステートパターンなどなどをご利用下さい。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

Stat

Re: 三大処理について

#10

投稿記事 by Stat » 12年前

>> softyaさん

返信ありがとうございます!
ご提示くださったコード、サイトを参考にして
とにかく三大処理は例外なく複数あってはいけない、という大原則に則って
一からクラス設計を考え直してみます。

閉鎖

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