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

View File

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

View File

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

View File

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

View File

@ -74,6 +74,23 @@ bool GLFWWindow::create(const WindowConfigData& cfg) {
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_);
// 初始化 GLAD

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,11 @@
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() {
if (initialized_) {

View File

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

View File

@ -1,11 +1,12 @@
#include "hello_module.h"
#include <extra2d/app/application.h>
#include <extra2d/platform/window_module.h>
#include <extra2d/graphics/core/render_module.h>
#include <extra2d/platform/window_module.h>
#include <extra2d/scene/scene.h>
#include <extra2d/services/scene_service.h>
#include <iostream>
using namespace extra2d;
class HelloScene : public Scene {
@ -17,7 +18,7 @@ public:
std::cout << "HelloScene entered" << std::endl;
setBackgroundColor(Color(0.1f, 0.1f, 0.2f, 1.0f));
auto* hello = Application::get().get<HelloModule>();
auto *hello = Application::get().get<HelloModule>();
if (hello) {
std::cout << "Scene calling HelloModule from onEnter..." << std::endl;
hello->sayHello();
@ -29,7 +30,7 @@ public:
time_ += dt;
if (time_ >= 5.0f) {
auto* hello = Application::get().get<HelloModule>();
auto *hello = Application::get().get<HelloModule>();
if (hello) {
std::cout << "Scene calling HelloModule from onUpdate..." << std::endl;
hello->sayHello();
@ -47,15 +48,23 @@ int main(int argc, char *argv[]) {
(void)argv;
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;
Application &app = Application::get();
// 注册模块
app.use<WindowModule>(WindowModule::Cfg{.w = 800, .h = 600});
app.use<RenderModule>();
app.use<HelloModule>(HelloModule::Cfg{.greeting = "Hello from custom module!", .repeatCount = 3});
app.use<WindowModule>([](auto &cfg) {
cfg.w = 800;
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()) {
std::cerr << "Failed to initialize application" << std::endl;