refactor(模块系统): 重构模块配置方式为lambda函数

将InputModule、WindowModule、RenderModule等核心模块的配置方式从结构体改为lambda函数
添加独立的配置结构体(InputCfg/WindowCfg/RenderCfg)并更新文档
实现GLFW窗口居中功能
更新示例代码使用新的配置方式
This commit is contained in:
ChestnutYueyue 2026-02-17 00:34:14 +08:00
parent 4b1de5e36a
commit 4f02ad0e39
13 changed files with 228 additions and 167 deletions

View File

@ -3,27 +3,22 @@
#include <extra2d/core/module.h> #include <extra2d/core/module.h>
#include <extra2d/graphics/core/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <extra2d/platform/window_module.h> #include <extra2d/platform/window_module.h>
#include <functional>
#include <typeindex> #include <typeindex>
namespace extra2d { namespace extra2d {
/** /**
* @brief * @brief
*
*/ */
class RenderModule : public Module { struct RenderCfg {
public:
/**
* @brief
*/
struct Cfg {
BackendType backend; BackendType backend;
int targetFPS; int targetFPS;
bool vsync; bool vsync;
int multisamples; int multisamples;
int priority; int priority;
Cfg() RenderCfg()
: backend(BackendType::OpenGL) : backend(BackendType::OpenGL)
, targetFPS(60) , targetFPS(60)
, vsync(true) , vsync(true)
@ -33,10 +28,16 @@ public:
}; };
/** /**
* @brief * @brief
* @param cfg *
*/ */
explicit RenderModule(const Cfg& cfg = Cfg{}); class RenderModule : public Module {
public:
/**
* @brief Lambda
* @param configFn
*/
explicit RenderModule(std::function<void(RenderCfg&)> configFn);
/** /**
* @brief * @brief
@ -64,7 +65,7 @@ public:
RenderBackend* renderer() const { return renderer_.get(); } RenderBackend* renderer() const { return renderer_.get(); }
private: private:
Cfg cfg_; RenderCfg cfg_;
UniquePtr<RenderBackend> renderer_; UniquePtr<RenderBackend> renderer_;
bool initialized_ = false; bool initialized_ = false;
}; };

View File

@ -3,27 +3,22 @@
#include <extra2d/core/module.h> #include <extra2d/core/module.h>
#include <extra2d/platform/iinput.h> #include <extra2d/platform/iinput.h>
#include <extra2d/platform/window_module.h> #include <extra2d/platform/window_module.h>
#include <functional>
#include <typeindex> #include <typeindex>
namespace extra2d { namespace extra2d {
/** /**
* @brief * @brief
*
*/ */
class InputModule : public Module { struct InputCfg {
public:
/**
* @brief
*/
struct Cfg {
float deadzone; float deadzone;
float mouseSensitivity; float mouseSensitivity;
bool enableVibration; bool enableVibration;
int maxGamepads; int maxGamepads;
int priority; int priority;
Cfg() InputCfg()
: deadzone(0.15f) : deadzone(0.15f)
, mouseSensitivity(1.0f) , mouseSensitivity(1.0f)
, enableVibration(true) , enableVibration(true)
@ -33,10 +28,16 @@ public:
}; };
/** /**
* @brief * @brief
* @param cfg *
*/ */
explicit InputModule(const Cfg& cfg = Cfg{}); class InputModule : public Module {
public:
/**
* @brief Lambda
* @param configFn
*/
explicit InputModule(std::function<void(InputCfg&)> configFn);
/** /**
* @brief * @brief
@ -69,7 +70,7 @@ public:
void update(); void update();
private: private:
Cfg cfg_; InputCfg cfg_;
IInput* input_ = nullptr; IInput* input_ = nullptr;
bool initialized_ = false; bool initialized_ = false;
}; };

View File

@ -1,21 +1,17 @@
#pragma once #pragma once
#include <extra2d/core/module.h> #include <extra2d/core/module.h>
#include <extra2d/platform/window_config.h>
#include <extra2d/platform/iwindow.h> #include <extra2d/platform/iwindow.h>
#include <extra2d/platform/window_config.h>
#include <functional>
#include <string>
namespace extra2d { namespace extra2d {
/** /**
* @brief * @brief
*
*/ */
class WindowModule : public Module { struct WindowCfg {
public:
/**
* @brief
*/
struct Cfg {
std::string title; std::string title;
int w; int w;
int h; int h;
@ -24,21 +20,22 @@ public:
int priority; int priority;
std::string backend; std::string backend;
Cfg() WindowCfg()
: title("Extra2D") : title("Extra2D"), w(1280), h(720), mode(WindowMode::Windowed),
, w(1280) vsync(true), priority(0), backend("sdl2") {}
, h(720)
, mode(WindowMode::Windowed)
, vsync(true)
, priority(0)
, backend("sdl2") {}
}; };
/** /**
* @brief * @brief
* @param cfg *
*/ */
explicit WindowModule(const Cfg& cfg = Cfg()); class WindowModule : public Module {
public:
/**
* @brief Lambda
* @param configFn
*/
explicit WindowModule(std::function<void(WindowCfg &)> configFn);
/** /**
* @brief * @brief
@ -58,7 +55,7 @@ public:
IWindow *win() const { return win_.get(); } IWindow *win() const { return win_.get(); }
private: private:
Cfg cfg_; WindowCfg cfg_;
UniquePtr<IWindow> win_; UniquePtr<IWindow> win_;
bool initialized_ = false; bool initialized_ = false;
bool sdlInited_ = false; bool sdlInited_ = false;

View File

@ -11,7 +11,9 @@
namespace extra2d { namespace extra2d {
RenderModule::RenderModule(const Cfg& cfg) : cfg_(cfg) {} RenderModule::RenderModule(std::function<void(RenderCfg&)> configFn) {
configFn(cfg_);
}
RenderModule::~RenderModule() { RenderModule::~RenderModule() {
if (initialized_) { if (initialized_) {

View File

@ -74,6 +74,23 @@ bool GLFWWindow::create(const WindowConfigData& cfg) {
return false; return false;
} }
// 窗口居中(非全屏模式下)
#ifndef __SWITCH__
if (!fullscreen_ && !monitor) {
GLFWmonitor* primaryMonitor = glfwGetPrimaryMonitor();
if (primaryMonitor) {
const GLFWvidmode* mode = glfwGetVideoMode(primaryMonitor);
if (mode) {
int screenWidth = mode->width;
int screenHeight = mode->height;
int windowX = (screenWidth - cfg.width) / 2;
int windowY = (screenHeight - cfg.height) / 2;
glfwSetWindowPos(glfwWindow_, windowX, windowY);
}
}
}
#endif
glfwMakeContextCurrent(glfwWindow_); glfwMakeContextCurrent(glfwWindow_);
// 初始化 GLAD // 初始化 GLAD

View File

@ -6,7 +6,9 @@
namespace extra2d { namespace extra2d {
InputModule::InputModule(const Cfg& cfg) : cfg_(cfg) {} InputModule::InputModule(std::function<void(InputCfg&)> configFn) {
configFn(cfg_);
}
InputModule::~InputModule() { InputModule::~InputModule() {
if (initialized_) { if (initialized_) {

View File

@ -16,7 +16,9 @@ void initSDL2Backend();
void initGLFWBackend(); void initGLFWBackend();
#endif #endif
WindowModule::WindowModule(const Cfg& cfg) : cfg_(cfg) {} WindowModule::WindowModule(std::function<void(WindowCfg&)> configFn) {
configFn(cfg_);
}
WindowModule::~WindowModule() { WindowModule::~WindowModule() {
if (initialized_) { if (initialized_) {

View File

@ -462,13 +462,14 @@ if (caps.supportsGamepad) {
**配置** **配置**
```cpp ```cpp
WindowModule::Cfg cfg; app.use<WindowModule>([](auto& cfg) {
cfg.title = "My App"; cfg.title = "My App";
cfg.w = 1280; cfg.w = 1280;
cfg.h = 720; cfg.h = 720;
cfg.mode = WindowMode::Windowed; cfg.mode = WindowMode::Windowed;
cfg.vsync = true; cfg.vsync = true;
cfg.backend = "sdl2"; // 可选:"sdl2" 或 "glfw" cfg.backend = "sdl2"; // 可选:"sdl2" 或 "glfw"
});
``` ```
**构建时选择后端** **构建时选择后端**
@ -491,10 +492,11 @@ xmake f --window_backend=glfw -y
**配置** **配置**
```cpp ```cpp
InputModule::Cfg cfg; app.use<InputModule>([](auto& cfg) {
cfg.deadzone = 0.15f; cfg.deadzone = 0.15f;
cfg.mouseSensitivity = 1.0f; cfg.mouseSensitivity = 1.0f;
cfg.enableVibration = true; cfg.enableVibration = true;
});
``` ```
**使用示例** **使用示例**
@ -528,11 +530,12 @@ if (input->gamepad()) {
**配置** **配置**
```cpp ```cpp
RenderModule::Cfg cfg; app.use<RenderModule>([](auto& cfg) {
cfg.backend = BackendType::OpenGL; cfg.backend = BackendType::OpenGL;
cfg.vsync = true; cfg.vsync = true;
cfg.targetFPS = 60; cfg.targetFPS = 60;
cfg.multisamples = 4; cfg.multisamples = 4;
});
``` ```
--- ---
@ -723,11 +726,8 @@ if (caps.supportsTouch) { /* 支持触摸 */ }
### 1. 模块配置独立化 ### 1. 模块配置独立化
```cpp ```cpp
// 好的做法:模块管理自己的配置 // 好的做法:模块管理自己的配置,使用 Lambda 配置
class WindowModule : public Module { struct WindowCfg {
Cfg cfg_; // 模块内部配置
public:
struct Cfg {
std::string title = "Extra2D"; std::string title = "Extra2D";
int w = 1280; int w = 1280;
int h = 720; int h = 720;
@ -735,8 +735,22 @@ public:
bool vsync = true; bool vsync = true;
int priority = 0; int priority = 0;
}; };
class WindowModule : public Module {
WindowCfg cfg_; // 模块内部配置
public:
explicit WindowModule(std::function<void(WindowCfg&)> configFn) {
configFn(cfg_);
}
}; };
// 使用 Lambda 配置模块
app.use<WindowModule>([](auto& cfg) {
cfg.w = 1920;
cfg.h = 1080;
cfg.backend = "glfw";
});
// 不好的做法:所有配置放在 AppConfig // 不好的做法:所有配置放在 AppConfig
struct AppConfig { struct AppConfig {
WindowConfig window; // 耦合度高 WindowConfig window; // 耦合度高

View File

@ -12,7 +12,6 @@
#include <extra2d/services/event_service.h> #include <extra2d/services/event_service.h>
#include <iostream> #include <iostream>
using namespace extra2d; using namespace extra2d;
void createSceneGraph(Scene *scene) { void createSceneGraph(Scene *scene) {
@ -103,20 +102,16 @@ int main(int argc, char *argv[]) {
Application &app = Application::get(); Application &app = Application::get();
// 注册模块(按优先级顺序) // 注册模块(按优先级顺序)
WindowModule::Cfg winCfg; app.use<WindowModule>([](auto &cfg) {
winCfg.w = 1280; cfg.w = 1280;
winCfg.h = 720; cfg.h = 720;
winCfg.priority = 0; cfg.priority = 0;
winCfg.backend = "glfw"; cfg.backend = "glfw";
app.use<WindowModule>(winCfg); });
RenderModule::Cfg renderCfg; app.use<RenderModule>([](auto &cfg) { cfg.priority = 10; });
renderCfg.priority = 10;
app.use<RenderModule>(renderCfg);
InputModule::Cfg inputCfg; app.use<InputModule>([](auto &cfg) { cfg.priority = 20; });
inputCfg.priority = 20;
app.use<InputModule>(inputCfg);
std::cout << "Initializing application..." << std::endl; std::cout << "Initializing application..." << std::endl;
if (!app.init()) { if (!app.init()) {

View File

@ -3,7 +3,11 @@
namespace extra2d { namespace extra2d {
HelloModule::HelloModule(const Cfg& cfg) : cfg_(cfg) {} HelloModule::HelloModule(const HelloCfg& cfg) : cfg_(cfg) {}
HelloModule::HelloModule(std::function<void(HelloCfg&)> configFn) {
configFn(cfg_);
}
HelloModule::~HelloModule() { HelloModule::~HelloModule() {
if (initialized_) { if (initialized_) {

View File

@ -2,9 +2,24 @@
#include <extra2d/core/module.h> #include <extra2d/core/module.h>
#include <string> #include <string>
#include <functional>
namespace extra2d { namespace extra2d {
/**
* @brief Hello模块配置结构
*/
struct HelloCfg {
std::string greeting;
int repeatCount;
int priority;
HelloCfg()
: greeting("Hello, Extra2D!")
, repeatCount(1)
, priority(100) {}
};
/** /**
* @brief Hello模块示例 * @brief Hello模块示例
* *
@ -12,19 +27,21 @@ namespace extra2d {
class HelloModule : public Module { class HelloModule : public Module {
public: public:
/** /**
* @brief * @brief
*/ */
struct Cfg { using Cfg = HelloCfg;
std::string greeting = "Hello, Extra2D!";
int repeatCount = 1;
int priority = 100;
};
/** /**
* @brief * @brief
* @param cfg * @param cfg
*/ */
explicit HelloModule(const Cfg& cfg = Cfg{}); explicit HelloModule(const HelloCfg& cfg = HelloCfg{});
/**
* @brief Lambda
* @param configFn
*/
explicit HelloModule(std::function<void(HelloCfg&)> configFn);
/** /**
* @brief * @brief
@ -43,7 +60,7 @@ public:
void sayHello() const; void sayHello() const;
private: private:
Cfg cfg_; HelloCfg cfg_;
bool initialized_ = false; bool initialized_ = false;
}; };

View File

@ -1,11 +1,12 @@
#include "hello_module.h" #include "hello_module.h"
#include <extra2d/app/application.h> #include <extra2d/app/application.h>
#include <extra2d/platform/window_module.h>
#include <extra2d/graphics/core/render_module.h> #include <extra2d/graphics/core/render_module.h>
#include <extra2d/platform/window_module.h>
#include <extra2d/scene/scene.h> #include <extra2d/scene/scene.h>
#include <extra2d/services/scene_service.h> #include <extra2d/services/scene_service.h>
#include <iostream> #include <iostream>
using namespace extra2d; using namespace extra2d;
class HelloScene : public Scene { class HelloScene : public Scene {
@ -47,15 +48,23 @@ int main(int argc, char *argv[]) {
(void)argv; (void)argv;
std::cout << "=== Hello Module Example ===" << std::endl; std::cout << "=== Hello Module Example ===" << std::endl;
std::cout << "This example demonstrates how to create a custom module" << std::endl; std::cout << "This example demonstrates how to create a custom module"
<< std::endl;
std::cout << "" << std::endl; std::cout << "" << std::endl;
Application &app = Application::get(); Application &app = Application::get();
// 注册模块 // 注册模块
app.use<WindowModule>(WindowModule::Cfg{.w = 800, .h = 600}); app.use<WindowModule>([](auto &cfg) {
app.use<RenderModule>(); cfg.w = 800;
app.use<HelloModule>(HelloModule::Cfg{.greeting = "Hello from custom module!", .repeatCount = 3}); cfg.h = 600;
cfg.backend = "glfw";
});
app.use<RenderModule>([](auto &cfg) { cfg.priority = 10; });
app.use<HelloModule>([](auto &cfg) {
cfg.greeting = "Hello from custom module!";
cfg.repeatCount = 3;
});
if (!app.init()) { if (!app.init()) {
std::cerr << "Failed to initialize application" << std::endl; std::cerr << "Failed to initialize application" << std::endl;