> 新・C言語 〜ゲームプログラミングの館〜 [DXライブラリ]
> 3.14章 特定のFPSで動作させる方法
> dixq.net/g/03_14.html
最近 C# で DX ライブラリの使い方勉強中。以上の文章を読んで、試しにFPS制御を実装しました:
----------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DxLibDLL;
namespace DXLib_TEST
{
class Program
{
// Global Timer
public static System.Diagnostics.Stopwatch StopWatch = new System.Diagnostics.Stopwatch();
// Constants
public const int FPS = 60; // Target FPS
// Variables
public static uint FrameCounter;
public static double TimeOfFirstFrame;
static void Main(string[] args)
{
// Use Main Window as Log Console
Console.Title = "Log Window";
Console.WriteLine("Program started.");
// Init DXLib
DX.SetMainWindowText("DXLib TEST");
DX.ChangeWindowMode(DX.TRUE);
DX.SetGraphMode(480, 320, 32);
DX.SetAlwaysRunFlag(DX.TRUE);
DX.SetWaitVSyncFlag(DX.FALSE);
if (DX.DxLib_Init() == -1) {
Console.WriteLine("DXLibrary failed to load. \nPress any key to exit.");
Console.ReadLine();
return;
}
// Need to set after Init
DX.SetDrawScreen(DX.DX_SCREEN_BACK);
Console.WriteLine("DXLibrary loaded.");
// Some Values
double fps_counter = 0; // Average FPS in [target FPS] frames
double time_record = 0; // record once per[target FPS]
uint loops_waited_per_FPS = 0;
// Main Loop
StopWatch.Start();
while (DX.ProcessMessage() == 0 && DX.ClearDrawScreen() == 0) {
FrameCounter++;
// FPS Counter
if (FrameCounter == 1) {
TimeOfFirstFrame = StopWatch.Elapsed.TotalMilliseconds;
} else if (FrameCounter < FPS) {
fps_counter = 1000 / ((StopWatch.Elapsed.TotalMilliseconds - TimeOfFirstFrame) / FrameCounter);
DX.SetMainWindowText($"[{fps_counter:0.00} FPS]");
} else if (FrameCounter % FPS == 0) {
fps_counter = 1000 / ((StopWatch.Elapsed.TotalMilliseconds - time_record) / FPS);
time_record = StopWatch.Elapsed.TotalMilliseconds;
DX.SetMainWindowText($"[{fps_counter:0.00} FPS ({loops_waited_per_FPS} loops waited)]");
loops_waited_per_FPS = 0;
}
// Calculation
// Task.Delay(64).Wait(); // Simulate low speed
// Draw
DX.DrawString(10, 10, $"Frame : {FrameCounter}", DX.GetColor(255, 255, 255));
DX.DrawString(10, 30, $"Timer : {StopWatch.Elapsed.TotalMilliseconds} ms", DX.GetColor(255, 255, 255));
DX.DrawString(10, 50, $"Ticks : {StopWatch.ElapsedTicks}", DX.GetColor(255, 255, 255));
// Wait before ScreenFlip to control fps
while (CalculateWaitTime() > 0) {
loops_waited_per_FPS++;
DX.ProcessMessage();
}
DX.ScreenFlip();
}
// Exit
DX.DxLib_End();
return;
}
private static double CalculateWaitTime()
{
return FrameCounter * 1000 / FPS - (StopWatch.Elapsed.TotalMilliseconds - TimeOfFirstFrame);
}
}
}
----------------------------------------------------------------------------------------------------
・DX.GetNowCount() の代わりに C# の Stopwatch 使った理由はオーバーフローを防ぐためです
(自分のパソコン滅多に再起動しない、今の Uptime は既に 13 日以上…)
・精度を追求して Thread.Sleep() を使ってない(精度低いし、1ms 以下にもなれない)
----------------------------------------------------------------------------------------------------
1FPSでも、120FPSでも期待通り動く、やったね!
ですが、自分のパソコンで1秒(正確にいえば設定したフレーム)に...
( ;゚Д゚)「50万回以上」もループする!
結果として、秒で50万回以上 DX.ProcessMessage() を執行しています。
(´・ω・`;) これ…大丈夫なの?
----------------------------------------------------------------------------------------------------
編集:SetDrawScreen() を DxLib_Init() より前なら反映しないと発見しましたので、コードを修正しました。