這個是我再次參考Game Progarmming Pattern - Game Loop這一章節,與Ralylib這個純C的libary得到的一個結果。
之前想說設計是這樣子:
while (true) {
StartScene();
// Input here
// Update here
BeginRendering();
// Render here
EndRendering();
EndScene();
}
然後把時間的處理放在StartScene()
與EndScene()
,但看了Game Programming Pattern之後發現,正常來說如果要限制FPS,達到Framerate Indepent的話,應該是只要對Update()
,也就是邏輯的地方做限制,但上面這個的設計是連Input()
跟Render()
都在裡面了。後來在github上找到一個遊戲libary - Raylib(沒錯!是當"抄"人的時候了),他的做法是這樣
while (true) {
// Input here
// Update here
BeginDrawing();
// Render here
EndDrawing();
Raylib是把delta time稱作frame time,是由update time跟render time合併起來的。
BeginDrawing()
呼叫時會計算update time然後儲存;EndDrawing()
呼叫時,同理會更新render time。然後這兩個相加,就是這一個frame的處理時間,再來就是一個重點了。
// in `BeginDrawing`
WINDOW.Time.current_time = (float)glfwGetTime();
WINDOW.Time.update_time = WINDOW.Time.current_time - WINDOW.Time.previous_time;
WINDOW.Time.previous_time = WINDOW.Time.current_time;
// in `EndDrawing`
WINDOW.Time.current_time = (float)glfwGetTime();
WINDOW.Time.render_time = WINDOW.Time.current_time - WINDOW.Time.previous_time;
WINDOW.Time.previous_time = WINDOW.Time.current_time;
WINDOW.Time.frame_time = WINDOW.Time.render_time + WINDOW.Time.update_time; // 這個就是delta time
如果要限制60 FPS,也就是約0.01666秒的時間,update time加上render time的時間可能會小於0.016666秒,於是可以用一些方法讓game loop強制處理剩餘的秒數。
Sleep
沒錯!讓執行序強制停止N秒,沒記錯的話,SDL中的SDL_Delay
底下就是調用各名台相關的Sleep
while loop
我是用這一種方式,在EndDrawing()
那邊接下去寫來著:
if (WINDOW.Time.frame_time < WINDOW.Time.target_time) {
float wait_time = WINDOW.Time.target_time - WINDOW.Time.frame_time;
// wait time
float start_wait_time = glfwGetTime();
float end_wait_time = 0.0f;
while ((end_wait_time - start_wait_time) < wait_time) {
end_wait_time = glfwGetTime();
}
// add extra time
WINDOW.Time.current_time = (float)glfwGetTime();
float extra_time = WINDOW.Time.current_time - WINDOW.Time.previous_time;
WINDOW.Time.previous_time = WINDOW.Time.current_time;
WINDOW.Time.frame_time += extra_time;
}
昨天有提到Framerate Independent,來實驗一下
在不同的FPS下,高FPS會有較低的delta time;低FPS的會有較高的delta time。故兩者最後都會在同時間抵達目的地。
這邊貼上,我簡單測的code
SetTargetFPS(10); // change fps here!!
float current_time = GetTime();
float previous_time = GetTime();
float time_elapsed = 0.0f;
int test_count = 0;
// Game loop
while (IsWindowRunning()) {
if (e.pos.x > 800.0f) {
current_time = GetTime();
time_elapsed = current_time - previous_time;
previous_time = current_time;
LogInfo("Time elapsed: %f", time_elapsed);
e.pos.x = 0.0f;
}
e.pos = V2fAdd(e.pos, V2fScalef(e.speed, GetDeltaTime()));
StartScene(COLOR_BLACK);
BeginRendering();
DrawRectangle(e.pos, size, 0.0f, e.c);
EndRendering();
EndScene();
}
有了delta time的調節,不管是10 FPS還是144 FPS都會在同個(會有0.0幾的誤差...)時間,從左走到右。如果沒有個話,就會是全看CPU的頻率在表現跟處理了。
備註: raylib作者還有在更新,我貼的是在00a763e
的這個版本