478 lines
17 KiB
Markdown
478 lines
17 KiB
Markdown
# Extra2D 模块系统
|
||
|
||
## 概述
|
||
|
||
Extra2D 采用模块化架构设计,核心功能通过**模块系统**和**服务系统**管理:
|
||
|
||
- **模块 (Module)**: 平台级初始化,管理硬件资源(窗口、渲染、输入)
|
||
- **服务 (Service)**: 运行时功能,通过服务定位器管理(场景、计时器、事件、相机)
|
||
|
||
## 架构图
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Application │
|
||
│ (协调模块和服务,通过服务定位器获取依赖) │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
┌───────────────┴───────────────┐
|
||
▼ ▼
|
||
┌─────────────────────────────┐ ┌─────────────────────────────┐
|
||
│ 模块系统 │ │ 服务系统 │
|
||
│ (Module - 硬件资源) │ │ (Service - 运行时功能) │
|
||
├─────────────────────────────┤ ├─────────────────────────────┤
|
||
│ ┌─────────┐ ┌─────────┐ │ │ ┌─────────┐ ┌─────────┐ │
|
||
│ │ Window │ │ Render │ │ │ │ Logger │ │ Scene │ │
|
||
│ │ Module │ │ Module │ │ │ │ Service │ │ Service │ │
|
||
│ │ P0 │ │ P10 │ │ │ │ │ │ │ │
|
||
│ └─────────┘ └─────────┘ │ │ └─────────┘ └─────────┘ │
|
||
│ ┌─────────┐ │ │ ┌─────────┐ ┌─────────┐ │
|
||
│ │ Input │ │ │ │ Timer │ │ Event │ │
|
||
│ │ Module │ │ │ │ Service │ │ Service │ │
|
||
│ │ P20 │ │ │ │ │ │ │ │
|
||
│ └─────────┘ │ │ └─────────┘ └─────────┘ │
|
||
│ │ │ ┌─────────┐ │
|
||
│ │ │ │ Camera │ │
|
||
│ │ │ │ Service │ │
|
||
│ │ │ │ │ │
|
||
│ │ │ └─────────┘ │
|
||
└─────────────────────────────┘ └─────────────────────────────┘
|
||
```
|
||
|
||
## 模块 vs 服务
|
||
|
||
| 特性 | 模块 (Module) | 服务 (Service) |
|
||
|-----|--------------|---------------|
|
||
| 用途 | 平台级初始化 | 运行时功能 |
|
||
| 生命周期 | Application 管理 | ServiceLocator 管理 |
|
||
| 依赖方式 | 通过 `deps()` 方法声明 | 通过 `ServiceLocator` 获取 |
|
||
| 可替换性 | 编译时确定 | 运行时可替换 |
|
||
| 示例 | Window, Render, Input | Scene, Timer, Event, Camera |
|
||
|
||
## 模块系统
|
||
|
||
### 模块基类
|
||
|
||
所有模块继承自 `Module` 基类:
|
||
|
||
```cpp
|
||
class Module {
|
||
public:
|
||
virtual ~Module() = default;
|
||
virtual bool init() = 0; // 初始化
|
||
virtual void shutdown() = 0; // 关闭
|
||
virtual bool ok() const = 0; // 检查状态
|
||
virtual const char* name() const = 0; // 模块名称
|
||
virtual int priority() const { return 100; } // 优先级(越小越优先)
|
||
virtual std::vector<std::type_index> deps() const { return {}; } // 依赖
|
||
void setApp(Application* app) { app_ = app; }
|
||
Application* app() const { return app_; }
|
||
protected:
|
||
Application* app_ = nullptr;
|
||
};
|
||
```
|
||
|
||
### 内置模块
|
||
|
||
| 模块 | 优先级 | 依赖 | 职责 |
|
||
|-----|-------|------|------|
|
||
| WindowModule | 0 | 无 | 窗口创建和管理 |
|
||
| RenderModule | 10 | WindowModule | 渲染后端初始化 |
|
||
| InputModule | 20 | WindowModule | 输入设备管理 |
|
||
|
||
### 模块配置
|
||
|
||
每个模块有自己的配置结构:
|
||
|
||
```cpp
|
||
// Window 模块配置
|
||
struct WindowCfg {
|
||
std::string title = "Extra2D";
|
||
int w = 1280;
|
||
int h = 720;
|
||
WindowMode mode = WindowMode::Windowed;
|
||
bool vsync = true;
|
||
int priority = 0;
|
||
std::string backend = "sdl2"; // "sdl2" 或 "glfw"
|
||
};
|
||
|
||
// Render 模块配置
|
||
struct RenderCfg {
|
||
BackendType backend = BackendType::OpenGL;
|
||
int targetFPS = 60;
|
||
bool vsync = true;
|
||
int multisamples = 0;
|
||
int priority = 10;
|
||
};
|
||
|
||
// Input 模块配置
|
||
struct InputCfg {
|
||
float deadzone = 0.15f;
|
||
float mouseSensitivity = 1.0f;
|
||
bool enableVibration = true;
|
||
int maxGamepads = 4;
|
||
int priority = 20;
|
||
};
|
||
```
|
||
|
||
### 使用模块
|
||
|
||
```cpp
|
||
Application app;
|
||
|
||
// 配置 Window 模块
|
||
app.use<WindowModule>([](auto& cfg) {
|
||
cfg.title = "My Game";
|
||
cfg.w = 1920;
|
||
cfg.h = 1080;
|
||
cfg.backend = "glfw"; // 使用 GLFW 后端
|
||
});
|
||
|
||
// 配置 Render 模块
|
||
app.use<RenderModule>([](auto& cfg) {
|
||
cfg.backend = BackendType::OpenGL;
|
||
cfg.vsync = true;
|
||
cfg.targetFPS = 60;
|
||
});
|
||
|
||
// 配置 Input 模块
|
||
app.use<InputModule>([](auto& cfg) {
|
||
cfg.deadzone = 0.2f;
|
||
cfg.enableVibration = true;
|
||
});
|
||
|
||
app.run();
|
||
```
|
||
|
||
### 构建时选择后端
|
||
|
||
```bash
|
||
# 选择窗口后端
|
||
xmake f --window_backend=sdl2 -y # 使用 SDL2(默认)
|
||
xmake f --window_backend=glfw -y # 使用 GLFW
|
||
|
||
# 选择渲染后端
|
||
xmake f --render_backend=opengl -y # 使用 OpenGL(默认)
|
||
xmake f --render_backend=vulkan -y # 使用 Vulkan
|
||
```
|
||
|
||
## 服务系统
|
||
|
||
### 服务接口基类
|
||
|
||
```cpp
|
||
class IService {
|
||
public:
|
||
virtual ~IService() = default;
|
||
virtual ServiceInfo getServiceInfo() const = 0;
|
||
virtual bool initialize() = 0;
|
||
virtual void shutdown() = 0;
|
||
virtual void update(float deltaTime) {}
|
||
virtual bool isInitialized() const = 0;
|
||
};
|
||
```
|
||
|
||
### 内置服务
|
||
|
||
| 服务 | 用途 | 自动注册宏 |
|
||
|-----|------|-----------|
|
||
| LoggerService | 日志记录 | `E2D_AUTO_REGISTER_SERVICE(ILogger, ConsoleLogger)` |
|
||
| SceneService | 场景管理 | `E2D_AUTO_REGISTER_SERVICE(ISceneService, SceneService)` |
|
||
| TimerService | 计时器 | `E2D_AUTO_REGISTER_SERVICE(ITimerService, TimerService)` |
|
||
| EventService | 事件分发 | `E2D_AUTO_REGISTER_SERVICE(IEventService, EventService)` |
|
||
| CameraService | 相机系统 | `E2D_AUTO_REGISTER_SERVICE(ICameraService, CameraService)` |
|
||
|
||
### 使用服务
|
||
|
||
```cpp
|
||
// 获取服务
|
||
auto logger = app.logger();
|
||
auto sceneService = app.scenes();
|
||
auto timerService = app.timers();
|
||
auto eventService = app.events();
|
||
auto cameraService = app.camera();
|
||
|
||
// 使用日志服务
|
||
logger->info("Application started");
|
||
logger->setLevel(LogLevel::Debug);
|
||
E2D_LOG_INFO("Player position: {}, {}", x, y);
|
||
E2D_LOG_ERROR("Failed to load texture: {}", filepath);
|
||
|
||
// 使用场景服务
|
||
sceneService->pushScene(myScene);
|
||
|
||
// 使用计时器服务
|
||
timerService->addTimer(1.0f, []() {
|
||
E2D_LOG_INFO("Timer fired!");
|
||
});
|
||
|
||
// 使用事件服务
|
||
eventService->addListener(EventType::KeyPressed, [](Event& e) {
|
||
auto& keyEvent = std::get<KeyEvent>(e.data);
|
||
E2D_LOG_INFO("Key pressed: {}", keyEvent.keyCode);
|
||
});
|
||
|
||
// 使用相机服务
|
||
cameraService->setPosition(100, 100);
|
||
cameraService->setZoom(2.0f);
|
||
```
|
||
|
||
## 渲染后端
|
||
|
||
### 支持的后端
|
||
|
||
| 后端 | 状态 | 平台支持 |
|
||
|-----|------|---------|
|
||
| OpenGL | 完整实现 | Windows, Linux, macOS, Switch |
|
||
| Vulkan | 计划中 | Windows, Linux, macOS, Switch |
|
||
|
||
### 渲染后端接口
|
||
|
||
```cpp
|
||
class Renderer {
|
||
public:
|
||
virtual ~Renderer() = default;
|
||
|
||
// 生命周期
|
||
virtual bool init(IWindow* window) = 0;
|
||
virtual void shutdown() = 0;
|
||
|
||
// 帧管理
|
||
virtual void beginFrame(const Color &clearColor) = 0;
|
||
virtual void endFrame() = 0;
|
||
virtual void setViewport(int x, int y, int width, int height) = 0;
|
||
virtual void setVSync(bool enabled) = 0;
|
||
|
||
// 状态设置
|
||
virtual void setBlendMode(BlendMode mode) = 0;
|
||
virtual void setViewProjection(const glm::mat4 &matrix) = 0;
|
||
|
||
// 变换矩阵栈
|
||
virtual void pushTransform(const glm::mat4 &transform) = 0;
|
||
virtual void popTransform() = 0;
|
||
virtual glm::mat4 getCurrentTransform() const = 0;
|
||
|
||
// 纹理
|
||
virtual Ptr<Texture> createTexture(int width, int height, const uint8_t *pixels, int channels) = 0;
|
||
virtual Ptr<Texture> loadTexture(const std::string &filepath) = 0;
|
||
|
||
// 精灵批处理
|
||
virtual void beginSpriteBatch() = 0;
|
||
virtual void drawSprite(const Texture &texture, const Rect &destRect, const Rect &srcRect,
|
||
const Color &tint, float rotation, const Vec2 &anchor) = 0;
|
||
virtual void endSpriteBatch() = 0;
|
||
virtual void flush() = 0;
|
||
|
||
// 形状渲染
|
||
virtual void drawLine(const Vec2 &start, const Vec2 &end, const Color &color, float width = 1.0f) = 0;
|
||
virtual void drawRect(const Rect &rect, const Color &color, float width = 1.0f) = 0;
|
||
virtual void fillRect(const Rect &rect, const Color &color) = 0;
|
||
virtual void drawCircle(const Vec2 ¢er, float radius, const Color &color,
|
||
int segments = 32, float width = 1.0f) = 0;
|
||
virtual void fillCircle(const Vec2 ¢er, float radius, const Color &color, int segments = 32) = 0;
|
||
|
||
// 文字渲染
|
||
virtual Ptr<FontAtlas> createFontAtlas(const std::string &filepath, int fontSize, bool useSDF = false) = 0;
|
||
virtual void drawText(const FontAtlas &font, const std::string &text, const Vec2 &position, const Color &color) = 0;
|
||
|
||
// 工厂方法
|
||
static UniquePtr<Renderer> create(BackendType type);
|
||
};
|
||
```
|
||
|
||
## 资源抽象层
|
||
|
||
渲染后端通过资源抽象层实现跨平台兼容:
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 前端 (Frontend) │
|
||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
||
│ │ Renderer │ │ SpriteBatch │ │ 其他 │ │
|
||
│ └──────┬──────┘ └──────┬──────┘ └─────────────────────┘ │
|
||
└─────────┼────────────────┼──────────────────────────────────┘
|
||
│ │
|
||
▼ ▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 资源抽象层 (resources/) │
|
||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||
│ │ Buffer │ │ Pipeline │ │ Framebuf │ │ Shader │ │
|
||
│ │ (缓冲区) │ │ (管线) │ │(帧缓冲) │ │ (着色器) │ │
|
||
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
||
│ ┌──────────┐ ┌──────────┐ │
|
||
│ │ Texture │ │ FontAtlas│ │
|
||
│ │ (纹理) │ │(字体图集)│ │
|
||
│ └──────────┘ └──────────┘ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 后端实现 (backends/) │
|
||
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||
│ │ OpenGL │ │ Vulkan │ │
|
||
│ │ GLBuffer │ │ VKBuffer │ │
|
||
│ │ GLPipeline │ │ VKPipeline │ │
|
||
│ │ GLFramebuffer │ │ VKFramebuffer │ │
|
||
│ │ GLShader │ │ VKShader │ │
|
||
│ │ GLTexture │ │ VKTexture │ │
|
||
│ └─────────────────┘ └─────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## 输入事件系统
|
||
|
||
### 事件类型
|
||
|
||
```cpp
|
||
enum class EventType {
|
||
// 键盘
|
||
KeyPressed,
|
||
KeyReleased,
|
||
KeyRepeat,
|
||
|
||
// 鼠标
|
||
MouseButtonPressed,
|
||
MouseButtonReleased,
|
||
MouseMoved,
|
||
MouseScrolled,
|
||
|
||
// 手柄
|
||
GamepadConnected,
|
||
GamepadDisconnected,
|
||
GamepadButtonPressed,
|
||
GamepadButtonReleased,
|
||
|
||
// 触摸
|
||
TouchBegan,
|
||
TouchMoved,
|
||
TouchEnded,
|
||
|
||
// 窗口
|
||
WindowResize,
|
||
WindowClose,
|
||
};
|
||
```
|
||
|
||
### 事件监听
|
||
|
||
```cpp
|
||
auto eventService = app.events();
|
||
|
||
// 监听键盘事件
|
||
eventService->addListener(EventType::KeyPressed, [](Event& e) {
|
||
auto& key = std::get<KeyEvent>(e.data);
|
||
E2D_LOG_INFO("Key: {}, mods: {}", key.keyCode, key.mods);
|
||
});
|
||
|
||
// 监听鼠标事件
|
||
eventService->addListener(EventType::MouseButtonPressed, [](Event& e) {
|
||
auto& mouse = std::get<MouseButtonEvent>(e.data);
|
||
E2D_LOG_INFO("Mouse button: {} at ({}, {})",
|
||
mouse.button, mouse.position.x, mouse.position.y);
|
||
});
|
||
```
|
||
|
||
## 场景图系统
|
||
|
||
### Node 基类
|
||
|
||
```cpp
|
||
class Node : public std::enable_shared_from_this<Node> {
|
||
public:
|
||
// 层级管理
|
||
void addChild(Ptr<Node> child);
|
||
void removeChild(Ptr<Node> child);
|
||
void detach();
|
||
void clearChildren();
|
||
|
||
Ptr<Node> getParent() const;
|
||
const std::vector<Ptr<Node>>& getChildren() const;
|
||
|
||
// 变换属性
|
||
void setPos(const Vec2& pos);
|
||
void setRotation(float degrees);
|
||
void setScale(const Vec2& scale);
|
||
void setAnchor(const Vec2& anchor);
|
||
void setOpacity(float opacity);
|
||
void setVisible(bool visible);
|
||
void setZOrder(int zOrder);
|
||
|
||
// 世界变换
|
||
Vec2 toWorld(const Vec2& localPos) const;
|
||
Vec2 toLocal(const Vec2& worldPos) const;
|
||
glm::mat4 getLocalTransform() const;
|
||
glm::mat4 getWorldTransform() const;
|
||
|
||
// 生命周期回调
|
||
virtual void onEnter();
|
||
virtual void onExit();
|
||
virtual void onUpdate(float dt);
|
||
virtual void onRender(Renderer& renderer);
|
||
};
|
||
```
|
||
|
||
### 使用场景
|
||
|
||
```cpp
|
||
auto scene = Scene::create();
|
||
|
||
// 创建形状节点
|
||
auto rect = ShapeNode::createFilledRect(Rect(0, 0, 100, 100), Color(1.0f, 0.4f, 0.4f, 1.0f));
|
||
auto circle = ShapeNode::createFilledCircle(Vec2(0, 0), 50, Color(0.4f, 0.4f, 1.0f, 1.0f));
|
||
|
||
// 添加到场景
|
||
scene->addChild(rect);
|
||
scene->addChild(circle);
|
||
|
||
// 运行场景
|
||
app.scenes()->runWithScene(scene);
|
||
```
|
||
|
||
## 视口适配系统
|
||
|
||
### 视口适配模式
|
||
|
||
```cpp
|
||
enum class ViewportMode {
|
||
AspectRatio, // 保持宽高比,可能有黑边
|
||
Stretch, // 拉伸填满整个窗口
|
||
Center, // 居中显示,不缩放
|
||
Custom // 自定义缩放和偏移
|
||
};
|
||
```
|
||
|
||
### 配置视口
|
||
|
||
```cpp
|
||
auto cameraService = app.camera();
|
||
if (cameraService) {
|
||
ViewportConfig vpConfig;
|
||
vpConfig.logicWidth = 1280.0f;
|
||
vpConfig.logicHeight = 720.0f;
|
||
vpConfig.mode = ViewportMode::AspectRatio;
|
||
vpConfig.letterboxColor = Color(0.0f, 0.0f, 0.0f, 1.0f);
|
||
|
||
cameraService->setViewportConfig(vpConfig);
|
||
cameraService->updateViewport(windowWidth, windowHeight);
|
||
cameraService->applyViewportAdapter();
|
||
}
|
||
```
|
||
|
||
## 平台支持
|
||
|
||
| 平台 | 窗口后端 | 图形 API |
|
||
|-----|---------|---------|
|
||
| Windows | SDL2 / GLFW | OpenGL / Vulkan |
|
||
| Linux | SDL2 / GLFW | OpenGL / Vulkan |
|
||
| macOS | SDL2 / GLFW | OpenGL / Vulkan |
|
||
| Nintendo Switch | SDL2 / GLFW | OpenGL / Vulkan |
|
||
|
||
## 示例
|
||
|
||
完整示例请参考:
|
||
- [examples/basic/main.cpp](../../examples/basic/main.cpp) - 基础示例
|
||
- [examples/demo_shapes/main.cpp](../../examples/demo_shapes/main.cpp) - 形状绘制
|
||
- [examples/demo_sprite/main.cpp](../../examples/demo_sprite/main.cpp) - 精灵渲染
|
||
- [examples/demo_text/main.cpp](../../examples/demo_text/main.cpp) - 文字渲染
|
||
- [examples/demo_framebuffer/main.cpp](../../examples/demo_framebuffer/main.cpp) - 帧缓冲
|