【SDL游戏编程入门第十九卷】获取DeltaTime并控制帧率
一、前言
在 SDL2 中,常用的有两种方式获取时间和控制帧率
1. SDL_GetTicks64()
2. SDL_GetPerformanceCounter() 和 SDL_GetPerformanceFrequency()
二、使用 SDL_GetPerformanceCounter 示例代码
#include <SDL.h>
#pragma comment(lib, "SDL2.lib")
#pragma comment(lib, "SDL2main.lib")
class Game
{
private:
SDL_Window* m_Window;
SDL_Renderer* m_Renderer;
SDL_Event m_SDLEvent;
bool m_BGameRunning;
// deltatime
float m_DeltaTime;
Uint64 m_StartTime;
Uint64 m_PreFrameTime;
void InitWindow()
{
m_Window = SDL_CreateWindow("HelloSDL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
m_Renderer = SDL_CreateRenderer(m_Window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
}
public:
Game()
:
m_BGameRunning(true),
m_DeltaTime(0)
{
InitWindow();
// deltaTime
m_StartTime = SDL_GetPerformanceCounter();
m_PreFrameTime = m_StartTime;
}
~Game()
{
SDL_DestroyRenderer(m_Renderer);
SDL_DestroyWindow(m_Window);
SDL_Quit();
}
void Run()
{
while (m_BGameRunning)
{
UpdateDeltaTime();
Update();
Render();
}
}
void Update()
{
UpdateSDLEvent();
}
void Render()
{
SDL_SetRenderDrawColor(m_Renderer, 0, 0, 0, 255);
SDL_RenderClear(m_Renderer);
// Draw items
SDL_RenderPresent(m_Renderer);
}
void UpdateDeltaTime()
{
/* Updates the deltaTime with the time it takes to update and render one frame. */
Uint64 currTime = SDL_GetPerformanceCounter();
m_DeltaTime = (currTime - m_PreFrameTime) / (float)SDL_GetPerformanceFrequency();
m_PreFrameTime = currTime;
float totalTime = (currTime - m_StartTime) / (float)SDL_GetPerformanceFrequency();
float FPS = 1.0f / m_DeltaTime;
SDL_LogInfo(0, "totalTime: %f\tdt: %f\tFPS: %f\n", totalTime, m_DeltaTime, FPS);
// 限制帧率
int MaxFPS = 60;
if (m_DeltaTime < 1.0f / MaxFPS * 1000.0f)
SDL_Delay(Uint32(floor(1.0 / MaxFPS * 1000.0 - m_DeltaTime)));
}
void UpdateSDLEvent()
{
while (SDL_PollEvent(&m_SDLEvent))
{
switch (m_SDLEvent.type)
{
case SDL_QUIT:
m_BGameRunning = false;
break;
}
}
}
};
int main(int argc, char* argv[])
{
Game game;
game.Run();
return 0;
}
下面第二种方式,只做了一点点改动,在精度上会低一点
三、使用 SDL_GetTicks64() 示例代码
#include <SDL.h>
#pragma comment(lib, "SDL2.lib")
#pragma comment(lib, "SDL2main.lib")
class Game
{
private:
SDL_Window* m_Window;
SDL_Renderer* m_Renderer;
SDL_Event m_SDLEvent;
bool m_BGameRunning;
// deltatime
float m_DeltaTime;
Uint64 m_StartTime;
Uint64 m_PreFrameTime;
void InitWindow()
{
m_Window = SDL_CreateWindow("HelloSDL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
m_Renderer = SDL_CreateRenderer(m_Window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
}
public:
Game()
:
m_BGameRunning(true),
m_DeltaTime(0)
{
InitWindow();
// deltaTime
m_StartTime = SDL_GetTicks64();
m_PreFrameTime = m_StartTime;
}
~Game()
{
SDL_DestroyRenderer(m_Renderer);
SDL_DestroyWindow(m_Window);
SDL_Quit();
}
void Run()
{
while (m_BGameRunning)
{
UpdateDeltaTime();
Update();
Render();
}
}
void Update()
{
UpdateSDLEvent();
}
void Render()
{
SDL_SetRenderDrawColor(m_Renderer, 0, 0, 0, 255);
SDL_RenderClear(m_Renderer);
// Draw items
SDL_RenderPresent(m_Renderer);
}
void UpdateDeltaTime()
{
/* Updates the deltaTime with the time it takes to update and render one frame. */
Uint64 currTime = SDL_GetTicks64();
m_DeltaTime = (currTime - m_PreFrameTime) / 1000.0f;
m_PreFrameTime = currTime;
float totalTime = (currTime - m_StartTime) / 1000.0f;
float FPS = 1.0f / m_DeltaTime;
SDL_LogInfo(0, "totalTime: %f\tdt: %f\tFPS: %f\n", totalTime, m_DeltaTime, FPS);
// 限制帧率
int MaxFPS = 60;
if (m_DeltaTime < 1.0f / MaxFPS * 1000.0f)
SDL_Delay(Uint32(floor(1.0 / MaxFPS * 1000.0 - m_DeltaTime)));
}
void UpdateSDLEvent()
{
while (SDL_PollEvent(&m_SDLEvent))
{
switch (m_SDLEvent.type)
{
case SDL_QUIT:
m_BGameRunning = false;
break;
}
}
}
};
int main(int argc, char* argv[])
{
Game game;
game.Run();
return 0;
}
四、最后
SDL_GetPerformanceCounter
精度要高一些,推荐使用
在控制帧率的时候,精度都不太高,还没想到好的办法同时兼顾到跨平台和高精度
1. 本卷演示
最新更新
还有很多优化空间,这里提供控制标题更改不太快的方法
void Game::UpdateDeltaTime() // 如果在窗口未激活状态,应该暂停,这里没处理事件...
{
/* Updates the deltaTime with the time it takes to update and render one frame. */
Uint64 currTime = SDL_GetPerformanceCounter();
m_DeltaTime = (currTime - m_PreFrameTime) / (float)SDL_GetPerformanceFrequency();
m_PreFrameTime = currTime;
// 限制最大帧率
int MaxFPS = 120;
if (m_DeltaTime < 1.0f / MaxFPS * 1000.0f)
SDL_Delay(Uint32(floor(1.0 / MaxFPS * 1000.0 - m_DeltaTime)));
// 游戏从开始到目前所消耗的时间
float totalTime = (currTime - m_StartTime) / (float)SDL_GetPerformanceFrequency();
static int frameCount = 0;
static float timeElapsed = 0.0f;
frameCount++;
// 每秒更改一次标题,避免更新太快
if ((totalTime - timeElapsed) > 1.0f)
{
float mspf = 1000.0f / frameCount;
std::ostringstream outs;
outs << "C++ SDL2 RPG" << " " <<
"FPS: " << frameCount << " " <<
"Frame Time: " << mspf << " (ms)";
// 更改窗口标题
SDL_SetWindowTitle(m_Window, outs.str().c_str());
frameCount = 0;
timeElapsed += 1.0f;
}
}
本节内容就到这里了,下卷会继续分享 SDL 的基本使用
The End.