ゲームプログラミングTips(3):タイマ
2012年3月21日……前回の講座より、なんと約10日も経過していた!
原因は講座を書くのをサボってたからではなく、日記を書くのをサボっていたからである。
……どうでもいいか、そんなこと(´Д`)
1.タイマとは?
タイマとは即ちタイマである、タイマーとも言う。
以上、説明終わり(´Д`)
2.タイマのゲームプログラミングにおける活用事例
タイマを使わないゲームがあるのだろうか?いや、ない(反語)
いちいち説明するまでもないと思うが、ゲームでタイマは必須である。
……これだけで終わらせると短すぎるので、タイマの要件でも以下に挙げてみよう。
1.「スタート」で0秒から開始すること
2.「ストップ」でカウントが停止すること
3.再度の「スタート」でカウントが再開すること
4.「リセット」でカウントが0秒に設定されること
何れも当たり前のことではあるが、先に挙げておくことは重要である。
3.具体例(ヘッダ解説)
本項では、俺が作ったタイマクラスを掲載する。
/************************************************* ファイル名:Timer.h 作成者 :あびす 役割 :タイマー *************************************************/ /** * @file Timer.h * @brief 経過時間の取得を行うタイマークラスです。 |
ヘッダに対する解説では本クラスの使用方法を説明する。使用方法は簡単である、
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かはたまたその前に箸休め的に汎用関数か……何れにせよ、近い内にもっと濃い内容にする予定である。