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

9:共通クラスを作る

 テクスチャ、プレイヤー、敵、背景、BGM、etc...。とにかくゲームには様々な要素が詰まっていますが、これらをまとめて一元管理できるクラスを作ると、以下のようなメリットを得ることができます。


[DirectXデバイスを共有できる]
 基本クラスの中にstatic宣言で、ウィンドウハンドラ(HWND)やDirectX関連のオブジェクトを格納しておけば、externを使うことなく、これらの値を共有することができ、プログラム的にもスッキリします。

[オブジェクト管理が簡単になる]
 すべての派生クラスは基本クラスのポインタ型に代入が可能です。つまり、派生クラスをすべて基本クラスとしても処理できるので、基本クラスリストにすべて詰め込んでおけば、プログラムの管理が簡単になります。

[ゲーム処理に柔軟に対応できる]
 仮想関数の概念を使えば、リスト内容をループで回してやるだけで、登録したすべてのオブジェクトを実行させることができ、ひとつひとつのオブジェクトに対して固有の命令を与える必要がありません。仮想関数については後述。


(ゲームクラスのイメージ)

 では早速基本クラスを作成してみましょう。基本クラス名はCGameObjectとしています。とりあえず、ゲームに必要なデータを作成するために関数と、そのデータを解放するための関数を用意することにしましょう。

GameObject1.h
#pragma once

class CGameObject
{
public:
	void Initialize();		// 初期化時に呼び出す
	void Uninitialize();	// 終了時に呼び出す
};

GameObject1.cpp
#include "GameObject.h"

void CGameObject::Initialize()
{
}

void CGameObject::Uninitialize()
{
}

 ちなみに「GameObject1.h」の1行目にある「#pragma once」はそのコードが記述されているファイルを2回以上読み込むのを禁止するためのプリプロセッサです。Microsoft社のCコンパイラ以外では無効となりますが、代わりに以下のように記述することで、他社のコンパイラでも二重読み込みを防止することができます。

GameObject1_2.h
#ifndef CGAMEOBJECT
	// ↑↓宣言名は、このファイル以外で使われる可能性がないのであれば何でもよい
#define CGAMEOBJECT

class CGameObject
{
public:
	void Initialize();		// 初期化時に呼び出す
	void Uninitialize();	// 終了時に呼び出す
};

#endif

 ではInitialize関数の中にDirect3Dの初期化部分を記述しましょう。Direct3Dの初期化にはCreateWindow()で取得したHWNDハンドラがいり、またDirectInput等の初期化にはWinMain()の引数として送られてくるHINSTANCEハンドラが必要ですので、Initialize()の引数として、これらを追加することにします。


転職、求人情報ならリクルートの転職サイト【リクナビNEXT】

 Direct3Dやスプライト描画に用いられるLPDIRECT3D9、LPDIRECT3DDEVICE9、およびLPD3DXSPRITEはプログラムを終了するまで使い続けることになるため、この3つはクラスのstaticなメンバ変数として格納しておきます。Uninitialize関数には、3つのDirect3D関連のインターフェイスを解放するための記述をお忘れ無く。

 これらをふまえて修正したコード例は次の通りです。

GameObject2.h
#pragma once

#include <tchar.h>
#include <windows.h>

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

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

class CGameObject
{
private:
	static HWND hWnd;
	static HINSTANCE hInstance;

public:
	static const HWND GetHWnd(){ return hWnd; }
	static const HINSTANCE GetHInstance(){ return hInstance; }
	
	BOOL Initialize(HWND hWnd, HINSTANCE hInstance);
	void Uninitialize();

protected:
	static LPDIRECT3D9 pD3D;
	static LPDIRECT3DDEVICE9 pD3Ddevice;
	static LPD3DXSPRITE pSprite;
};

GameObject2.cpp
#include "GameObject.h"

HWND		CGameObject::hWnd = NULL;
HINSTANCE	CGameObject::hInstance = NULL;

LPDIRECT3D9			CGameObject::pD3D = NULL;
LPDIRECT3DDEVICE9	CGameObject::pD3Ddevice = NULL;
LPD3DXSPRITE		CGameObject::pSprite = NULL;

BOOL CGameObject::Initialize(HWND hWnd, HINSTANCE hInstance)
{
	this->hWnd = hWnd;
	this->hInstance = hInstance;

	pD3D = Direct3DCreate9(D3D_SDK_VERSION);
    if(pD3D == NULL){
		DXTRACE_MSG(_T("DirectXDeviceの初期化に失敗しました"));
		return FALSE;
    }

    D3DDISPLAYMODE d3ddm;
    if(FAILED(pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm)))
    {
		DXTRACE_MSG(_T("DirectX3DDeviceの初期化に失敗しました"));
		return FALSE;
    }

    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
    d3dpp.PresentationInterval  = D3DPRESENT_INTERVAL_IMMEDIATE;

    if(FAILED(pD3D->CreateDevice(
		D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd,
        D3DCREATE_SOFTWARE_VERTEXPROCESSING,
        &d3dpp,&pD3Ddevice)))
    {
		DXTRACE_MSG(_T("3DDeviceObjectの初期化に失敗しました"));
		return FALSE;
    }

    pD3Ddevice->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );
    pD3Ddevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
    pD3Ddevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );

    if(FAILED(D3DXCreateSprite(pD3Ddevice, &pSprite)))
    {
       DXTRACE_MSG(_T("SpriteObjectの作成に失敗しました"));
       return FALSE;
    }

	return TRUE;
}

void CGameObject::Uninitialize()
{
	RELEASE(pSprite);
	RELEASE(pD3Ddevice);
	RELEASE(pD3D);
}