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) | ゲーム制作

2011年07月27日

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

ゲームとしてのメイン処理

一般的にゲームプログラムの場合は「ゲームループ」と言われるループ構造によって処理されることが多いです。無限ループの中で「入力」「更新」「描画」をくり返し実施します。

「入力」というのは、キーボード・マウス・パッド(ゲーム機のコントローラのようなもの)からの入力を受け付ける部分です。

「更新」というのは、入力したデータを元にキャラクターを移動させたり、あるいはコマンドを選択したりするものと、入力が何も無くても勝手に行うもの(アニメーションや、敵キャラクターの移動など)があります。

「描画」というのは、キャラクターやテキストなどを画面に出力する部分です。

例えばシューティングゲームでは、プレイヤーからの「入力」により「プレイヤーキャラクターを移動」させたり「弾を発射」したり「爆弾(ボム)を爆発(発動)」させたりしますが、「入力」がなくても「敵キャラクターは移動」するし「弾を発射」したりします。

プログラムのイメージとしては次のようになります。

Main()
{
    while (true)    // 無限ループ
    {
        入力処理();
        更新処理();
        描画処理();
    }
}

実際にはこの他に、イニシャル処理や終了処理、ゲームを止めるための仕組み、などが必要です。

また、Windowsアプリケーションでは定期的にWindowsメッセージを処理する必要があるため、ループ内にその仕組みを入れる必要があります。これは、DXライブラリを使う場合は「DX.ProcessMessage()」を呼び出すだけです。この関数はウィンドウが閉じられると「-1」を返すため、「ゲームを止めるための仕組み」としても使えます。

Main()
{
    初期化処理();
    if(初期化に失敗)
    {
        終了;
    }
    while (true)    // 無限ループ
    {
        入力処理();
        更新処理();
        描画処理();

        DX.ProcessMessage(); // Windowsメッセージの処理
        if(戻り値が-1)
        {
            break; // ループを抜ける
        }
    }
    終了処理();
}

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

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

2011年07月24日

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

テストプログラムの作成

とりあえず簡単なゲームを作ってみることにします。ゲーム制作の解説として多いのはコマンド入力式のアドベンチャーゲームや、シューティングゲームですが、私はもっと簡単なもので良いと思います。ここでは、『○×ゲーム』(三目並べ)を題材にします。

アプリケーションのエントリーポイント

ゲーム本体の前に、Windowsアプリケーションとして動作するための基本的な部分を記述します。いわゆるMain関数にあたるところです。ここでは、Windowsアプリケーションとしての初期化処理と終了処理、ゲームの本体部分の呼び出しを行います。もちろんDXライブラリの初期化処理・終了処理も行います。

関数のイメージとしては、このような感じになります。

Main()
{
	初期化処理();
	if(初期化に失敗)
	{
		終了;
	}
	ゲームの本体部分();
	終了処理();
}

実際のソースコードは次のようになります。

    class SystemMain
    {
        /// <summary>
        /// アプリケーションのエントリーポイント(ここからプログラムが始まる)
        /// </summary>
        [STAThread]
        static void Main()
        {
            // ウィンドウモードに切り替え
            DX.ChangeWindowMode(DX.TRUE);

            // ウィンドウタイトルの変更
            DX.SetMainWindowText("○×ゲーム");

#if !DEBUG
            // ログ出力を行わない
            DX.SetOutApplicationLogValidFlag(DX.FALSE);
#endif
            
            // ウィンドウのサイズ指定
            DX.SetGraphMode(800, 600, 32);

            // DXライブラリの初期化
            if (DX.DxLib_Init() == -1)
            {
                // 初期化に失敗した場合は終了
                return;
            }

            // ゲームのメイン部分を生成
            GameMain gm = new GameMain();

            // ゲームのメイン部分を実行
            gm.Main();

            // DXライブラリの終了処理
            DX.DxLib_End();

            return;
        }
    }

DXライブラリの関数を使い、「ウィンドウモードへの切り替え」「ウィンドウタイトルの変更」を行います。DXライブラリは初期状態だとログを出力するので、デバッグビルド以外のときは「ログ出力を行わない」ようにします。また、「ウィンドウのサイズ指定」も行っておきます。

「DXライブラリの初期化」に失敗すると戻り値に「-1」が返されるので、その場合は「終了」させます。このときエラー表示などを行った方が親切ですが、テストプログラムなので省略します。

ゲームのメイン部分は「GameMain」というクラスの「Main」に記述することにします。「GameMain」のインスタンスを生成し、「Main」を実行します。

最後に「DXライブラリの終了処理」を実行してアプリケーションは終わります。

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

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