メリークリスマス!そしてハッピーバースデー私!was-blue.0793ことSeiten Minagawaです。
僭越ながら私が令和元年、2019年のDxLib Advent Calendarを主催してみましたが、今年のDxLib Advent Calendarはいかがだったでしょうか?
今回のDxLib Advent Calendarのラストはあまり知られていない関数、SetWaitVSyncFlag関数について考えてみる記事で締めくくろうと思います。
まえがき
垂直同期を待つかどうかを設定するための関数、SetWaitVSyncFlag。
今回はこの「SetWaitVSyncFlag」で垂直同期を切るべき場面を考えてみました。
そもそも垂直同期とは?
簡単に言えば、モニターへの描画が完了するまで処理全体を停止し、描画処理と更新処理の間隔を双方一定となるように処理の間隔を調整することです。
例えば、60FPSでしか描画できないのに1000FPSで描画処理・更新処理を行ってしまうと、描画が追いつかず問題になることがありました。
"ありました"と過去形なのは、筆者が制作した垂直同期を切るようにしているアプリをいくつかの環境で使ってみても、描画関連の問題が起きたことがなく、むしろ一部のゲームの記事では垂直同期を切ることを推奨している記事が見受けられたためです。
垂直同期を切るべき場面
音楽ゲーム
音楽ゲームは一般的に正しいタイミングの時間が使用されます。
そうなった時、60FPSの型にはめてしまうと1フレーム単位、つまり1/60秒(16ms)単位でしか判定できず、1ms、またはそれ以上で精度を判定したい場合に都合が悪いです。(特にいわゆる"キー音"があるゲームでは)
そんな時は、垂直同期を切って16msより速い間隔で判定するようにするべきです。
懸念されるのはティアリングですが、近年のモニターでそのような問題が発生するケースはほぼないと考えています。
絶対に特定のFPSで動かしたいゲーム
例えば格闘ゲームは、1/60秒単位で同時押しやコマンド入力の受付時間を設定しているケースが多いです。
「ゲームは絶対に60FPSで動く!」と思いがちですが、実はこのFPSは環境依存です。
これを環境依存せずに絶対に特定のFPSで動かしたい場合は、垂直同期を切り自前のFPS制御を行うことになります。
const int refrashtime = 1000 / 60;
int sectiontime = GetNowCount();
while (ProcessMessage() == 0)
{
if (GetNowCount() - sectiontime >= refrashtime)
{
sectiontime = GetNowCount();
Update();
Draw();
}
}
SetWaitVSyncFlagの注意点
SetWaitVSyncFlag関数は、Windows版ではDirectXとの兼ね合いでDxLib_Initを呼ぶ前にしか使えません。
Android版では、DxLib_Init関数を呼んだ後でもSetWaitVSyncFlagで垂直同期を切るか切らないかを動的に切り替えることができますが、Windows版でも動的に垂直同期を切り替えたい場合は、以下のようにします。
(コードを平易にするためグローバル変数を使用していますが、一般的にはクラス変数などに閉じ込めて管理するようにします)
//垂直同期を切るかどうか切り替えるグローバル変数
bool vsyncflag;
while (ProcessMessage() == 0)
{
if (vsyncflag) WaitVSync(1);
Update();
Draw();
}
コードのライセンスについて
Apache License 2.0にします。著作権者名は「Seiten Minagawa」としてください。