完成帧率控制
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
#include "game_app.h"
|
||||
#include "time.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
namespace engine::core {
|
||||
|
||||
GameApp::GameApp() = default;
|
||||
GameApp::GameApp() {
|
||||
time_ = std::make_unique<Time>();
|
||||
}
|
||||
|
||||
GameApp::~GameApp() {
|
||||
if (is_running_) {
|
||||
@@ -18,12 +21,16 @@ void GameApp::run() {
|
||||
spdlog::error("初始化失败,无法运行游戏。");
|
||||
return;
|
||||
}
|
||||
|
||||
time_->setTargetFps(144); // 设置目标帧率(临时,未来会从配置文件读取)
|
||||
while (is_running_) {
|
||||
float delta_time = 0.01f; // 每帧的时间间隔(临时设定)
|
||||
time_->update();
|
||||
float delta_time = time_->getDeltaTime();
|
||||
|
||||
handleEvents();
|
||||
update(delta_time);
|
||||
render();
|
||||
|
||||
// spdlog::info("delta_time: {}", delta_time);
|
||||
}
|
||||
|
||||
close();
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
|
||||
// 前向声明, 减少头文件的依赖,增加编译速度
|
||||
struct SDL_Window;
|
||||
struct SDL_Renderer;
|
||||
|
||||
namespace engine::core {
|
||||
namespace engine::core { // 命名空间的最佳实践:与文件路径一致
|
||||
class Time;
|
||||
|
||||
/**
|
||||
* @brief 主游戏应用程序类,初始化SDL,管理游戏循环。
|
||||
*/
|
||||
class GameApp final {
|
||||
class GameApp final { // final 表示该类不能被继承
|
||||
private:
|
||||
SDL_Window* window_ = nullptr;
|
||||
SDL_Renderer* sdl_renderer_ = nullptr;
|
||||
bool is_running_ = false;
|
||||
|
||||
// 引擎组件
|
||||
std::unique_ptr<engine::core::Time> time_;
|
||||
|
||||
public:
|
||||
GameApp();
|
||||
~GameApp();
|
||||
@@ -31,7 +36,7 @@ public:
|
||||
GameApp& operator=(GameApp&&) = delete;
|
||||
|
||||
private:
|
||||
[[nodiscard]] bool init();
|
||||
[[nodiscard]] bool init(); // nodiscard 表示该函数返回值不应该被忽略
|
||||
void handleEvents();
|
||||
void update(float delta_time);
|
||||
void render();
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#include "time.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <SDL3/SDL_timer.h> // 用于 SDL_GetTicksNS()
|
||||
|
||||
namespace engine::core {
|
||||
|
||||
Time::Time() {
|
||||
// 初始化 last_time_ 和 frame_start_time_ 为当前时间,避免第一帧 DeltaTime 过大
|
||||
last_time_ = SDL_GetTicksNS();
|
||||
frame_start_time_ = last_time_;
|
||||
spdlog::trace("Time 初始化。Last time: {}", last_time_);
|
||||
}
|
||||
|
||||
void Time::update() {
|
||||
|
||||
frame_start_time_ = SDL_GetTicksNS(); // 记录进入 update 时的时间戳
|
||||
auto current_delta_time = static_cast<double>(frame_start_time_ - last_time_) / 1000000000.0;
|
||||
if (target_frame_time_ > 0.0){ // 如果设置了目标帧率,则限制帧率;否则delta_time_ = current_delta_time
|
||||
limitFrameRate(current_delta_time);
|
||||
} else {
|
||||
delta_time_ = current_delta_time;
|
||||
}
|
||||
|
||||
last_time_ = SDL_GetTicksNS(); // 记录离开 update 时的时间戳
|
||||
}
|
||||
|
||||
void Time::limitFrameRate(float current_delta_time) {
|
||||
// 如果当前帧耗费的时间小于目标帧时间,则等待剩余时间
|
||||
if (current_delta_time < target_frame_time_) {
|
||||
double time_to_wait = target_frame_time_ - current_delta_time;
|
||||
Uint64 ns_to_wait = static_cast<Uint64>(time_to_wait * 1000000000.0);
|
||||
SDL_DelayNS(ns_to_wait);
|
||||
delta_time_ = static_cast<double>(SDL_GetTicksNS() - last_time_) / 1000000000.0;
|
||||
}
|
||||
}
|
||||
|
||||
float Time::getDeltaTime() const {
|
||||
return delta_time_ * time_scale_;
|
||||
}
|
||||
|
||||
float Time::getUnscaledDeltaTime() const {
|
||||
return delta_time_;
|
||||
}
|
||||
|
||||
void Time::setTimeScale(float scale) {
|
||||
if (scale < 0.0) {
|
||||
spdlog::warn("Time scale 不能为负。Clamping to 0.");
|
||||
scale = 0.0; // 防止负时间缩放
|
||||
}
|
||||
time_scale_ = scale;
|
||||
}
|
||||
|
||||
float Time::getTimeScale() const {
|
||||
return time_scale_;
|
||||
}
|
||||
|
||||
void Time::setTargetFps(int fps) {
|
||||
if (fps < 0) {
|
||||
spdlog::warn("Target FPS 不能为负。Setting to 0 (unlimited).");
|
||||
target_fps_ = 0;
|
||||
} else {
|
||||
target_fps_ = fps;
|
||||
}
|
||||
|
||||
if (target_fps_ > 0) {
|
||||
target_frame_time_ = 1.0 / static_cast<double>(target_fps_);
|
||||
spdlog::info("Target FPS 设置为: {} (Frame time: {:.6f}s)", target_fps_, target_frame_time_);
|
||||
} else {
|
||||
target_frame_time_ = 0.0;
|
||||
spdlog::info("Target FPS 设置为: Unlimited");
|
||||
}
|
||||
}
|
||||
|
||||
int Time::getTargetFps() const {
|
||||
return target_fps_;
|
||||
}
|
||||
|
||||
} // namespace engine::core
|
||||
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
#include <SDL3/SDL_stdinc.h> // 用于 Uint64
|
||||
|
||||
namespace engine::core {
|
||||
|
||||
/**
|
||||
* @brief 管理游戏循环中的时间,计算帧间时间差 (DeltaTime)。
|
||||
*
|
||||
* 使用 SDL 的高精度性能计数器来确保时间测量的准确性。
|
||||
* 提供获取缩放和未缩放 DeltaTime 的方法,以及设置时间缩放因子的能力。
|
||||
*/
|
||||
class Time final{
|
||||
private:
|
||||
Uint64 last_time_ = 0; ///< @brief 上一帧的时间戳 (用于计算 delta)
|
||||
Uint64 frame_start_time_ = 0; ///< @brief 当前帧开始的时间戳 (用于帧率限制)
|
||||
double delta_time_ = 0.0; ///< @brief 未缩放的帧间时间差 (秒)
|
||||
double time_scale_ = 1.0; ///< @brief 时间缩放因子
|
||||
|
||||
// 帧率限制相关
|
||||
int target_fps_ = 0; ///< @brief 目标 FPS (0 表示不限制)
|
||||
double target_frame_time_ = 0.0; ///< @brief 目标每帧时间 (秒)
|
||||
|
||||
public:
|
||||
Time();
|
||||
|
||||
// 简单起见,直接删除拷贝、移动和赋值运算符
|
||||
Time(const Time&) = delete;
|
||||
Time& operator=(const Time&) = delete;
|
||||
Time(Time&&) = delete;
|
||||
Time& operator=(Time&&) = delete;
|
||||
|
||||
/**
|
||||
* @brief 每帧开始时调用,更新内部时间状态并计算 DeltaTime。
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* @brief 获取经过时间缩放调整后的帧间时间差 (DeltaTime)。
|
||||
*
|
||||
* @return double 缩放后的 DeltaTime (秒)。
|
||||
*/
|
||||
float getDeltaTime() const;
|
||||
|
||||
/**
|
||||
* @brief 获取未经过时间缩放的原始帧间时间差。
|
||||
*
|
||||
* @return double 未缩放的 DeltaTime (秒)。
|
||||
*/
|
||||
float getUnscaledDeltaTime() const;
|
||||
|
||||
/**
|
||||
* @brief 设置时间缩放因子。
|
||||
*
|
||||
* @param scale 时间缩放值。1.0 为正常速度,< 1.0 为慢动作,> 1.0 为快进。
|
||||
* 不允许负值。
|
||||
*/
|
||||
void setTimeScale(float scale);
|
||||
|
||||
/**
|
||||
* @brief 获取当前的时间缩放因子。
|
||||
*
|
||||
* @return float 当前的时间缩放因子。
|
||||
*/
|
||||
float getTimeScale() const;
|
||||
|
||||
/**
|
||||
* @brief 设置目标帧率。
|
||||
*
|
||||
* @param fps 目标每秒帧数。设置为 0 表示不限制帧率。负值将被视为 0。
|
||||
*/
|
||||
void setTargetFps(int fps);
|
||||
|
||||
/**
|
||||
* @brief 获取当前设置的目标帧率。
|
||||
*
|
||||
* @return int 目标 FPS,0 表示不限制。
|
||||
*/
|
||||
int getTargetFps() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief update 中调用,用于限制帧率。如果设置了 target_fps_ > 0,且当前帧执行时间小于目标帧时间,则会调用 SDL_DelayNS() 来等待剩余时间。
|
||||
*
|
||||
* @param current_delta_time 当前帧的执行时间(秒)
|
||||
*/
|
||||
void limitFrameRate(float current_delta_time);
|
||||
};
|
||||
|
||||
} // namespace engine::core
|
||||
Reference in New Issue
Block a user