クラスまみれのゲームプログラミング入門

14:タスク処理を実装する(4)

 いよいよタスク処理実装も大詰め。WinMain()にタスク処理ルーチンを組み込む方法と、ゲームオブジェクトをタスクに追加するやりかたについて解説します。

 WinMain()にタスク処理ルーチンを実装します。CGameObjectを初期化する関数「Initialize()」をCreateWindow()とShowWindow()の間に追加します。いったんウィンドウを表示してから初期化を行うと、一瞬ウィンドウが固まったかのような印象を与えかねませんので、あたかもバックグラウンドで起動しているかのように振る舞うのがベストです。CGameObjectを解放する関数「Uninitialize()」はウィンドウ処理ループを抜けた後の箇所に記述します。

 続いてタスク処理ですが、これはウィンドウのイベントが発生していないときに実行させるようにしましょう。これはPeekMessage()で返値がNULL、つまり処理するウィンドウイベントがなかった時となります。

 ここで少し話題は変わるのですが、デバッグにおいて便利な機能をご紹介します。アクション性の高いゲームであるほど、キャラクターが頻繁に出入りするため、メモリの作成・除去もしきりに行われます。そのため、プログラムの如何によっては消去せずほったらかしにしたままプログラムを終了させてしまう可能性もあります。そういった、メモリリークを通知する関数がVisual C++には用意されています。

 方法は至って簡単。#include <crtdbg.h>を宣言して、WinMain()の一番先頭に

dbgflag.cpp
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

の一行を追加するだけ。これでプログラム終了時に解放していないメモリがあれば

このように警告が発生します。しかしこれだけでは警告としては不親切ですので、「CGameObject.h」に以下の定義を追加しましょう(7~10行目)。DirectX SDK内で記述されているnewまでもが置換されるのを避けるため、DirectX関連のdefineを行った後で追加するように。

debugdef.h
/* 省略 */

#include <dxerr9.h>
#include <d3d9.h>
#include <d3dx9.h>

#if _DEBUG
#include <crtdbg.h>
#define new  ::new( _NORMAL_BLOCK, __FILE__, __LINE__ )
#endif

#define RELEASE(x) {if(x) x->Release();}

class CGameObject;

/* 省略 */

 引数付きのnewに置き換えてやることで、開発環境がメモリの作成位置を取得できるようになり、メモリリークが発生してもどこに問題があったかを突き止めやすくなります(赤線は執筆者によるもの)。

 では、長い間WinMain()にふれてこなかったので、winmain.cppファイルのコードをおさらいしておきましょう。

winmain.cpp
#include <crtdbg.h>

#include "GameObject.h"

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
		case WM_DESTROY:
			PostQuitMessage(NULL);
			break;
		default:
			return (DefWindowProc(hWnd, msg, wParam, lParam));
	}
	return (0);
}

int APIENTRY _tWinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 LPTSTR lpszCmdLine, int nCmdShow)
{
#ifdef _DEBUG
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif

	WNDCLASS wc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.hCursor = LoadCursor(hInstance, IDC_ARROW);
	wc.hIcon = NULL;
	wc.hInstance = hInstance;
	wc.lpfnWndProc = WndProc;	//イベント処理関数を連結
	wc.lpszClassName = _T("Game");
	wc.lpszMenuName = NULL;
	wc.style = NULL;
	
	if(RegisterClass(&wc) == NULL){
		return 0;
	}

	HWND hWnd = CreateWindow(
		wc.lpszClassName,
		_T("クラスまみれのゲームプログラミング入門"),
		WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
		CW_USEDEFAULT, CW_USEDEFAULT, 640, 480,	
		NULL, NULL, hInstance, NULL );

	CGameObject game;
	game.Initialize(hWnd, hInstance);

	ShowWindow(hWnd, nCmdShow);

	MSG msg;
	while(TRUE){
		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
			if(msg.message == WM_QUIT) break;

			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}else{
			game.DoAllTasks();
		}
	}

	game.Uninitialize();

	return 0;
}