From 583e866861713bb99820e3b90c45e8dfaf198ad6 Mon Sep 17 00:00:00 2001 From: ChestnutYueyue <952134128@qq.com> Date: Tue, 17 Feb 2026 23:57:50 +0800 Subject: [PATCH] =?UTF-8?q?docs(module=5Fsystem):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E7=B3=BB=E7=BB=9F=E6=96=87=E6=A1=A3=EF=BC=8C?= =?UTF-8?q?=E7=AE=80=E5=8C=96=E5=86=85=E5=AE=B9=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除冗余的配置系统实现细节,聚焦核心架构 - 优化模块与服务的对比表格,突出关键差异 - 简化示例代码,保留核心用法展示 - 更新架构图,更清晰展示模块与服务的层级关系 - 统一术语使用,增强文档一致性 --- docs/module_system.md | 1267 ++++++++--------------------------------- 1 file changed, 252 insertions(+), 1015 deletions(-) diff --git a/docs/module_system.md b/docs/module_system.md index 31a3e4d..ef8b3e6 100644 --- a/docs/module_system.md +++ b/docs/module_system.md @@ -2,614 +2,201 @@ ## 概述 -Extra2D 采用模块化架构设计,所有核心功能通过模块系统和服务系统管理。系统提供: +Extra2D 采用模块化架构设计,核心功能通过**模块系统**和**服务系统**管理: -- **统一的生命周期管理**:初始化、关闭、依赖处理 -- **优先级排序**:确保模块/服务按正确顺序初始化 -- **模块化配置**:每个模块独立管理自己的配置 -- **依赖注入**:通过服务定位器解耦模块间依赖 -- **可扩展性**:新增模块无需修改引擎核心代码 +- **模块 (Module)**: 平台级初始化,管理硬件资源(窗口、渲染、输入) +- **服务 (Service)**: 运行时功能,通过服务定位器管理(场景、计时器、事件、相机) ## 架构图 ``` ┌─────────────────────────────────────────────────────────────┐ │ Application │ -│ (协调模块和服务,通过服务定位器获取依赖) │ +│ (协调模块和服务,通过服务定位器获取依赖) │ └─────────────────────────────────────────────────────────────┘ │ ┌───────────────┴───────────────┐ ▼ ▼ ┌─────────────────────────────┐ ┌─────────────────────────────┐ -│ ModuleRegistry │ │ ServiceLocator │ -│ (模块注册表,管理平台级模块) │ │ (服务定位器,管理运行时服务) │ +│ 模块系统 │ │ 服务系统 │ +│ (Module - 硬件资源) │ │ (Service - 运行时功能) │ +├─────────────────────────────┤ ├─────────────────────────────┤ +│ ┌─────────┐ ┌─────────┐ │ │ ┌─────────┐ ┌─────────┐ │ +│ │ Window │ │ Render │ │ │ │ Logger │ │ Scene │ │ +│ │ Module │ │ Module │ │ │ │ Service │ │ Service │ │ +│ │ P0 │ │ P10 │ │ │ │ │ │ │ │ +│ └─────────┘ └─────────┘ │ │ └─────────┘ └─────────┘ │ +│ ┌─────────┐ │ │ ┌─────────┐ ┌─────────┐ │ +│ │ Input │ │ │ │ Timer │ │ Event │ │ +│ │ Module │ │ │ │ Service │ │ Service │ │ +│ │ P20 │ │ │ │ │ │ │ │ +│ └─────────┘ │ │ └─────────┘ └─────────┘ │ +│ │ │ ┌─────────┐ │ +│ │ │ │ Camera │ │ +│ │ │ │ Service │ │ +│ │ │ │ │ │ +│ │ │ └─────────┘ │ └─────────────────────────────┘ └─────────────────────────────┘ - │ │ - ┌─────┴─────┐ ┌───────┴───────┐ - ▼ ▼ ▼ ▼ -┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ -│ Config │ │ Window │ │ Scene │ │ Timer │ -│ Module │ │ Module │ │ Service │ │ Service │ -└───────────┘ └───────────┘ └───────────┘ └───────────┘ ``` -## 模块化配置系统 - -### 设计原则 - -Extra2D 采用**模块化配置系统**,遵循开闭原则: - -- **AppConfig** 只包含应用级别配置(appName, appVersion, organization 等) -- **各模块配置** 由模块自己管理,实现 `IModuleConfig` 接口 -- **新增模块** 无需修改引擎核心代码 - -### 配置文件结构 - -``` -Extra2D/include/extra2d/ -├── config/ -│ ├── app_config.h # 应用级别配置 -│ ├── module_config.h # 模块配置接口 -│ └── config_manager.h # 配置管理器 -├── platform/ -│ └── window_module.h # 窗口模块(含 Cfg 配置结构) -├── graphics/ -│ ├── core/ # 渲染核心 -│ │ ├── render_backend.h # 渲染后端接口 -│ │ ├── render_command.h # 渲染命令 -│ │ ├── render_module.h # 渲染模块(含 Cfg 配置结构) -│ │ └── render_target.h # 渲染目标 -│ ├── camera/ # 相机和视口 -│ │ ├── camera.h # 2D 相机 -│ │ └── viewport_adapter.h # 视口适配器 -│ ├── shader/ # Shader 系统 -│ │ ├── shader_interface.h -│ │ ├── shader_manager.h -│ │ ├── shader_loader.h -│ │ ├── shader_cache.h -│ │ ├── shader_hot_reloader.h -│ │ └── shader_preset.h -│ ├── texture/ # 纹理系统 -│ │ ├── texture.h # 纹理接口 -│ │ ├── texture_atlas.h # 纹理图集 -│ │ ├── texture_pool.h # 纹理池 -│ │ ├── alpha_mask.h # Alpha 遮罩 -│ │ └── font.h # 字体接口 -│ ├── memory/ # GPU 内存管理 -│ │ ├── vram_manager.h # VRAM 管理器 -│ │ └── gpu_context.h # GPU 上下文 -│ └── opengl/ # OpenGL 实现 -├── scene/ # 场景系统 -├── services/ # 服务接口 -└── event/ # 事件系统 -``` - -### AppConfig 结构 - -```cpp -struct AppConfig { - std::string appName = "Extra2D App"; - std::string appVersion = "1.0.0"; - std::string organization = ""; - std::string configFile = "config.json"; - PlatformType targetPlatform = PlatformType::Auto; - - static AppConfig createDefault(); - bool validate() const; - void reset(); - void merge(const AppConfig& other); -}; -``` - -### 模块配置示例 - -```cpp -// window_config.h -struct WindowConfigData { - std::string title = "Extra2D Application"; - int width = 1280; - int height = 720; - WindowMode mode = WindowMode::Windowed; - bool vsync = true; - bool resizable = true; - // ... -}; - -// window_module.h -class WindowModuleConfig : public IModuleConfig { -public: - WindowConfigData windowConfig; - - ModuleInfo getModuleInfo() const override; - std::string getConfigSectionName() const override { return "window"; } - bool validate() const override; - void applyPlatformConstraints(PlatformType platform) override; - bool loadFromJson(const void* jsonData) override; - bool saveToJson(void* jsonData) const override; -}; -``` - ---- - ## 模块 vs 服务 | 特性 | 模块 (Module) | 服务 (Service) | |-----|--------------|---------------| | 用途 | 平台级初始化 | 运行时功能 | | 生命周期 | Application 管理 | ServiceLocator 管理 | -| 配置管理 | 独立配置文件 | 无配置 | -| 依赖方式 | 通过 ModuleRegistry | 通过 ServiceLocator | +| 依赖方式 | 通过 `deps()` 方法声明 | 通过 `ServiceLocator` 获取 | | 可替换性 | 编译时确定 | 运行时可替换 | | 示例 | Window, Render, Input | Scene, Timer, Event, Camera | -## 模块优先级 - -模块按优先级从小到大初始化,关闭时逆序执行: - -| 优先级值 | 枚举名称 | 用途 | 模块示例 | -|---------|---------|------|---------| -| 0 | `Core` | 核心模块,最先初始化 | Config, Platform, Window | -| 50 | `Input` | 输入系统 | Input | -| 100 | `Graphics` | 图形渲染 | Render | -| 200 | `Audio` | 音频系统 | Audio | -| 500 | `Resource` | 资源管理 | Resource | - ---- - ## 模块系统 -### IModuleConfig +### 模块基类 -模块配置接口,定义模块的元数据和配置: +所有模块继承自 `Module` 基类: ```cpp -class IModuleConfig { +class Module { public: - virtual ~IModuleConfig() = default; - - virtual ModuleInfo getModuleInfo() const = 0; - virtual std::string getConfigSectionName() const = 0; - virtual bool validate() const = 0; - virtual void resetToDefaults() = 0; - virtual bool loadFromJson(const void* jsonData) = 0; - virtual bool saveToJson(void* jsonData) const = 0; - virtual void applyPlatformConstraints(PlatformType platform) {} + 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 deps() const { return {}; } // 依赖 + void setApp(Application* app) { app_ = app; } + Application* app() const { return app_; } +protected: + Application* app_ = nullptr; }; ``` -### IModuleInitializer +### 内置模块 -模块初始化器接口,管理模块的生命周期: +| 模块 | 优先级 | 依赖 | 职责 | +|-----|-------|------|------| +| WindowModule | 0 | 无 | 窗口创建和管理 | +| RenderModule | 10 | WindowModule | 渲染后端初始化 | +| InputModule | 20 | WindowModule | 输入设备管理 | + +### 模块配置 + +每个模块有自己的配置结构: ```cpp -class IModuleInitializer { -public: - virtual ~IModuleInitializer() = default; - - virtual ModuleId getModuleId() const = 0; - virtual ModulePriority getPriority() const = 0; - virtual std::vector getDependencies() const = 0; - - virtual bool initialize(const IModuleConfig* config) = 0; - virtual void shutdown() = 0; - virtual bool isInitialized() const = 0; +// 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; }; ``` -### ModuleRegistry - -模块注册表,管理所有模块: +### 使用模块 ```cpp -class ModuleRegistry { -public: - static ModuleRegistry& instance(); - - ModuleId registerModule( - UniquePtr config, - ModuleInitializerFactory factory - ); - - IModuleConfig* getModuleConfig(ModuleId id); - IModuleInitializer* getInitializer(ModuleId id); - std::vector getAllModules() const; - std::vector getInitializationOrder() const; -}; -``` +Application app; ---- - -## 创建新模块 - -### 步骤 1:定义配置数据结构 - -```cpp -// my_module_config.h -#pragma once - -#include - -namespace extra2d { - -struct MyModuleConfigData { - int someSetting = 42; - bool enabled = true; - std::string path = "default"; -}; - -} -``` - -### 步骤 2:定义配置类 - -```cpp -// my_module.h -#pragma once - -#include -#include -#include "my_module_config.h" - -namespace extra2d { - -class MyModuleConfig : public IModuleConfig { -public: - MyModuleConfigData config; - - ModuleInfo getModuleInfo() const override { - ModuleInfo info; - info.id = 0; - info.name = "MyModule"; - info.version = "1.0.0"; - info.priority = ModulePriority::Graphics; - info.enabled = true; - return info; - } - - std::string getConfigSectionName() const override { return "my_module"; } - bool validate() const override { return config.someSetting > 0; } - - void resetToDefaults() override { - config = MyModuleConfigData{}; - } - - void applyPlatformConstraints(PlatformType platform) override { -#ifdef __SWITCH__ - config.someSetting = 30; // Switch 平台优化 -#else - (void)platform; -#endif - } - - bool loadFromJson(const void* jsonData) override; - bool saveToJson(void* jsonData) const override; -}; - -} -``` - -### 步骤 3:定义初始化器 - -```cpp -// my_module.h (续) -class MyModuleInitializer : public IModuleInitializer { -public: - MyModuleInitializer(); - ~MyModuleInitializer() override; - - ModuleId getModuleId() const override { return moduleId_; } - ModulePriority getPriority() const override { return ModulePriority::Graphics; } - std::vector getDependencies() const override; - - bool initialize(const IModuleConfig* config) override; - void shutdown() override; - bool isInitialized() const override { return initialized_; } - - void setModuleId(ModuleId id) { moduleId_ = id; } - -private: - ModuleId moduleId_ = INVALID_MODULE_ID; - bool initialized_ = false; - MyModuleConfigData config_; -}; - -ModuleId get_my_module_id(); -void register_my_module(); -``` - -### 步骤 4:实现模块 - -```cpp -// my_module.cpp -#include "my_module.h" -#include -#include -#include - -using json = nlohmann::json; - -namespace extra2d { - -static ModuleId s_myModuleId = INVALID_MODULE_ID; - -ModuleId get_my_module_id() { return s_myModuleId; } - -bool MyModuleConfig::loadFromJson(const void* jsonData) { - if (!jsonData) return false; - try { - const json& j = *static_cast(jsonData); - if (j.contains("someSetting")) config.someSetting = j["someSetting"].get(); - if (j.contains("enabled")) config.enabled = j["enabled"].get(); - if (j.contains("path")) config.path = j["path"].get(); - return true; - } catch (...) { - return false; - } -} - -bool MyModuleConfig::saveToJson(void* jsonData) const { - if (!jsonData) return false; - try { - json& j = *static_cast(jsonData); - j["someSetting"] = config.someSetting; - j["enabled"] = config.enabled; - j["path"] = config.path; - return true; - } catch (...) { - return false; - } -} - -MyModuleInitializer::MyModuleInitializer() : moduleId_(INVALID_MODULE_ID), initialized_(false) {} - -MyModuleInitializer::~MyModuleInitializer() { if (initialized_) shutdown(); } - -std::vector MyModuleInitializer::getDependencies() const { - return {}; // 无依赖 -} - -bool MyModuleInitializer::initialize(const IModuleConfig* config) { - if (initialized_) return true; - - const MyModuleConfig* cfg = dynamic_cast(config); - if (!cfg) { - E2D_LOG_ERROR("Invalid MyModule config"); - return false; - } - - config_ = cfg->config; - - // 执行初始化逻辑... - - initialized_ = true; - E2D_LOG_INFO("MyModule initialized with setting: {}", config_.someSetting); - return true; -} - -void MyModuleInitializer::shutdown() { - if (!initialized_) return; - // 执行清理逻辑... - initialized_ = false; - E2D_LOG_INFO("MyModule shutdown"); -} - -void register_my_module() { - if (s_myModuleId != INVALID_MODULE_ID) return; - - s_myModuleId = ModuleRegistry::instance().registerModule( - makeUnique(), - []() -> UniquePtr { - auto initializer = makeUnique(); - initializer->setModuleId(s_myModuleId); - return initializer; - } - ); -} - -namespace { - struct MyModuleAutoRegister { - MyModuleAutoRegister() { register_my_module(); } - }; - static MyModuleAutoRegister s_autoRegister; -} - -} -``` - ---- - -## 内置模块 - -### Config 模块 - -**职责**:管理 ConfigManager 和应用配置 - -**配置**: -```cpp -AppConfig config; -config.appName = "My Application"; -config.appVersion = "1.0.0"; -``` - ---- - -### Platform 模块 - -**职责**:平台检测和平台特定初始化 - -**支持平台**: -- Windows -- Linux -- macOS -- Nintendo Switch - -**平台能力查询**: -```cpp -auto* platformConfig = createPlatformConfig(); -const auto& caps = platformConfig->capabilities(); -if (caps.supportsGamepad) { - // 支持手柄 -} -``` - ---- - -### Window 模块 - -**职责**:窗口创建和管理 - -**后端**:支持 SDL2 和 GLFW,可通过配置切换 - -**配置**: -```cpp +// 配置 Window 模块 app.use([](auto& cfg) { - cfg.title = "My App"; - cfg.w = 1280; - cfg.h = 720; - cfg.mode = WindowMode::Windowed; - cfg.vsync = true; - cfg.backend = "sdl2"; // 可选:"sdl2" 或 "glfw" + cfg.title = "My Game"; + cfg.w = 1920; + cfg.h = 1080; + cfg.backend = "glfw"; // 使用 GLFW 后端 }); -``` -**构建时选择后端**: -```bash -# 使用 SDL2 后端(默认) -xmake f --window_backend=sdl2 -y - -# 使用 GLFW 后端 -xmake f --window_backend=glfw -y -``` - -**平台约束**: -- Switch 平台自动强制全屏模式 - ---- - -### Input 模块 - -**职责**:输入设备管理(键盘、鼠标、手柄) - -**配置**: -```cpp -app.use([](auto& cfg) { - cfg.deadzone = 0.15f; - cfg.mouseSensitivity = 1.0f; - cfg.enableVibration = true; -}); -``` - -**使用示例**: -```cpp -IInput* input = app.window().input(); - -// 键盘 -if (input->pressed(Key::Space)) { - // 空格键刚按下 -} - -// 鼠标 -if (input->down(Mouse::Left)) { - Vec2 pos = input->mouse(); -} - -// 手柄 -if (input->gamepad()) { - Vec2 stick = input->leftStick(); - if (input->pressed(Gamepad::A)) { - input->vibrate(0.5f, 0.5f); // 振动反馈 - } -} -``` - ---- - -### Render 模块 - -**职责**:渲染器初始化和管理 - -**配置**: -```cpp +// 配置 Render 模块 app.use([](auto& cfg) { cfg.backend = BackendType::OpenGL; cfg.vsync = true; cfg.targetFPS = 60; - cfg.multisamples = 4; }); + +// 配置 Input 模块 +app.use([](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 -配置使用 JSON 格式,每个模块有独立的配置节: - -```json -{ - "app": { - "name": "My Application", - "version": "1.0.0", - "organization": "MyCompany" - }, - "window": { - "title": "My Application", - "width": 1280, - "height": 720, - "mode": "windowed", - "vsync": true - }, - "render": { - "targetFPS": 60, - "multisamples": 4 - }, - "input": { - "deadzone": 0.15, - "mouseSensitivity": 1.0, - "enableVibration": true - } -} +# 选择渲染后端 +xmake f --render_backend=opengl -y # 使用 OpenGL(默认) +xmake f --render_backend=vulkan -y # 使用 Vulkan ``` ---- - ## 服务系统 -### IService - -服务接口基类,所有服务必须实现: +### 服务接口基类 ```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; - - ServiceState getState() const; - const std::string& getName() const; + virtual void update(float deltaTime) {} + virtual bool isInitialized() const = 0; }; ``` ### 内置服务 -| 服务 | 用途 | 优先级 | -|-----|------|-------| -| SceneService | 场景管理 | 300 | -| TimerService | 计时器 | 200 | -| EventService | 事件分发 | 100 | -| CameraService | 相机系统 | 400 | +| 服务 | 用途 | 自动注册宏 | +|-----|------|-----------| +| 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 sceneService = Application::get().scenes(); -auto timerService = Application::get().timers(); -auto eventService = Application::get().events(); +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); @@ -624,9 +211,113 @@ eventService->addListener(EventType::KeyPressed, [](Event& e) { auto& keyEvent = std::get(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 RenderBackend { +public: + virtual ~RenderBackend() = 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 createTexture(int width, int height, const uint8_t *pixels, int channels) = 0; + virtual Ptr 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 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 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 │ │ +│ └─────────────────┘ └─────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` ## 输入事件系统 @@ -659,14 +350,13 @@ enum class EventType { // 窗口 WindowResize, WindowClose, - // ... }; ``` ### 事件监听 ```cpp -auto eventService = Application::get().events(); +auto eventService = app.events(); // 监听键盘事件 eventService->addListener(EventType::KeyPressed, [](Event& e) { @@ -682,148 +372,10 @@ eventService->addListener(EventType::MouseButtonPressed, [](Event& e) { }); ``` ---- - -## 平台支持 - -### 支持的平台 - -| 平台 | 窗口后端 | 图形 API | 特殊处理 | -|-----|---------|---------|---------| -| Windows | SDL2 / GLFW | OpenGL ES 3.2 | - | -| Linux | SDL2 / GLFW | OpenGL ES 3.2 | - | -| macOS | SDL2 / GLFW | OpenGL ES 3.2 | - | -| Nintendo Switch | SDL2 / GLFW | OpenGL ES 3.2 | romfs, 强制全屏 | - -### 平台检测 - -```cpp -PlatformType platform = PlatformDetector::detect(); -const char* name = getPlatformTypeName(platform); - -switch (platform) { - case PlatformType::Windows: // Windows 处理 - case PlatformType::Switch: // Switch 处理 - // ... -} -``` - -### 平台能力 - -```cpp -auto* config = createPlatformConfig(); -const auto& caps = config->capabilities(); - -if (caps.supportsWindowed) { /* 支持窗口模式 */ } -if (caps.supportsGamepad) { /* 支持手柄 */ } -if (caps.supportsTouch) { /* 支持触摸 */ } -``` - ---- - -## 最佳实践 - -### 1. 模块配置独立化 - -```cpp -// 好的做法:模块管理自己的配置,使用 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 configFn) { - configFn(cfg_); - } -}; - -// 使用 Lambda 配置模块 -app.use([](auto& cfg) { - cfg.w = 1920; - cfg.h = 1080; - cfg.backend = "glfw"; -}); - -// 不好的做法:所有配置放在 AppConfig -struct AppConfig { - WindowConfig window; // 耦合度高 - RenderConfig render; - // ... 新增模块需要修改 AppConfig -}; -``` - -### 2. 使用平台约束 - -```cpp -void WindowModuleConfig::applyPlatformConstraints(PlatformType platform) { -#ifdef __SWITCH__ - windowConfig.mode = WindowMode::Fullscreen; - windowConfig.resizable = false; -#else - (void)platform; -#endif -} -``` - -### 3. 模块自动注册 - -```cpp -namespace { - struct MyModuleAutoRegister { - MyModuleAutoRegister() { register_my_module(); } - }; - static MyModuleAutoRegister s_autoRegister; // 程序启动时自动注册 -} -``` - ---- - -## 调试 - -### 查看模块初始化顺序 - -```cpp -auto order = ModuleRegistry::instance().getInitializationOrder(); -for (ModuleId id : order) { - auto* config = ModuleRegistry::instance().getModuleConfig(id); - if (config) { - auto info = config->getModuleInfo(); - E2D_LOG_INFO("Module: {} (priority: {})", - info.name, static_cast(info.priority)); - } -} -``` - -### 查看服务状态 - -```cpp -auto services = ServiceLocator::instance().getAllServices(); -for (const auto& service : services) { - auto info = service->getServiceInfo(); - E2D_LOG_INFO("Service: {} (state: {})", - info.name, static_cast(info.state)); -} -``` - ---- - ## 场景图系统 -### 概述 - -Extra2D 使用场景图(Scene Graph)管理游戏对象。场景图是一个树形结构,每个节点可以包含子节点,形成层级关系。 - ### Node 基类 -所有场景对象的基类,提供变换、层级管理和渲染功能: - ```cpp class Node : public std::enable_shared_from_this { public: @@ -835,7 +387,6 @@ public: Ptr getParent() const; const std::vector>& getChildren() const; - Ptr findChild(const std::string& name) const; // 变换属性 void setPos(const Vec2& pos); @@ -860,124 +411,46 @@ public: }; ``` -### Scene 类 - -场景是场景图的根节点,管理相机和视口: +### 使用场景 ```cpp -class Scene : public Node { -public: - // 场景属性 - void setBackgroundColor(const Color& color); - - // 摄像机 - void setCamera(Ptr camera); - Camera* getActiveCamera() const; - - // 视口 - void setViewportSize(float width, float height); - - // 渲染和更新 - void renderScene(RenderBackend& renderer); - void updateScene(float dt); - - // 静态创建 - static Ptr create(); -}; -``` +auto scene = Scene::create(); -### ShapeNode 形状节点 - -用于绘制几何形状: - -```cpp // 创建形状节点 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)); -auto triangle = ShapeNode::createFilledTriangle( - Vec2(0, -40), Vec2(-35, 30), Vec2(35, 30), - Color(0.4f, 1.0f, 0.4f, 1.0f) -); -auto line = ShapeNode::createLine(Vec2(0, 0), Vec2(100, 100), Color(1.0f, 1.0f, 1.0f, 1.0f), 2.0f); -auto polygon = ShapeNode::createFilledPolygon( - {Vec2(0, -50), Vec2(50, 0), Vec2(0, 50), Vec2(-50, 0)}, - Color(1.0f, 0.4f, 1.0f, 1.0f) -); + +// 添加到场景 +scene->addChild(rect); +scene->addChild(circle); + +// 运行场景 +app.scenes()->runWithScene(scene); ``` -### 变换继承 - -子节点继承父节点的变换: - -```cpp -auto parent = makeShared(); -parent->setPos(100, 100); -parent->setRotation(45); // 旋转 45 度 - -auto child = makeShared(); -child->setPos(50, 0); // 相对于父节点的位置 -parent->addChild(child); - -// child 的世界位置 = parent 的变换 * child 的本地位置 -// child 会随 parent 一起旋转 -``` - -### 渲染流程 - -``` -Application::render() - └── CameraService::getViewProjectionMatrix() // 设置视图投影矩阵 - └── SceneService::render() - └── Scene::renderContent() - └── Node::render() (递归) - └── pushTransform(localTransform) // 压入本地变换 - └── onDraw() // 绘制形状 - └── children::onRender() // 递归渲染子节点 - └── popTransform() // 弹出变换 -``` - ---- - ## 视口适配系统 -### 概述 - -视口适配系统确保游戏内容在不同分辨率和宽高比的屏幕上正确显示。 - -### ViewportAdapter - -视口适配器,计算视口位置和缩放: +### 视口适配模式 ```cpp -// 视口适配模式 enum class ViewportMode { AspectRatio, // 保持宽高比,可能有黑边 Stretch, // 拉伸填满整个窗口 Center, // 居中显示,不缩放 Custom // 自定义缩放和偏移 }; - -// 视口配置 -struct ViewportConfig { - float logicWidth = 1920.0f; - float logicHeight = 1080.0f; - ViewportMode mode = ViewportMode::AspectRatio; - Color letterboxColor = Colors::Black; // 黑边颜色 - float customScale = 1.0f; - Vec2 customOffset = Vec2::Zero(); -}; ``` -### 使用 CameraService 配置视口 +### 配置视口 ```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); // 黑边颜色 + 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); @@ -985,256 +458,20 @@ if (cameraService) { } ``` -### 窗口大小变化处理 +## 平台支持 -当窗口大小变化时,Application 会自动更新视口: - -```cpp -// Application 内部处理 -window_->onResize([this, cameraService](int width, int height) { - cameraService->updateViewport(width, height); - cameraService->applyViewportAdapter(); - - auto sceneService = ServiceLocator::instance().getService(); - if (sceneService) { - auto currentScene = sceneService->getCurrentScene(); - if (currentScene) { - currentScene->setViewportSize(width, height); - } - } -}); -``` - -### 适配模式对比 - -| 模式 | 描述 | 适用场景 | -|-----|------|---------| -| `AspectRatio` | 保持宽高比,可能有黑边 | 大多数游戏 | -| `Stretch` | 拉伸填满整个窗口 | 不在乎变形的简单游戏 | -| `Center` | 居中显示,不缩放 | 固定分辨率的像素游戏 | -| `Custom` | 自定义缩放和偏移 | 特殊需求 | - ---- +| 平台 | 窗口后端 | 图形 API | +|-----|---------|---------| +| Windows | SDL2 / GLFW | OpenGL / Vulkan | +| Linux | SDL2 / GLFW | OpenGL / Vulkan | +| macOS | SDL2 / GLFW | OpenGL / Vulkan | +| Nintendo Switch | SDL2 / GLFW | OpenGL / Vulkan | ## 示例 完整示例请参考: -- [examples/hello_module/](../../examples/hello_module/) - **Hello World 自定义模块示例** -- [examples/basic/main.cpp](../../examples/basic/main.cpp) - 基础示例(场景图、输入事件、视口适配) -- [Extra2D/src/platform/window_module.cpp](../../Extra2D/src/platform/window_module.cpp) - Window 模块实现 -- [Extra2D/src/platform/input_module.cpp](../../Extra2D/src/platform/input_module.cpp) - Input 模块实现 -- [Extra2D/src/graphics/core/render_module.cpp](../../Extra2D/src/graphics/core/render_module.cpp) - Render 模块实现 -- [Extra2D/src/scene/node.cpp](../../Extra2D/src/scene/node.cpp) - Node 实现 -- [Extra2D/src/scene/shape_node.cpp](../../Extra2D/src/scene/shape_node.cpp) - ShapeNode 实现 - ---- - -## Hello World 自定义模块示例 - -### 示例概述 - -`examples/hello_module/` 目录包含一个完整的自定义模块示例,展示如何: - -1. 定义模块配置数据结构 -2. 实现 `IModuleConfig` 接口 -3. 实现 `IModuleInitializer` 接口 -4. 使用自动注册机制 -5. 支持 JSON 配置 - -### 文件结构 - -``` -examples/hello_module/ -├── hello_module.h # 模块头文件(配置类 + 初始化器类) -├── hello_module.cpp # 模块实现 -├── main.cpp # 示例入口 -└── config.json # 配置文件示例 -``` - -### 核心代码解析 - -#### 1. 配置数据结构 - -```cpp -struct HelloModuleConfigData { - std::string greeting = "Hello, Extra2D!"; - int repeatCount = 1; - bool enableLogging = true; -}; -``` - -#### 2. 配置类实现 - -```cpp -class HelloModuleConfig : public IModuleConfig { -public: - HelloModuleConfigData config; - - ModuleInfo getModuleInfo() const override { - ModuleInfo info; - info.name = "HelloModule"; - info.version = "1.0.0"; - info.priority = ModulePriority::User; // 用户自定义模块 - return info; - } - - std::string getConfigSectionName() const override { - return "hello"; // 对应 config.json 中的 "hello" 节 - } - - bool validate() const override { - return !config.greeting.empty() && config.repeatCount > 0; - } -}; -``` - -#### 3. 初始化器实现 - -```cpp -class HelloModuleInitializer : public IModuleInitializer { -public: - bool initialize(const IModuleConfig* config) override { - const HelloModuleConfig* cfg = dynamic_cast(config); - if (!cfg || !cfg->validate()) { - return false; - } - - config_ = cfg->config; - initialized_ = true; - - // 执行模块初始化逻辑 - E2D_LOG_INFO("HelloModule initialized: {}", config_.greeting); - return true; - } - - void shutdown() override { - E2D_LOG_INFO("HelloModule shutdown"); - initialized_ = false; - } -}; -``` - -#### 4. 自动注册机制 - -```cpp -namespace { - struct HelloModuleAutoRegister { - HelloModuleAutoRegister() { - register_hello_module(); - } - }; - - static HelloModuleAutoRegister s_autoRegister; // 程序启动时自动执行 -} -``` - -### 配置文件示例 - -```json -{ - "hello": { - "greeting": "Hello from custom module!", - "repeatCount": 3, - "enableLogging": true - } -} -``` - -### 运行示例 - -```bash -xmake run demo_hello_module -``` - -### 预期输出 - -``` -[INFO] HelloModule initialized -[INFO] Greeting: Hello, Extra2D! -[INFO] Repeat Count: 1 -[INFO] Logging Enabled: true -[INFO] [HelloModule] Hello, Extra2D! -[INFO] HelloScene entered -[INFO] [HelloModule] Hello, Extra2D! # 场景 onEnter() 调用 -[INFO] Scene calling HelloModule from onUpdate... -[INFO] [HelloModule] Hello, Extra2D! # 场景每5秒调用 -[INFO] HelloModule shutdown - Goodbye! -``` - -### 在场景中使用模块 - -模块初始化后,可以在场景中通过 `ModuleRegistry` 获取模块实例并调用其功能: - -```cpp -class HelloScene : public Scene { -public: - void onEnter() override { - Scene::onEnter(); - - // 获取模块初始化器 - ModuleId helloId = get_hello_module_id(); - auto* initializer = ModuleRegistry::instance().getInitializer(helloId); - - if (initializer) { - // 转换为具体类型 - auto* helloInit = dynamic_cast(initializer); - if (helloInit) { - // 调用模块功能 - helloInit->sayHello(); - } - } - } - - void onUpdate(float dt) override { - Scene::onUpdate(dt); - - time_ += dt; - - // 每5秒调用一次模块功能 - if (time_ >= 5.0f) { - ModuleId helloId = get_hello_module_id(); - auto* initializer = ModuleRegistry::instance().getInitializer(helloId); - if (initializer) { - auto* helloInit = dynamic_cast(initializer); - if (helloInit) { - helloInit->sayHello(); - } - } - time_ = 0.0f; - } - } - -private: - float time_ = 0.0f; -}; -``` - -### 模块使用流程总结 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 1. 程序启动 │ -│ └── 静态变量 HelloModuleAutoRegister 自动注册模块 │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 2. Application::init() │ -│ └── 遍历 ModuleRegistry 中所有已注册模块 │ -│ └── 按优先级顺序调用 initialize() │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 3. 场景/其他代码使用模块 │ -│ └── ModuleRegistry::getInitializer(moduleId) │ -│ └── dynamic_cast 转换为具体类型 │ -│ └── 调用模块方法 │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 4. Application::shutdown() │ -│ └── 按逆序调用所有模块的 shutdown() │ -└─────────────────────────────────────────────────────────────┘ -``` +- [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) - 帧缓冲