2026-02-15 10:11:09 +08:00
|
|
|
|
# Extra2D 模块系统
|
|
|
|
|
|
|
|
|
|
|
|
## 概述
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
Extra2D 采用模块化架构设计(参考 Kiwano),所有核心功能通过模块系统和服务系统管理。系统提供:
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
- **统一的生命周期管理**:初始化、更新、渲染、关闭
|
|
|
|
|
|
- **优先级排序**:确保模块按正确顺序初始化
|
|
|
|
|
|
- **显式注册**:通过 `Application::use()` 注册模块
|
|
|
|
|
|
- **Context 模式**:使用上下文对象遍历模块链
|
2026-02-15 12:36:36 +08:00
|
|
|
|
- **依赖注入**:通过服务定位器解耦模块间依赖
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
|
|
|
|
|
## 架构图
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ Application │
|
2026-02-15 12:36:36 +08:00
|
|
|
|
│ (协调模块和服务,通过服务定位器获取依赖) │
|
2026-02-15 10:11:09 +08:00
|
|
|
|
└─────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
│
|
2026-02-15 12:36:36 +08:00
|
|
|
|
┌───────────────┴───────────────┐
|
|
|
|
|
|
▼ ▼
|
|
|
|
|
|
┌─────────────────────────────┐ ┌─────────────────────────────┐
|
2026-02-15 20:13:18 +08:00
|
|
|
|
│ Modules │ │ ServiceLocator │
|
|
|
|
|
|
│ (模块列表,按优先级排序) │ │ (服务定位器,管理运行时服务) │
|
2026-02-15 12:36:36 +08:00
|
|
|
|
└─────────────────────────────┘ └─────────────────────────────┘
|
|
|
|
|
|
│ │
|
|
|
|
|
|
┌─────┴─────┐ ┌───────┴───────┐
|
|
|
|
|
|
▼ ▼ ▼ ▼
|
|
|
|
|
|
┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐
|
|
|
|
|
|
│ Config │ │ Window │ │ Scene │ │ Timer │
|
|
|
|
|
|
│ Module │ │ Module │ │ Service │ │ Service │
|
|
|
|
|
|
└───────────┘ └───────────┘ └───────────┘ └───────────┘
|
2026-02-15 10:11:09 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
---
|
2026-02-15 13:32:42 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
## 模块基类
|
2026-02-15 13:32:42 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
### Module
|
2026-02-15 13:32:42 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
所有模块只需继承 `Module` 基类,实现需要的生命周期方法:
|
2026-02-15 13:32:42 +08:00
|
|
|
|
|
|
|
|
|
|
```cpp
|
2026-02-15 20:13:18 +08:00
|
|
|
|
class Module {
|
|
|
|
|
|
public:
|
|
|
|
|
|
virtual ~Module() = default;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 设置模块(初始化)
|
|
|
|
|
|
*/
|
|
|
|
|
|
virtual void setupModule() {}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 销毁模块
|
|
|
|
|
|
*/
|
|
|
|
|
|
virtual void destroyModule() {}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 更新时
|
|
|
|
|
|
*/
|
|
|
|
|
|
virtual void onUpdate(UpdateContext& ctx) { ctx.next(); }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 渲染前
|
|
|
|
|
|
*/
|
|
|
|
|
|
virtual void beforeRender(RenderContext& ctx) { ctx.next(); }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 渲染时
|
|
|
|
|
|
*/
|
|
|
|
|
|
virtual void onRender(RenderContext& ctx) { ctx.next(); }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 渲染后
|
|
|
|
|
|
*/
|
|
|
|
|
|
virtual void afterRender(RenderContext& ctx) { ctx.next(); }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 事件处理
|
|
|
|
|
|
*/
|
|
|
|
|
|
virtual void handleEvent(EventContext& ctx) { ctx.next(); }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 获取模块名称
|
|
|
|
|
|
*/
|
|
|
|
|
|
virtual const char* getName() const = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 获取模块优先级
|
|
|
|
|
|
*/
|
|
|
|
|
|
virtual int getPriority() const { return 0; }
|
2026-02-15 13:32:42 +08:00
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
### 模块上下文
|
|
|
|
|
|
|
|
|
|
|
|
用于遍历模块链,支持链式调用:
|
2026-02-15 13:32:42 +08:00
|
|
|
|
|
|
|
|
|
|
```cpp
|
2026-02-15 20:13:18 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 模块上下文基类
|
|
|
|
|
|
*/
|
|
|
|
|
|
class ModuleContext {
|
|
|
|
|
|
public:
|
|
|
|
|
|
void next(); // 遍历下一个模块
|
|
|
|
|
|
bool isDone() const; // 检查是否完成
|
2026-02-15 13:32:42 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 更新上下文
|
|
|
|
|
|
*/
|
|
|
|
|
|
class UpdateContext : public ModuleContext {
|
2026-02-15 13:32:42 +08:00
|
|
|
|
public:
|
2026-02-15 20:13:18 +08:00
|
|
|
|
float getDeltaTime() const; // 获取帧间隔时间
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 渲染上下文
|
|
|
|
|
|
*/
|
|
|
|
|
|
class RenderContext : public ModuleContext {
|
|
|
|
|
|
public:
|
|
|
|
|
|
enum class Phase { Before, On, After };
|
|
|
|
|
|
Phase getPhase() const;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 事件上下文
|
|
|
|
|
|
*/
|
|
|
|
|
|
class EventContext : public ModuleContext {
|
2026-02-15 13:32:42 +08:00
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-15 12:36:36 +08:00
|
|
|
|
## 模块 vs 服务
|
|
|
|
|
|
|
|
|
|
|
|
| 特性 | 模块 (Module) | 服务 (Service) |
|
|
|
|
|
|
|-----|--------------|---------------|
|
|
|
|
|
|
| 用途 | 平台级初始化 | 运行时功能 |
|
|
|
|
|
|
| 生命周期 | Application 管理 | ServiceLocator 管理 |
|
2026-02-15 20:13:18 +08:00
|
|
|
|
| 注册方式 | `app.use(module)` | `locator.registerService()` |
|
2026-02-15 12:36:36 +08:00
|
|
|
|
| 可替换性 | 编译时确定 | 运行时可替换 |
|
2026-02-15 13:32:42 +08:00
|
|
|
|
| 示例 | Window, Render, Input | Scene, Timer, Event, Camera |
|
2026-02-15 12:36:36 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-15 10:11:09 +08:00
|
|
|
|
## 模块优先级
|
|
|
|
|
|
|
|
|
|
|
|
模块按优先级从小到大初始化,关闭时逆序执行:
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
| 模块 | 优先级 | 说明 |
|
|
|
|
|
|
|------|--------|------|
|
|
|
|
|
|
| LoggerModule | -1 | 最先初始化 |
|
|
|
|
|
|
| ConfigModule | 0 | 配置加载 |
|
|
|
|
|
|
| PlatformModule | 10 | 平台初始化 |
|
|
|
|
|
|
| WindowModule | 20 | 窗口创建 |
|
|
|
|
|
|
| InputModule | 30 | 输入系统 |
|
|
|
|
|
|
| RenderModule | 40 | 渲染系统 |
|
|
|
|
|
|
| ScriptModule | 500 | 脚本系统 |
|
|
|
|
|
|
| 用户模块 | 1000+ | 用户自定义 |
|
2026-02-15 12:36:36 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
## 创建新模块
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
### 简单示例
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
|
|
|
|
|
```cpp
|
2026-02-15 20:13:18 +08:00
|
|
|
|
#include <extra2d/core/module.h>
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
class MyModule : public extra2d::Module {
|
2026-02-15 10:11:09 +08:00
|
|
|
|
public:
|
2026-02-15 20:13:18 +08:00
|
|
|
|
const char* getName() const override { return "MyModule"; }
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
int getPriority() const override { return 1000; }
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
void setupModule() override {
|
|
|
|
|
|
// 初始化资源
|
|
|
|
|
|
extra2d::E2D_LOG_INFO("MyModule initialized");
|
|
|
|
|
|
}
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
void destroyModule() override {
|
|
|
|
|
|
// 清理资源
|
|
|
|
|
|
extra2d::E2D_LOG_INFO("MyModule destroyed");
|
|
|
|
|
|
}
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
void onUpdate(extra2d::UpdateContext& ctx) override {
|
|
|
|
|
|
// 更新逻辑
|
|
|
|
|
|
ctx.next(); // 继续下一个模块
|
|
|
|
|
|
}
|
2026-02-15 12:36:36 +08:00
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
### 注册模块
|
2026-02-15 12:36:36 +08:00
|
|
|
|
|
|
|
|
|
|
```cpp
|
2026-02-15 20:13:18 +08:00
|
|
|
|
int main() {
|
|
|
|
|
|
auto& app = extra2d::Application::get();
|
|
|
|
|
|
|
|
|
|
|
|
MyModule myModule;
|
|
|
|
|
|
app.use(myModule); // 显式注册
|
|
|
|
|
|
|
|
|
|
|
|
app.init();
|
|
|
|
|
|
app.run();
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
2026-02-15 12:36:36 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
### 带配置的模块
|
2026-02-15 12:36:36 +08:00
|
|
|
|
|
|
|
|
|
|
```cpp
|
2026-02-15 13:32:42 +08:00
|
|
|
|
// my_module.h
|
|
|
|
|
|
#pragma once
|
2026-02-15 12:36:36 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
#include <extra2d/core/module.h>
|
|
|
|
|
|
#include <string>
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
struct MyModuleConfig {
|
|
|
|
|
|
std::string greeting = "Hello!";
|
|
|
|
|
|
int repeatCount = 1;
|
2026-02-15 10:11:09 +08:00
|
|
|
|
};
|
2026-02-15 13:32:42 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
class MyModule : public extra2d::Module {
|
2026-02-15 10:11:09 +08:00
|
|
|
|
public:
|
2026-02-15 20:13:18 +08:00
|
|
|
|
MyModule();
|
|
|
|
|
|
~MyModule() override;
|
2026-02-15 13:32:42 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
const char* getName() const override { return "MyModule"; }
|
|
|
|
|
|
int getPriority() const override { return 1000; }
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
void setupModule() override;
|
|
|
|
|
|
void destroyModule() override;
|
|
|
|
|
|
void onUpdate(extra2d::UpdateContext& ctx) override;
|
2026-02-15 13:32:42 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
void setConfig(const MyModuleConfig& config) { config_ = config; }
|
|
|
|
|
|
void sayHello() const;
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
|
|
|
|
|
private:
|
2026-02-15 20:13:18 +08:00
|
|
|
|
MyModuleConfig config_;
|
|
|
|
|
|
float time_ = 0.0f;
|
2026-02-15 10:11:09 +08:00
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
// my_module.cpp
|
2026-02-15 13:32:42 +08:00
|
|
|
|
#include "my_module.h"
|
|
|
|
|
|
#include <extra2d/utils/logger.h>
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
MyModule::MyModule() : Module() {}
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
MyModule::~MyModule() {
|
|
|
|
|
|
if (isInitialized()) {
|
|
|
|
|
|
destroyModule();
|
2026-02-15 13:32:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
void MyModule::setupModule() {
|
|
|
|
|
|
if (isInitialized()) return;
|
2026-02-15 13:32:42 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
setInitialized(true);
|
|
|
|
|
|
E2D_LOG_INFO("MyModule initialized");
|
|
|
|
|
|
E2D_LOG_INFO(" Greeting: {}", config_.greeting);
|
|
|
|
|
|
E2D_LOG_INFO(" Repeat Count: {}", config_.repeatCount);
|
2026-02-15 13:32:42 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
sayHello();
|
2026-02-15 13:32:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
void MyModule::destroyModule() {
|
|
|
|
|
|
if (!isInitialized()) return;
|
|
|
|
|
|
|
2026-02-15 13:32:42 +08:00
|
|
|
|
E2D_LOG_INFO("MyModule shutdown");
|
2026-02-15 20:13:18 +08:00
|
|
|
|
setInitialized(false);
|
2026-02-15 13:32:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
void MyModule::onUpdate(extra2d::UpdateContext& ctx) {
|
|
|
|
|
|
if (!isInitialized()) {
|
|
|
|
|
|
ctx.next();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
time_ += ctx.getDeltaTime();
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
if (time_ >= 5.0f) {
|
|
|
|
|
|
sayHello();
|
|
|
|
|
|
time_ = 0.0f;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ctx.next();
|
2026-02-15 10:11:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
void MyModule::sayHello() const {
|
|
|
|
|
|
for (int i = 0; i < config_.repeatCount; ++i) {
|
|
|
|
|
|
E2D_LOG_INFO("[MyModule] {}", config_.greeting);
|
|
|
|
|
|
}
|
2026-02-15 13:32:42 +08:00
|
|
|
|
}
|
2026-02-15 10:11:09 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-02-15 12:36:36 +08:00
|
|
|
|
---
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
|
|
|
|
|
## 内置模块
|
|
|
|
|
|
|
2026-02-15 13:32:42 +08:00
|
|
|
|
### Config 模块
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 13:32:42 +08:00
|
|
|
|
**职责**:管理 ConfigManager 和应用配置
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
**优先级**:0
|
|
|
|
|
|
|
2026-02-15 10:11:09 +08:00
|
|
|
|
```cpp
|
2026-02-15 20:13:18 +08:00
|
|
|
|
extra2d::AppConfig config;
|
2026-02-15 13:32:42 +08:00
|
|
|
|
config.appName = "My Application";
|
|
|
|
|
|
config.appVersion = "1.0.0";
|
2026-02-15 10:11:09 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
### Logger 模块
|
|
|
|
|
|
|
|
|
|
|
|
**职责**:日志系统初始化
|
|
|
|
|
|
|
|
|
|
|
|
**优先级**:-1(最先初始化)
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
extra2d::LoggerModule loggerModule;
|
|
|
|
|
|
loggerModule.setLogLevel(extra2d::LogLevel::Debug);
|
|
|
|
|
|
loggerModule.setFileOutput("app.log");
|
|
|
|
|
|
app.use(loggerModule);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-15 13:32:42 +08:00
|
|
|
|
### Platform 模块
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 13:32:42 +08:00
|
|
|
|
**职责**:平台检测和平台特定初始化
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
**优先级**:10
|
|
|
|
|
|
|
2026-02-15 13:32:42 +08:00
|
|
|
|
**支持平台**:
|
|
|
|
|
|
- Windows
|
|
|
|
|
|
- Linux
|
|
|
|
|
|
- macOS
|
|
|
|
|
|
- Nintendo Switch
|
|
|
|
|
|
|
2026-02-15 10:11:09 +08:00
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-15 13:32:42 +08:00
|
|
|
|
### Window 模块
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 13:32:42 +08:00
|
|
|
|
**职责**:窗口创建和管理
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
**优先级**:20
|
|
|
|
|
|
|
|
|
|
|
|
**后端**:统一使用 SDL2
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 13:32:42 +08:00
|
|
|
|
```cpp
|
2026-02-15 20:13:18 +08:00
|
|
|
|
extra2d::WindowModule windowModule;
|
|
|
|
|
|
extra2d::WindowConfigData windowConfig;
|
|
|
|
|
|
windowConfig.title = "My App";
|
|
|
|
|
|
windowConfig.width = 1280;
|
|
|
|
|
|
windowConfig.height = 720;
|
|
|
|
|
|
windowConfig.vsync = true;
|
|
|
|
|
|
windowModule.setWindowConfig(windowConfig);
|
|
|
|
|
|
app.use(windowModule);
|
2026-02-15 13:32:42 +08:00
|
|
|
|
```
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 13:32:42 +08:00
|
|
|
|
---
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 13:32:42 +08:00
|
|
|
|
### Input 模块
|
|
|
|
|
|
|
|
|
|
|
|
**职责**:输入设备管理(键盘、鼠标、手柄)
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
**优先级**:30
|
2026-02-15 13:32:42 +08:00
|
|
|
|
|
|
|
|
|
|
```cpp
|
2026-02-15 20:13:18 +08:00
|
|
|
|
extra2d::InputModule inputModule;
|
|
|
|
|
|
extra2d::InputConfigData inputConfig;
|
|
|
|
|
|
inputConfig.deadzone = 0.15f;
|
|
|
|
|
|
inputConfig.enableVibration = true;
|
|
|
|
|
|
inputModule.setInputConfig(inputConfig);
|
|
|
|
|
|
app.use(inputModule);
|
2026-02-15 10:11:09 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### Render 模块
|
|
|
|
|
|
|
|
|
|
|
|
**职责**:渲染器初始化和管理
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
**优先级**:40
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
```cpp
|
|
|
|
|
|
extra2d::RenderModule renderModule;
|
|
|
|
|
|
extra2d::RenderModuleConfig renderConfig;
|
|
|
|
|
|
renderConfig.vsync = true;
|
|
|
|
|
|
renderConfig.multisamples = 4;
|
|
|
|
|
|
renderModule.setRenderConfig(renderConfig);
|
|
|
|
|
|
app.use(renderModule);
|
2026-02-15 10:11:09 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-02-15 12:36:36 +08:00
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-15 13:32:42 +08:00
|
|
|
|
## 服务系统
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 13:32:42 +08:00
|
|
|
|
### IService
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
服务接口基类:
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 12:36:36 +08:00
|
|
|
|
```cpp
|
2026-02-15 13:32:42 +08:00
|
|
|
|
class IService {
|
|
|
|
|
|
public:
|
|
|
|
|
|
virtual ~IService() = default;
|
|
|
|
|
|
|
|
|
|
|
|
virtual ServiceInfo getServiceInfo() const = 0;
|
|
|
|
|
|
virtual bool initialize() = 0;
|
|
|
|
|
|
virtual void shutdown() = 0;
|
|
|
|
|
|
virtual void update(float deltaTime);
|
2026-02-15 12:36:36 +08:00
|
|
|
|
};
|
2026-02-15 13:32:42 +08:00
|
|
|
|
```
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
2026-02-15 13:32:42 +08:00
|
|
|
|
### 内置服务
|
|
|
|
|
|
|
|
|
|
|
|
| 服务 | 用途 | 优先级 |
|
|
|
|
|
|
|-----|------|-------|
|
|
|
|
|
|
| EventService | 事件分发 | 100 |
|
2026-02-15 20:13:18 +08:00
|
|
|
|
| TimerService | 计时器 | 200 |
|
|
|
|
|
|
| SceneService | 场景管理 | 300 |
|
2026-02-15 13:32:42 +08:00
|
|
|
|
| CameraService | 相机系统 | 400 |
|
|
|
|
|
|
|
|
|
|
|
|
### 使用服务
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
2026-02-15 20:13:18 +08:00
|
|
|
|
auto& app = extra2d::Application::get();
|
|
|
|
|
|
|
2026-02-15 13:32:42 +08:00
|
|
|
|
// 获取服务
|
2026-02-15 20:13:18 +08:00
|
|
|
|
auto sceneService = app.scenes();
|
|
|
|
|
|
auto timerService = app.timers();
|
|
|
|
|
|
auto eventService = app.events();
|
|
|
|
|
|
auto cameraService = app.camera();
|
2026-02-15 13:32:42 +08:00
|
|
|
|
|
|
|
|
|
|
// 使用场景服务
|
|
|
|
|
|
sceneService->pushScene(myScene);
|
|
|
|
|
|
|
|
|
|
|
|
// 使用计时器服务
|
|
|
|
|
|
timerService->addTimer(1.0f, []() {
|
|
|
|
|
|
E2D_LOG_INFO("Timer fired!");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 使用事件服务
|
2026-02-15 20:42:39 +08:00
|
|
|
|
eventService->addListener(extra2d::EventType::KeyPress, [](extra2d::Event& e) {
|
2026-02-15 20:13:18 +08:00
|
|
|
|
auto& keyEvent = std::get<extra2d::KeyEvent>(e.data);
|
2026-02-15 13:32:42 +08:00
|
|
|
|
E2D_LOG_INFO("Key pressed: {}", keyEvent.keyCode);
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 输入事件系统
|
|
|
|
|
|
|
|
|
|
|
|
### 事件类型
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
enum class EventType {
|
|
|
|
|
|
// 键盘
|
2026-02-15 20:42:39 +08:00
|
|
|
|
KeyPress,
|
|
|
|
|
|
KeyRelease,
|
2026-02-15 13:32:42 +08:00
|
|
|
|
KeyRepeat,
|
|
|
|
|
|
|
|
|
|
|
|
// 鼠标
|
2026-02-15 20:42:39 +08:00
|
|
|
|
MousePress,
|
|
|
|
|
|
MouseRelease,
|
|
|
|
|
|
MouseMove,
|
|
|
|
|
|
MouseScroll,
|
2026-02-15 13:32:42 +08:00
|
|
|
|
|
|
|
|
|
|
// 手柄
|
2026-02-15 20:42:39 +08:00
|
|
|
|
GamepadConnect,
|
|
|
|
|
|
GamepadDisconnect,
|
|
|
|
|
|
GamepadPress,
|
|
|
|
|
|
GamepadRelease,
|
2026-02-15 13:32:42 +08:00
|
|
|
|
|
|
|
|
|
|
// 触摸
|
2026-02-15 20:42:39 +08:00
|
|
|
|
TouchBegin,
|
|
|
|
|
|
TouchMove,
|
|
|
|
|
|
TouchEnd,
|
2026-02-15 13:32:42 +08:00
|
|
|
|
|
|
|
|
|
|
// 窗口
|
|
|
|
|
|
WindowResize,
|
|
|
|
|
|
WindowClose,
|
2026-02-15 12:36:36 +08:00
|
|
|
|
};
|
2026-02-15 10:11:09 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-02-15 13:32:42 +08:00
|
|
|
|
### 事件监听
|
2026-02-15 10:11:09 +08:00
|
|
|
|
|
|
|
|
|
|
```cpp
|
2026-02-15 20:13:18 +08:00
|
|
|
|
auto eventService = app.events();
|
2026-02-15 13:32:42 +08:00
|
|
|
|
|
|
|
|
|
|
// 监听键盘事件
|
2026-02-15 20:42:39 +08:00
|
|
|
|
eventService->addListener(EventType::KeyPress, [](Event& e) {
|
2026-02-15 13:32:42 +08:00
|
|
|
|
auto& key = std::get<KeyEvent>(e.data);
|
2026-02-15 20:48:09 +08:00
|
|
|
|
if (key.scancode == static_cast<int>(Key::Escape)) {
|
2026-02-15 20:13:18 +08:00
|
|
|
|
Application::get().quit();
|
|
|
|
|
|
}
|
2026-02-15 13:32:42 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 监听鼠标事件
|
2026-02-15 20:42:39 +08:00
|
|
|
|
eventService->addListener(EventType::MousePress, [](Event& e) {
|
|
|
|
|
|
auto& mouse = std::get<MouseEvent>(e.data);
|
2026-02-15 20:13:18 +08:00
|
|
|
|
E2D_LOG_INFO("Click at ({}, {})", mouse.position.x, mouse.position.y);
|
2026-02-15 13:32:42 +08:00
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-15 13:39:18 +08:00
|
|
|
|
## 场景图系统
|
|
|
|
|
|
|
|
|
|
|
|
### Node 基类
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
class Node : public std::enable_shared_from_this<Node> {
|
|
|
|
|
|
public:
|
|
|
|
|
|
// 层级管理
|
|
|
|
|
|
void addChild(Ptr<Node> child);
|
|
|
|
|
|
void removeChild(Ptr<Node> child);
|
|
|
|
|
|
Ptr<Node> getParent() const;
|
|
|
|
|
|
|
|
|
|
|
|
// 变换属性
|
|
|
|
|
|
void setPos(const Vec2& pos);
|
|
|
|
|
|
void setRotation(float degrees);
|
|
|
|
|
|
void setScale(const Vec2& scale);
|
|
|
|
|
|
|
|
|
|
|
|
// 世界变换
|
|
|
|
|
|
Vec2 toWorld(const Vec2& localPos) const;
|
|
|
|
|
|
glm::mat4 getWorldTransform() const;
|
|
|
|
|
|
|
|
|
|
|
|
// 生命周期回调
|
|
|
|
|
|
virtual void onEnter();
|
|
|
|
|
|
virtual void onExit();
|
|
|
|
|
|
virtual void onUpdate(float dt);
|
|
|
|
|
|
virtual void onRender(RenderBackend& renderer);
|
|
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### ShapeNode 形状节点
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
// 创建形状节点
|
2026-02-15 20:13:18 +08:00
|
|
|
|
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)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2026-02-15 13:39:18 +08:00
|
|
|
|
auto triangle = ShapeNode::createFilledTriangle(
|
|
|
|
|
|
Vec2(0, -40), Vec2(-35, 30), Vec2(35, 30),
|
|
|
|
|
|
Color(0.4f, 1.0f, 0.4f, 1.0f)
|
|
|
|
|
|
);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 变换继承
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
auto parent = makeShared<Node>();
|
|
|
|
|
|
parent->setPos(100, 100);
|
2026-02-15 20:13:18 +08:00
|
|
|
|
parent->setRotation(45);
|
2026-02-15 13:39:18 +08:00
|
|
|
|
|
|
|
|
|
|
auto child = makeShared<Node>();
|
2026-02-15 20:13:18 +08:00
|
|
|
|
child->setPos(50, 0); // 相对于父节点
|
2026-02-15 13:39:18 +08:00
|
|
|
|
parent->addChild(child);
|
|
|
|
|
|
|
|
|
|
|
|
// child 会随 parent 一起旋转
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 视口适配系统
|
|
|
|
|
|
|
|
|
|
|
|
### ViewportAdapter
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
enum class ViewportMode {
|
|
|
|
|
|
AspectRatio, // 保持宽高比,可能有黑边
|
|
|
|
|
|
Stretch, // 拉伸填满整个窗口
|
|
|
|
|
|
Center, // 居中显示,不缩放
|
|
|
|
|
|
Custom // 自定义缩放和偏移
|
|
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 使用 CameraService 配置视口
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
auto cameraService = app.camera();
|
|
|
|
|
|
if (cameraService) {
|
2026-02-15 20:13:18 +08:00
|
|
|
|
extra2d::ViewportConfig vpConfig;
|
|
|
|
|
|
vpConfig.logicWidth = 1280.0f;
|
|
|
|
|
|
vpConfig.logicHeight = 720.0f;
|
|
|
|
|
|
vpConfig.mode = extra2d::ViewportMode::AspectRatio;
|
2026-02-15 13:39:18 +08:00
|
|
|
|
|
|
|
|
|
|
cameraService->setViewportConfig(vpConfig);
|
|
|
|
|
|
cameraService->updateViewport(windowWidth, windowHeight);
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-15 10:11:09 +08:00
|
|
|
|
## 示例
|
|
|
|
|
|
|
|
|
|
|
|
完整示例请参考:
|
2026-02-15 20:13:18 +08:00
|
|
|
|
- [examples/hello_module/](../../examples/hello_module/) - 自定义模块示例
|
|
|
|
|
|
- [examples/basic/main.cpp](../../examples/basic/main.cpp) - 基础示例
|
2026-02-15 17:00:39 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
## 最佳实践
|
2026-02-15 17:00:39 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
### 1. 模块优先级
|
2026-02-15 17:00:39 +08:00
|
|
|
|
|
|
|
|
|
|
```cpp
|
2026-02-15 20:13:18 +08:00
|
|
|
|
// 核心模块使用低优先级
|
|
|
|
|
|
class LoggerModule : public Module {
|
|
|
|
|
|
int getPriority() const override { return -1; }
|
2026-02-15 17:00:39 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
// 用户模块使用高优先级
|
|
|
|
|
|
class MyModule : public Module {
|
|
|
|
|
|
int getPriority() const override { return 1000; }
|
2026-02-15 17:00:39 +08:00
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
### 2. 链式调用
|
2026-02-15 17:00:39 +08:00
|
|
|
|
|
|
|
|
|
|
```cpp
|
2026-02-15 20:13:18 +08:00
|
|
|
|
void onUpdate(UpdateContext& ctx) override {
|
|
|
|
|
|
// 执行更新逻辑
|
|
|
|
|
|
doSomething();
|
2026-02-15 17:00:39 +08:00
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
// 继续下一个模块
|
|
|
|
|
|
ctx.next();
|
2026-02-15 17:00:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-02-15 20:13:18 +08:00
|
|
|
|
### 3. 检查初始化状态
|
2026-02-15 17:00:39 +08:00
|
|
|
|
|
|
|
|
|
|
```cpp
|
2026-02-15 20:13:18 +08:00
|
|
|
|
void onUpdate(UpdateContext& ctx) override {
|
|
|
|
|
|
if (!isInitialized()) {
|
|
|
|
|
|
ctx.next();
|
|
|
|
|
|
return;
|
2026-02-15 17:00:39 +08:00
|
|
|
|
}
|
2026-02-15 20:13:18 +08:00
|
|
|
|
|
|
|
|
|
|
// 执行更新逻辑
|
|
|
|
|
|
ctx.next();
|
|
|
|
|
|
}
|
2026-02-15 17:00:39 +08:00
|
|
|
|
```
|