完成配置载入类Config

This commit is contained in:
Ziyu
2025-06-16 14:44:47 +08:00
parent ac3b6121b4
commit d3b9ad3f84
6 changed files with 261 additions and 7 deletions
+1
View File
@@ -33,6 +33,7 @@ add_executable(${TARGET}
src/main.cpp src/main.cpp
src/engine/core/game_app.cpp src/engine/core/game_app.cpp
src/engine/core/time.cpp src/engine/core/time.cpp
src/engine/core/config.cpp
src/engine/resource/resource_manager.cpp src/engine/resource/resource_manager.cpp
src/engine/resource/texture_manager.cpp src/engine/resource/texture_manager.cpp
src/engine/resource/audio_manager.cpp src/engine/resource/audio_manager.cpp
+48
View File
@@ -0,0 +1,48 @@
{
"window": {
"title": "SunnyLand",
"width": 1280,
"height": 720,
"resizable": true
},
"graphics": {
"vsync": true
},
"performance": {
"target_fps": 144
},
"audio": {
"music_volume": 0.5,
"sound_volume": 0.5
},
"input_mappings": {
"pause": [
"P",
"Escape"
],
"move_down": [
"S",
"Down"
],
"jump": [
"J",
"Space"
],
"move_up": [
"W",
"Up"
],
"move_right": [
"D",
"Right"
],
"attack": [
"K",
"MouseLeft"
],
"move_left": [
"A",
"Left"
]
}
}
+119
View File
@@ -0,0 +1,119 @@
#include "config.h"
#include <fstream>
#include <nlohmann/json.hpp>
#include "spdlog/spdlog.h"
namespace engine::core {
Config::Config(const std::string &filepath)
{
loadFromFile(filepath);
}
bool Config::loadFromFile(const std::string& filepath) {
std::ifstream file(filepath);
if (!file.is_open()) {
spdlog::warn("配置文件 '{}' 未找到。使用默认设置并创建默认配置文件。", filepath);
if (!saveToFile(filepath)) {
spdlog::error("无法创建默认配置文件 '{}'。", filepath);
return false;
}
return false; // 文件不存在,使用默认值
}
try {
nlohmann::json j;
file >> j;
fromJson(j);
spdlog::info("成功从 '{}' 加载配置。", filepath);
return true;
} catch (const std::exception& e) {
spdlog::error("读取配置文件 '{}' 时出错:{}。使用默认设置。", filepath, e.what());
}
return false;
}
bool Config::saveToFile(const std::string& filepath) {
std::ofstream file(filepath);
if (!file.is_open()) {
spdlog::error("无法打开配置文件 '{}' 进行写入。", filepath);
return false;
}
try {
nlohmann::ordered_json j = toJson();
file << j.dump(4);
spdlog::info("成功将配置保存到 '{}'。", filepath);
return true;
} catch (const std::exception& e) {
spdlog::error("写入配置文件 '{}' 时出错:{}", filepath, e.what());
}
return false;
}
void Config::fromJson(const nlohmann::json& j) {
if (j.contains("window")) {
const auto& window_config = j["window"];
window_title_ = window_config.value("title", window_title_);
window_width_ = window_config.value("width", window_width_);
window_height_ = window_config.value("height", window_height_);
window_resizable_ = window_config.value("resizable", window_resizable_);
}
if (j.contains("graphics")) {
const auto& graphics_config = j["graphics"];
vsync_enabled_ = graphics_config.value("vsync", vsync_enabled_);
}
if (j.contains("performance")) {
const auto& perf_config = j["performance"];
target_fps_ = perf_config.value("target_fps", target_fps_);
if (target_fps_ < 0) {
spdlog::warn("目标 FPS 不能为负数。设置为 0(无限制)。");
target_fps_ = 0;
}
}
if (j.contains("audio")) {
const auto& audio_config = j["audio"];
music_volume_ = audio_config.value("music_volume", music_volume_);
sound_volume_ = audio_config.value("sound_volume", sound_volume_);
}
// 从 JSON 加载 input_mappings
if (j.contains("input_mappings") && j["input_mappings"].is_object()) {
const auto& mappings_json = j["input_mappings"];
try {
// 直接尝试从 JSON 对象转换为 map<string, vector<string>>
auto input_mappings = mappings_json.get<std::unordered_map<std::string, std::vector<std::string>>>();
// 如果成功,则将 input_mappings 移动到 input_mappings_
input_mappings_ = std::move(input_mappings);
spdlog::trace("成功从配置加载输入映射。");
} catch (const std::exception& e) {
spdlog::warn("配置加载警告:解析 'input_mappings' 时发生异常。使用默认映射。错误:{}", e.what());
}
} else {
spdlog::trace("配置跟踪:未找到 'input_mappings' 部分或不是对象。使用头文件中定义的默认映射。");
}
}
nlohmann::ordered_json Config::toJson() const {
return nlohmann::ordered_json{
{"window", {
{"title", window_title_},
{"width", window_width_},
{"height", window_height_},
{"resizable", window_resizable_}
}},
{"graphics", {
{"vsync", vsync_enabled_}
}},
{"performance", {
{"target_fps", target_fps_}
}},
{"audio", {
{"music_volume", music_volume_},
{"sound_volume", sound_volume_}
}},
{"input_mappings", input_mappings_}
};
}
} // namespace engine::core
+63
View File
@@ -0,0 +1,63 @@
#pragma once
#include <string>
#include <vector>
#include <unordered_map>
#include <nlohmann/json_fwd.hpp> // nlohmann_json 提供的前向声明
namespace engine::core {
/**
* @brief 管理应用程序的配置设置。
*
* 提供配置项的默认值,并支持从 JSON 文件加载/保存配置。
* 如果加载失败或文件不存在,将使用默认值。
*/
class Config final {
public:
// --- 默认配置值 --- (为了方便拓展,全部设置为公有)
// 窗口设置
std::string window_title_ = "SunnyLand";
int window_width_ = 1280;
int window_height_ = 720;
bool window_resizable_ = true;
// 图形设置
bool vsync_enabled_ = true; ///< @brief 是否启用垂直同步
// 性能设置
int target_fps_ = 144; ///< @brief 目标 FPS 设置,0 表示不限制
// 音频设置
float music_volume_ = 0.5f;
float sound_volume_ = 0.5f;
// 存储动作名称到 SDL Scancode 名称列表的映射
std::unordered_map<std::string, std::vector<std::string>> input_mappings_ = {
// 提供一些合理的默认值,以防配置文件加载失败或缺少此部分
{"move_left", {"A", "Left"}},
{"move_right", {"D", "Right"}},
{"move_up", {"W", "Up"}},
{"move_down", {"S", "Down"}},
{"jump", {"J", "Space"}},
{"attack", {"K", "MouseLeft"}},
{"pause", {"P", "Escape"}},
// 可以继续添加更多默认动作
};
explicit Config(const std::string& filepath); ///< @brief 构造函数,指定配置文件路径。
// 删除拷贝和移动语义
Config(const Config&) = delete;
Config& operator=(const Config&) = delete;
Config(Config&&) = delete;
Config& operator=(Config&&) = delete;
bool loadFromFile(const std::string& filepath); ///< @brief 从指定的 JSON 文件加载配置。成功返回 true,否则返回 false。
[[nodiscard]] bool saveToFile(const std::string& filepath); ///< @brief 将当前配置保存到指定的 JSON 文件。成功返回 true,否则返回 false。
private:
void fromJson(const nlohmann::json& j); ///< @brief 从 JSON 对象反序列化配置。
nlohmann::ordered_json toJson() const; ///< @brief 将当前配置转换为 JSON 对象(按顺序)。
};
} // namespace engine::core
+27 -6
View File
@@ -3,6 +3,7 @@
#include "../resource/resource_manager.h" #include "../resource/resource_manager.h"
#include "../render/renderer.h" #include "../render/renderer.h"
#include "../render/camera.h" #include "../render/camera.h"
#include "config.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
@@ -22,7 +23,7 @@ void GameApp::run() {
spdlog::error("初始化失败,无法运行游戏。"); spdlog::error("初始化失败,无法运行游戏。");
return; return;
} }
time_->setTargetFps(144); // 设置目标帧率(临时,未来会从配置文件读取)
while (is_running_) { while (is_running_) {
time_->update(); time_->update();
float delta_time = time_->getDeltaTime(); float delta_time = time_->getDeltaTime();
@@ -39,6 +40,7 @@ void GameApp::run() {
bool GameApp::init() { bool GameApp::init() {
spdlog::trace("初始化 GameApp ..."); spdlog::trace("初始化 GameApp ...");
if (!initConfig()) return false;
if (!initSDL()) return false; if (!initSDL()) return false;
if (!initTime()) return false; if (!initTime()) return false;
if (!initResourceManager()) return false; if (!initResourceManager()) return false;
@@ -96,13 +98,26 @@ void GameApp::close() {
is_running_ = false; is_running_ = false;
} }
bool GameApp::initSDL() { bool GameApp::initConfig()
{
try {
config_ = std::make_unique<engine::core::Config>("assets/config.json");
} catch (const std::exception& e) {
spdlog::error("初始化配置失败: {}", e.what());
return false;
}
spdlog::trace("配置初始化成功。");
return true;
}
bool GameApp::initSDL()
{
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) { if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
spdlog::error("SDL 初始化失败! SDL错误: {}", SDL_GetError()); spdlog::error("SDL 初始化失败! SDL错误: {}", SDL_GetError());
return false; return false;
} }
window_ = SDL_CreateWindow("SunnyLand", 1280, 720, SDL_WINDOW_RESIZABLE); window_ = SDL_CreateWindow(config_->window_title_.c_str(), config_->window_width_, config_->window_height_, SDL_WINDOW_RESIZABLE);
if (window_ == nullptr) { if (window_ == nullptr) {
spdlog::error("无法创建窗口! SDL错误: {}", SDL_GetError()); spdlog::error("无法创建窗口! SDL错误: {}", SDL_GetError());
return false; return false;
@@ -114,8 +129,13 @@ bool GameApp::initSDL() {
return false; return false;
} }
// 设置逻辑分辨率 // 设置 VSync (注意: VSync 开启时,驱动程序会尝试将帧率限制到显示器刷新率,有可能会覆盖我们手动设置的 target_fps)
SDL_SetRenderLogicalPresentation(sdl_renderer_, 640, 360, SDL_LOGICAL_PRESENTATION_LETTERBOX); int vsync_mode = config_->vsync_enabled_ ? SDL_RENDERER_VSYNC_ADAPTIVE : SDL_RENDERER_VSYNC_DISABLED;
SDL_SetRenderVSync(sdl_renderer_, vsync_mode);
spdlog::trace("VSync 设置为: {}", config_->vsync_enabled_ ? "Enabled" : "Disabled");
// 设置逻辑分辨率为窗口大小的一半(针对像素游戏)
SDL_SetRenderLogicalPresentation(sdl_renderer_, config_->window_width_ / 2, config_->window_height_ / 2, SDL_LOGICAL_PRESENTATION_LETTERBOX);
spdlog::trace("SDL 初始化成功。"); spdlog::trace("SDL 初始化成功。");
return true; return true;
} }
@@ -127,6 +147,7 @@ bool GameApp::initTime() {
spdlog::error("初始化时间管理失败: {}", e.what()); spdlog::error("初始化时间管理失败: {}", e.what());
return false; return false;
} }
time_->setTargetFps(config_->target_fps_);
spdlog::trace("时间管理初始化成功。"); spdlog::trace("时间管理初始化成功。");
return true; return true;
} }
@@ -155,7 +176,7 @@ bool GameApp::initRenderer() {
bool GameApp::initCamera() { bool GameApp::initCamera() {
try { try {
camera_ = std::make_unique<engine::render::Camera>(glm::vec2(640, 360)); camera_ = std::make_unique<engine::render::Camera>(glm::vec2(config_->window_width_ / 2, config_->window_height_ / 2));
} catch (const std::exception& e) { } catch (const std::exception& e) {
spdlog::error("初始化相机失败: {}", e.what()); spdlog::error("初始化相机失败: {}", e.what());
return false; return false;
+3 -1
View File
@@ -16,6 +16,7 @@ class Camera;
namespace engine::core { // 命名空间的最佳实践:与文件路径一致 namespace engine::core { // 命名空间的最佳实践:与文件路径一致
class Time; class Time;
class Config;
/** /**
* @brief 主游戏应用程序类,初始化SDL,管理游戏循环。 * @brief 主游戏应用程序类,初始化SDL,管理游戏循环。
@@ -31,7 +32,7 @@ private:
std::unique_ptr<engine::resource::ResourceManager> resource_manager_; std::unique_ptr<engine::resource::ResourceManager> resource_manager_;
std::unique_ptr<engine::render::Renderer> renderer_; std::unique_ptr<engine::render::Renderer> renderer_;
std::unique_ptr<engine::render::Camera> camera_; std::unique_ptr<engine::render::Camera> camera_;
std::unique_ptr<engine::core::Config> config_;
public: public:
GameApp(); GameApp();
~GameApp(); ~GameApp();
@@ -55,6 +56,7 @@ private:
void close(); void close();
// 各模块的初始化/创建函数,在init()中调用 // 各模块的初始化/创建函数,在init()中调用
[[nodiscard]] bool initConfig();
[[nodiscard]] bool initSDL(); [[nodiscard]] bool initSDL();
[[nodiscard]] bool initTime(); [[nodiscard]] bool initTime();
[[nodiscard]] bool initResourceManager(); [[nodiscard]] bool initResourceManager();