【DXライブラリ】フレームレートを固定する

DXライブラリでフレームレートを固定する方法について説明しています。

フレームレートはモニターのリフレッシュレートに依存するのできちんと設定しないと環境によっては倍速になったりして大変なことになります(知人にテストプレイを頼むときとか特に)。

フレームレートを制御したい

PCゲームをやると必ずと言っていい程耳にするのがフレームレート。1秒間に60回画面が描写される、60FPSだと滑らかに動くというあれ。一人称視点ゲーム(FirstPersonShooting)の方ではない。

このフレームレート、設定をしなくてもどんなゲームでも勝手に60FPSで動くのかと思いきゃ実はユーザの環境依存だったりする。

DXライブラリを使ってゲームを作る場合、以下のwhile文の中身がゲームのメイン処理になる。

while (1) {
    //裏画面を表画面に反映, メッセージ処理, 画面クリアの3つが正しく動作してるか確認
    //どれか1つでも異常があれば強制終了
    if (!(ScreenFlip() == 0)) return false;
    if (!(ProcessMessage() == 0)) return false;
    if (!(ClearDrawScreen() == 0)) return false;

    //以下ここにキー入力を得るとかプレイヤーの情報更新とかあれこれ書く
}

60FPSで動かす場合、1秒間にこのwhile()の部分が60回処理するわけだがこれはモニターのリフレッシュレートの値に依存していて120Hzだったりすると倍の120回処理されることになる。なのでゲーム速度が大きく変わってしまう。

ということでフレームレート制御関数は大事。

サンプルコード

さっそくコードをペタリ。60FPSで固定するコード。詳しい説明はこのコードの参考にさせてもらったこちらのサイトで。今回はFpsControll.cpp/hの2つを追加した。

FpsControll.h

#ifndef DEF_FPSCONTROLL_H

#define DEF_FPSCONTROLL_H

void FpsControll_Initialize();
bool FpsControll_Update();
void FpsControll_Draw();
void FpsControll_Wait();

#endif

FpsControll.cpp

#include <math.h>
#include "DxLib.h"
#include "FpsControll.h"

static int mStartTime;      //測定開始時刻
static int mCount;          //カウンタ
static float mFps;          //fps
static const int N = 60;  //平均を取るサンプル数
static const int FPS = 60;  //設定したFPS

static const int FPS_X = 0;
static const int FPS_Y = 20;

//初期化
void FpsControll_Initialize() {
    mStartTime = GetNowCount();
    mCount = 0;
    mFps = 0;
}

//FPS制御
bool FpsControll_Update() {
    if (mCount == 0) { //1フレーム目なら時刻を記憶
        mStartTime = GetNowCount();
    }
    if (mCount == N) { //60フレーム目なら平均を計算する
        int t = GetNowCount();
        mFps = 1000.f / ((t - mStartTime) / (float)N);
        mCount = 0;
        mStartTime = t;
    }
    mCount++;

    return true;
}

//FPS表示
void FpsControll_Draw() {
    DrawFormatString(FPS_X, FPS_Y, GetColor(255, 255, 255), "%.1f", mFps);
}

void FpsControll_Wait() {
    int tookTime = GetNowCount() - mStartTime;  //かかった時間
    int waitTime = mCount * 1000 / FPS - tookTime;  //待つべき時間

    if (waitTime > 0) {
        Sleep(waitTime);  //待機
    }
}

これで60FPSで動くようになるが、環境によっては43~45FPSあたりでウロウロと低フレームレート状態になってしまうことがあるかもしれない(以前の自分の環境がそうだった)。そんな時は垂直同期を切ってあげると正常に動作するようになる。

垂直同期を切る関数は SetWaitVSyncFlag() だがこの関数はDXライブラリの初期化より前に置いてあげる必要がある。

//垂直同期を切る。DxLib_Init()実行前に書くこと
    SetWaitVSyncFlag(0);

    // DXライブラリ初期化処理
    if (DxLib_Init() < 0) {
        //エラーなら終了する
        return -1;
    }

ゲームのメイン処理となるwhile()部分に先ほどのフレームレート制御関数を挿入する。

System.cpp

//メインループ
bool System_MainLoop() {
    while (1) {
        //裏画面を表画面に反映, メッセージ処理, 画面クリアの3つが正しく動作してるか確認
        //どれか1つでも異常があれば強制終了
        if (!(ScreenFlip() == 0)) return false;
        if (!(ProcessMessage() == 0)) return false;
        if (!(ClearDrawScreen() == 0)) return false;

        //フレームレート制御
        FpsControll_Update();

        //キーボードの入力状態を更新
        Input_UpdateKeyboard();

        //テキストなどの描画
        System_Draw();

        //フレームレート表示
        FpsControll_Draw();

        //フレームレート待機
        FpsControll_Wait();

        //ESCキーが30フレーム以上押されていれば終了
        //押された瞬間で判定する場合は if(Input_GetKeyboardDown())
        //離された瞬間で判定する場合は if(Input_GetKeyboardUp())  とする
        if (Input_GetKeyboard(KEY_INPUT_ESCAPE) > 30) {
            break;
        }
    }

    return true;
}

低スペックのPCやスマホ向けにゲームを開発する場合、フレームレートは30にしておくと処理が軽くなるので30に固定するかプレイ中に変更できるようにするのがベター。

余談だがかなり前にモニターを買い替えたことがあり、その後自作ゲームを動かしてみたらなんか動きが妙に遅いという違和感があった。原因はリフレッシュレートの違いだというのがわかるまでけっこう時間がかかってしまった(ちなみに前のモニターは75Hzだった)。

コメント

タイトルとURLをコピーしました