2011年07月31日

C#とDXライブラリでゲーム制作 7回目

キーボード押下状態の取得

DXライブラリでキーボード押下状態を取得するためには、GetHitKeyStateAllを使います。(CheckHitKeyを使うこともできますが、“CheckHitKey 関数はとても無駄の多い関数”なので、GetHitKeyStateAllを使うようにします)

基本的には下記のように使います。

// キーボード押下状態を保存するバッファの宣言
Byte[] keyStateBuf = new byte[256];

// キーボード押下状態の読み込み
DX.GetHitKeyStateAll(out keyStateBuf[0]);

// 押下状態の判定(例:リターンキーが押されたか?)
if ( keyStateBuf[ DX.KEY_INPUT_RETURN ] == 1 )
{
    // リターンキーが押された
}

しかし、この方法ではリターンキーを押しっぱなしにすると、常に「リターンキーが押された」と判定してしまいます。場面によってはリターンキーが押された瞬間だけ処理をしたいこともありますね。例えばテトリスのような落ちモノパズルを考えてみてください。左右の移動や落下についてはカーソルキーが押されている間動き続けても良いでしょうが、回転についてはボタンを押した瞬間だけ処理をするようにしないと、ボタンを押し続けている間、回転し続けることになります。これではゲームになりません。

対処方法として、キーボード押下状態を保存するバッファを2つ持ち、順番に切り替えて状態を取得します。そして、「直前は放されていた」「今回は押されている」の条件を満たしたときだけ「キーが入力された」と判断します。

// キーボード押下状態を保存するバッファ
byte[,] keyStateBuf = new byte[2, 256];

// バッファのフレーム(どちら側が有効か)
int keyStateBufNo = 0;

〜キーボード押下状態を読み込むとき〜

// フレームを切り替える
keyStateBufNo ^= 1;

// 今のフレームにキーボード押下状態を読み込む
DX.GetHitKeyStateAll(out keyStateBuf[keyStateBufNo, 0]);

〜キーボード押下状態の判定のとき〜

// 今のフレーム:押されている AND 前のフレーム:放されている のとき true
if ( (keyStateBuf[ KeyStateBufNo, DX.KEY_INPUT_RETURN ] == 1) &&
    (keyStateBuf[ KeyStateBufNo ^ 1, DX.KEY_INPUT_RETURN ] == 0))
{
    // 入力されたと判断
}

キーボードの入力処理は毎フレーム実行する必要がありますし、キーボード押下状態の判定は演算処理で度々使うことになるため、クラス化しておくことにします。

InputController.cs

良かったらクリックしてください
にほんブログ村 IT技術ブログ プログラム・プログラマーへ  人気ブログランキングへ

posted by among at 20:00 | Comment(0) | TrackBack(0) | ゲーム制作

2011年07月30日

C#とDXライブラリでゲーム制作 6回目

ダブルバッファリングによる描画処理

描画処理の基本は「画面の消去」「画面の描画」のくり返しになりますが、実際に毎フレーム「消去」「描画」を実行すると、画面のちらつきが発生します。このちらつきを消すために「ダブルバッファリング」という手法があります。

DXライブラリでは「ダブルバッファリング」を簡単に扱えるようになっています。

// 描画先を裏画面に設定する
DX.SetDrawScreen(DX.DX_SCREEN_BACK);

// 画面に描かれているものを一回全部消す
DX.ClsDrawScreen();

// ここで描画処理をさせる
Draw();

// 裏画面の内容を面画面に反映される
DX.ScreenFlip();

このように実行します。

実際に画面へ描画するのはDraw()の中で行います。

良かったらクリックしてください
にほんブログ村 IT技術ブログ プログラム・プログラマーへ  人気ブログランキングへ

posted by among at 21:00 | Comment(0) | TrackBack(0) | ゲーム制作

2011年07月29日

C#とDXライブラリでゲーム制作 5回目

フレームレートの制御(FPS制御)

ゲームループをそのまま実装すると、パソコンの性能によってゲームの進行速度が変わってしまうという問題が発生します。制作者のPCの性能に合わせてゲームを作った場合、それよりも性能が高い・低いPCでは、ゲームの進行速度が違うため、ゲームとして成立しないことがあります。例えるなら、早送りやスローモーションでゲームをやるようなものです。

これを防ぐために、ループ内でスリープすることを考えます。例えば1ループにつき20ms(ミリ秒)のSleepを行えば、性能の高いPCでの問題は解決しそうです。しかし、「更新処理」や「描画処理」などにかかる時間の分は調整できません。アドベンチャーやシミュレーションであれば大きな問題にはならないかもしれませんが、アクションやシューティングでは致命的な問題になるかもしれません。それに、性能の低いPCでの問題は解決できません。

そこで「フレームレートの制御」を行います。

「フレームレートの制御」「フレーム制御」「FPS制御」などの名前で呼ばれるこの機能は、ゲームループと同じくゲーム制作における基本技術です。「FPS」は「Frames Per Second」のことで「フレームレート」を表す単位です。(したがって“FPS制御”という名前は間違っているような気がしますが、昔からこのような名称で呼ばれていました)

「フレームレートの制御」についての詳細は、この場では説明しきれないので割愛させていただきます。幸いにも解説しているサイトはいくらでもあるので、上記のキーワードで検索してみてください。

フレームレート制御の概略

フレームレートを制御するためには、「ディレイ」と「描画スキップ」の2つが必要です。1フレームの処理が早く終わって時間が余っている場合は「ディレイ」で調整します。1フレームの処理に時間がかかってしまい足りなくなっている場合は「描画スキップ」で時間を稼ぎます。これは1フレームの処理で最も時間がかかるのが「描画処理」であり、他の「入力処理」「更新処理」は大した時間がかからないためです。

具体的には、各フレームの開始時刻を演算で求め、その時刻まで間があれば「ディレイ」、そして次のフレームまで余裕があれば「描画処理」を行う。余裕がなければ「描画スキップ」という風にします。

これをゲームループに入れるとこのようになります。

while (true)    // 無限ループ
{
    次のフレームを待つ();
    入力処理();
    更新処理();
    if(描画する時間がある)
    {
        描画処理();
    }
    DX.ProcessMessage(); // Windowsメッセージの処理
    if(戻り値が-1)
    {
        break; // ループを抜ける
    }
}

「各フレームの開始時刻」はフレームレートに合わせて1フレームの時間を加算していくことで計算します。

「次のフレームを待つ」は現在の時刻を取得し、「次のフレームの時刻」になるまでスリープしながら待ち続けます。

「描画する時間がある」かどうかの判定は、現在の時刻から「次の次のフレームの時刻」まで時間があるかどうかで決めます。

フレームレート制御クラス

フレームレートを制御するためのクラス『FPSTimer.cs』を作ったので公開します。フレームレートの制御だけでなくフレームレートを測定する機能もつけました。本当に真面目にフレームレートを制御するには不十分ですが、個人で作るレベルのゲームであれば十分だと思います。

FPSTimer.cs

良かったらクリックしてください
にほんブログ村 IT技術ブログ プログラム・プログラマーへ  人気ブログランキングへ

posted by among at 21:00 | Comment(1) | TrackBack(0) | ゲーム制作