15:タスク処理を実装する(5)
Direct3Dの描画処理タスクを作成し、リストに追加します。描画開始処理の実行優先度は0、描画終了処理の実行優先度はINT_MAXにし、敵キャラなど描画に必要なデータの優先度は1~(INT_MAX - 1)の範囲とします。関数の中でCGameObject派生クラスを直接new
で作っても全く問題ありません。autodelete引数をtrue
にしておけば、削除はプログラムが勝手にやってくれます。
では、「GameMain.h」「GameMain.cpp」の2つのファイルを新規にプロジェクトに追加し、描画開始処理と終了処理を行うためのクラスを作成しましょう。
ウェイト処理を用意する
家庭用テレビが1秒間あたり60回の表示更新を行うこともあってか、たいていのゲームは1秒間に付き60回、つまり約16ms(=0.016秒)につき1回描画を行うような設計になっています。しかし、単純に指定時間だけ待機するウィンドウズ命令「Sleep(16);」を記入しただけでは不都合が起こります。例えば、ゲーム処理の計算に10msかかったとしたら、実際に画面が更新されるのは「10ms + Sleep(16) = 26ms」後になり、コンピュータにそれほど負担がかかっているわけでもないのに、処理落ちしたかのような挙動になってしまいます。これを解決するには、処理計算にかかった時間を取得して、ゲーム標準の待機時間から差し引けばよいことになります。
const DWORD WAIT_TIME = 16; // 約60FPS
//最後に行った処理からの時間を調べる
DWORD ntime = timeGetTime(); // システムが起動してからの時間を取得
DWORD rtime = ntime - lasttime;
lasttime = ntime;
if(rtime < WAIT_TIME){
//ウェイト処理を行う
Sleep(WAIT_TIME - rtime);
}
timeBeginPeriod(1);これにより、時間計測の厳密さが1ミリ秒単位に変更されるので、正確なウェイト処理が行われるようになります。
描画開始・終了処理を用意する
おおまかにすると、Direct3Dによる描画に必要な一連の命令は以下のようになります。
これをタスク処理に割り当てると、描画開始時に「Clear()」と「BeginScene()」、描画終了時には「EndScene()」と「Present()」を行えばよいことになります。これらをふまえてクラスを作成した結果がこちらです。
- Clear()で描画済みのデータを消去します。
- BeginScene()で、バックグラウンドにてシーンの描画を始めます。
- ID3DXSprite::Draw()などをつかって、描画を行います。
- EndScene()でシーンの描画を終わります。
- Present()でウィンドウに描画結果を転送し、実際に表示されます。
#pragma once
#include "GameObject.h"
class CTaskHead : public CGameObject
{
private:
DWORD lasttime;
protected:
void Init();
void Exec();
};
class CTaskTail : public CGameObject
{
protected:
void Exec();
};
#include "GameMain.h"
void CTaskHead::Init()
{
lasttime = 0; // 値の初期化
}
void CTaskHead::Exec()
{
const DWORD WAIT_TIME = 16; //約60FPS
//最後に行った処理からの時間を調べる
DWORD ntime = timeGetTime();
DWORD rtime = ntime - lasttime;
lasttime = ntime;
if(rtime < WAIT_TIME){
//ウェイト処理を行う
Sleep(WAIT_TIME - rtime);
}
//画像のクリア
pD3Ddevice->Clear(
0, // クリアする領域の配列個数
NULL, // クリアする領域の配列
D3DCLEAR_TARGET, // 対象を指定の色でクリアする
D3DCOLOR_XRGB(0,0,64), // クリアする色を紺色に指定
1.0f, // z方向のクリア(1.0fですべてをクリア)。
0 // ステンシルバッファのクリア(使用していないので0を指定)
);
//開始宣言
pD3Ddevice->BeginScene();
}
void CTaskTail::Exec()
{
// 表示
pD3Ddevice->EndScene();
pD3Ddevice->Present(
NULL, // 転送元の領域(NULLで全域指定)
NULL, // 転送先の領域(NULLで全域指定)
NULL, // 転送先のウィンドウを示すハンドル(NULLで標準のウィンドウ)
NULL // 現行バージョンでは常にNULLを指定
);
}
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);
game.AppendObject(new CTaskHead(), 0, true);
game.AppendObject(new CTaskTail(), INT_MAX, true);
ShowWindow(hWnd, nCmdShow);
