ゲームプログラミングTips(3):タイマ

 

2012年3月21日……前回の講座より、なんと約10日も経過していた!
原因は講座を書くのをサボってたからではなく、日記を書くのをサボっていたからである。
……どうでもいいか、そんなこと(´Д`)

 

1.タイマとは?

 タイマとは即ちタイマである、タイマーとも言う。
 以上、説明終わり(´Д`)

 

2.タイマのゲームプログラミングにおける活用事例

 タイマを使わないゲームがあるのだろうか?いや、ない(反語)
 いちいち説明するまでもないと思うが、ゲームでタイマは必須である。

 ……これだけで終わらせると短すぎるので、タイマの要件でも以下に挙げてみよう。

 1.「スタート」で0秒から開始すること
 2.「ストップ」でカウントが停止すること
 3.再度の「スタート」でカウントが再開すること
 4.「リセット」でカウントが0秒に設定されること

 何れも当たり前のことではあるが、先に挙げておくことは重要である。

 

3.具体例(ヘッダ解説)

  本項では、俺が作ったタイマクラスを掲載する。

/*************************************************
ファイル名:Timer.h
作成者  :あびす
役割   :タイマー
*************************************************/
/**
*	@file	Timer.h
*	@brief	経過時間の取得を行うタイマークラスです。
* ポーズを行うことも可能です。 */ #ifndef DX9A_MISC_TIMER_H #define DX9A_MISC_TIMER_H namespace nsDX9A{ namespace nsMisc{ namespace nsTimer{ //タイマー /** * @brief 経過時間の取得を行うタイマークラスです。
* ポーズを行うことも可能です。 */ class CTimer{ public: /** * @brief デフォルトコンストラクタです。 */ CTimer(); //デフォルトコンストラクタ /** * @brief デストラクタです。 */ ~CTimer(); //デストラクタ /** * @brief 経過時間のリセットを行います。
* メソッド実行後、経過時間が再び0から開始となります。 */ void Reset(); //リセット(経過時間を0に設定) /** * @brief 経過時間の取得を行います。 * @return 経過時間です(単位:ミリ秒) */ DWORD Get() const; //経過時間を取得 /** * @brief ポーズを行います。
* ポーズ中は、経過時間が固定されます。 * * (例:経過時間=1000の場合、Pauseメソッド実行後は何秒経過しようと経過時間=1000となります) */ void Pause(); //ポーズ /** * @brief ポーズ解除を行います。
* ポーズ解除後は、経過時間の固定が解除されます。 * * (例:経過時間=1000の場合、Restartメソッド実行後は経過時間が再び1000から開始となります) */ void Restart(); //ポーズ解除 /** * @brief ポーズ中かを取得します。 * @return ポーズ中の場合はtrue、ポーズ中でない場合はfalseを返します。 */ bool IsPause() const; //ポーズ中か? /** * @brief 経過時間の更新を行います。
* 本メソッドを実行しないと、経過時間の更新は行われません。 * * (例:経過時間=1000の場合、Flushメソッドが実行されない間はずっと1000のままとなります) */ void Flush(); //経過時間を更新 private: DWORD m_ElapsedTime; //経過時間 DWORD m_OffsetTime; //オフセット値 DWORD m_PauseTime; //ポーズ時の経過時間 bool m_IsPaused; //ポーズ中か? }; } } } #endif

 ヘッダに対する解説では本クラスの使用方法を説明する。使用方法は簡単である、 

 1.インスタンスを生成する
 2.各種メソッドを使用する

具体例

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){
	CTimer t;
	do{
		t.Flush();
	}while(t.Get()<10000);
	MessageBox(NULL, "もう終わっていいよね!", "10秒経ったよ!", MB_OK | MB_ICONWARNING);
	return(0);
}

 ……これだけである(´Д`)
 Reset、Get、Pause、Restart、IsPauseメソッドの使い方は上記ソース内のコメント通りである。

 本クラスのポイントはFlushメソッドである、本タイマはFlushメソッドを呼び出さなければカウントが更新されない。
 理由を述べると、「カウントを更新してほしくない」場合があるためである。以下に例を示そう。

カウントを更新してほしくない具体例(擬似コード)

DrawText("残り時間:%dミリ秒", t.Get());
DrawText("タイムボーナス:%d", 100000000 - t.Get());

 上記の擬似コードは、残り時間とそれに伴うタイムボーナスの表示を行っているものである。
 ここで、もしタイマがFlushメソッドがなく自動でカウントを更新する実装の場合問題が発生する。

 ・1行目のDrawText関数で処理に1ミリ秒以上の時間が掛かった場合、残り時間とタイムボーナスがずれる

 上記の例の場合だとたかが表示のずれの話だが、普通に考えるとバグだろう。
 つまり、ある処理の間はタイマのカウントを固定にしておきたい場合があるのである。
 Flushメソッドでカウントを更新する実装の場合、1行目よりも前でFlushメソッドを呼び出せばずれはなくなる。
 常にリアルタイムのカウントを取得したい場合は、単純にFlushメソッドとGetメソッドをセットにして呼び出せば良い。

 

4.具体例(ソース解説)

 次はソース解説である。

#include "stdafx.h"
#include "Timer.h"

namespace nsDX9A{
namespace nsMisc{
namespace nsTimer{

//デフォルトコンストラクタ
CTimer::CTimer(){
	timeBeginPeriod(1);
	Reset();
}
//デストラクタ
CTimer::~CTimer(){
	timeEndPeriod(1);
}

//リセット(経過時間を0に設定)
void CTimer::Reset(){
	m_ElapsedTime = m_PauseTime = 0;
	m_OffsetTime = timeGetTime();
	m_IsPaused = false;
}
//経過時間を取得
DWORD CTimer::Get() const{
	return(m_IsPaused ? m_PauseTime : m_ElapsedTime);
}
//ポーズ
void CTimer::Pause(){
	if(!m_IsPaused){
		m_IsPaused = true;
		m_PauseTime = m_ElapsedTime;
	}
}
//ポーズ解除
void CTimer::Restart(){
	if(m_IsPaused){
		m_IsPaused = false;
		m_OffsetTime = timeGetTime() - m_PauseTime;
	}
}
//ポーズ中か?
bool CTimer::IsPause() const{
	return(m_IsPaused);
}

//経過時間を更新
void CTimer::Flush(){
	m_ElapsedTime = timeGetTime() - m_OffsetTime;
}

}
}
}

 特に語るべきところはない、見たまんまである。
 ポーズ周りだけややこしいが、読み解けば分かる筈なので説明は割愛する<それで良いのかよ!

 一応、WindowsAPI周りだけ軽く説明する。

 timeBeginPeriod

 最小タイマ分解能を設定する。
 分からなければ、タイマ使用前に「timeBeginPeriod(1);」っておまじないが必要だよ!って覚えとけば良い<それで良い(略

 timeEndPeriod

 最小タイマ分解能の設定をクリアする。
 分からなければ、タイマ使用後に「timeEndPeriod(1);」っておまじないが必要だよ!って覚えとけば良い<それで(略

 timeGetTime

 システムが起動してからの時間を取得する。
 つまり、タイマとして使うには「timeGetTime() - カウント開始時に実行したtimeGetTime()の値」で経過時間を求める必要がある。

 

 

 

……以上で本項は終了である、お疲れ様でした(´Д`)
本当はGetTickCountとかQueryPerformanceCounterについても書こうか迷ったが、面倒臭いので止めた<おい
……ま、ゲームプログラミングにおいてはtimeGetTimeさえ覚えておけば大丈夫だろう(多分、保証はしないけど)
さて、次回はDirectX Graphicsかはたまたその前に箸休め的に汎用関数か……何れにせよ、近い内にもっと濃い内容にする予定である。

 

前へ                                        戻る                                        次へ