enchant.jsで状態管理実装 – イベント

2013 年 4 月 2 日 by 山平

どんなに美しい3Dクラフィックスであっても、基本的にゲームはパラパラ漫画のようにヒトコマずつ処理されます。
enchant.jsにも、「フレーム」という概念があります。

フレームが始まるごとにイベントが発生するので、作り手はフレーム内の処理を書くだけでゲームっぽいものが作れます。
ゲーム開始後つまり「ゲーム中」についてはこれだけでも十分ゲームが実装できます。
しかし、規模が大きくなるにつれて「ゲームの開始前、終了後」など管理項目が増えてきます。
これらの処理をフレームイベント内だけで記述しようとすると、だんだん無理がでてきます。

今回はイベントドリブン的な実装を行なった部分について、解説してみます。

作ったゲーム

ちょっとひねった神経衰弱ゲームを作りました。

  • カードは8種、16枚
  • 制限時間以内に全てをめくる
  • お邪魔キャラクタ
    ・カードの位置を変える
    ・勝手にカードをめくる
    ・etc…

構成と流れ

ゲームの構成要素と流れを下図に示します。
長くなるので流れはステージ開始までを抜粋しています。

image_starting_process

【凡例】
[M]マネージャ: 主に画面の遷移
[C]コントローラ: 主にオブジェクトの死活管理
[O]オブジェクト: 個別の動作

図に起こして眺めて見ると、意外と「待ち」の処理が多いことがわかります。
楽しんでゲームを作っている間は「動作」に目が向きがちですが、実際は待っている間に余計なこと(誤作動)をさせないための配慮の造り込みが結構な手間になります。

協調動作の例

ここで、時間切れを起点にしたゲームオーバーの動作を見てみます。

img_timeout

すぐにゲームオーバー状態にしたいもの

  • カード1、2が合っていても正解・クリアにはしたくない
  • もう制限時間のカウントダウンはしないでよい

すぐに制御を止めたくないもの

  • キャラクタの動作は続行させたい(じきに退場する)
  • カード2をめくるアニメーションは止めたくない

問題は、ゲームオーバーになる瞬間に、「何がどれだけ存在して、今どういう状況にあるか」というパターンがいくらでも存在し得ることです。
頑張ってすべてを洗い出して待ち処理を入れたとして、もしゲームに変更があった場合にもう一度整合性をチェックしながら処理を作り直すことを考えると辛いものがあります。

この様な状況はステージの開始、終了時に頻発します。

イベントを取り入れる

協調動作のために「すべての要素がすべての他の要素を把握・制御する」必要はありません。
結果的にすべての要素が正しく制御できればよいので、イベント的な仕組みを使って先の協調動作を実現してみます。

イベント受信部

[M]ステージにイベント受信の仕組みを作ります。

  • イベント定数を受け取る
  • 定数ごとに任意のコントローラへの指示を記述

イベント発信部

[M]ステージにイベントを送信するオブジェクトは何でも良いです。
任意のオブジェクトが任意のタイミングで発信するイベントを起点にして、あとは[M]ステージがよろしく処理してくれます。

img_timeout_event

  1. [O]タイムで時間切れ
    • 時間切れ(ゲームオーバー)イベント発生
  2. [M]ステージがイベントを受信
    • [C]情報に停止指示
      • カウントダウン停止(もう時間切れは発生させない)
    • [C]カードに停止指示
      • 正解/クリアチェック停止(もうクリアできない)
    • 指示がなかった要素
      • [O]カードは動作を続行
      • [O]キャラクタは動作を続行
  3. [M]ステージがゲームオーバーの処理を行なう

イベントドリブン

GUIプログラミングでは当たり前にイベントドリブンな作りができるように言語側から機能が提供されていますので、あまり裏の実装を意識することはありません。
しかし、C言語などで低レベルな制御を行なう場合は「メッセージループ」と呼ばれる処理でイベントの受発信を行なっています。

MFCダイアログベースでのメッセージ待ちループの実装方法

	while(1)
	{
		if( ::PeekMessage( &msg, this->GetSafeHwnd(), 0, 0, PM_NOREMOVE )  )
		{
			if (msg.message == WM_QUIT ) break;
			if( RetGetMsg = ::GetMessage( &msg, this->GetSafeHwnd(), 0, 0 ) != 0 )
			{
				/* 中略 */
			}
		}
	};

今回は「世の中で普通に使われている仕組みの実装」を知っていると、意外なところで役に立つことがある(あった)といういい例になりました。

以上です。

タグ: , ,

TrackBack