Extra2D/docs/module_system.md

14 KiB
Raw Blame History

Extra2D 模块系统

概述

Extra2D 采用模块化架构设计(参考 Kiwano所有核心功能通过模块系统和服务系统管理。系统提供

  • 统一的生命周期管理:初始化、更新、渲染、关闭
  • 优先级排序:确保模块按正确顺序初始化
  • 显式注册:通过 Application::use() 注册模块
  • Context 模式:使用上下文对象遍历模块链
  • 依赖注入:通过服务定位器解耦模块间依赖

架构图

┌─────────────────────────────────────────────────────────────┐
│                      Application                             │
│  (协调模块和服务,通过服务定位器获取依赖)                       │
└─────────────────────────────────────────────────────────────┘
                              │
              ┌───────────────┴───────────────┐
              ▼                               ▼
┌─────────────────────────────┐   ┌─────────────────────────────┐
│         Modules             │   │      ServiceLocator         │
│  (模块列表,按优先级排序)     │   │  (服务定位器,管理运行时服务) │
└─────────────────────────────┘   └─────────────────────────────┘
              │                               │
        ┌─────┴─────┐                 ┌───────┴───────┐
        ▼           ▼                 ▼               ▼
┌───────────┐ ┌───────────┐   ┌───────────┐   ┌───────────┐
│  Config   │ │  Window   │   │  Scene    │   │  Timer    │
│  Module   │ │  Module   │   │  Service  │   │  Service  │
└───────────┘ └───────────┘   └───────────┘   └───────────┘

模块基类

Module

所有模块只需继承 Module 基类,实现需要的生命周期方法:

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; }
};

模块上下文

用于遍历模块链,支持链式调用:

/**
 * @brief 模块上下文基类
 */
class ModuleContext {
public:
    void next();           // 遍历下一个模块
    bool isDone() const;   // 检查是否完成
};

/**
 * @brief 更新上下文
 */
class UpdateContext : public ModuleContext {
public:
    float getDeltaTime() const;  // 获取帧间隔时间
};

/**
 * @brief 渲染上下文
 */
class RenderContext : public ModuleContext {
public:
    enum class Phase { Before, On, After };
    Phase getPhase() const;
};

/**
 * @brief 事件上下文
 */
class EventContext : public ModuleContext {
};

模块 vs 服务

特性 模块 (Module) 服务 (Service)
用途 平台级初始化 运行时功能
生命周期 Application 管理 ServiceLocator 管理
注册方式 app.use(module) locator.registerService()
可替换性 编译时确定 运行时可替换
示例 Window, Render, Input Scene, Timer, Event, Camera

模块优先级

模块按优先级从小到大初始化,关闭时逆序执行:

模块 优先级 说明
LoggerModule -1 最先初始化
ConfigModule 0 配置加载
PlatformModule 10 平台初始化
WindowModule 20 窗口创建
InputModule 30 输入系统
RenderModule 40 渲染系统
ScriptModule 500 脚本系统
用户模块 1000+ 用户自定义

创建新模块

简单示例

#include <extra2d/core/module.h>

class MyModule : public extra2d::Module {
public:
    const char* getName() const override { return "MyModule"; }
    
    int getPriority() const override { return 1000; }
    
    void setupModule() override {
        // 初始化资源
        extra2d::E2D_LOG_INFO("MyModule initialized");
    }
    
    void destroyModule() override {
        // 清理资源
        extra2d::E2D_LOG_INFO("MyModule destroyed");
    }
    
    void onUpdate(extra2d::UpdateContext& ctx) override {
        // 更新逻辑
        ctx.next();  // 继续下一个模块
    }
};

注册模块

int main() {
    auto& app = extra2d::Application::get();
    
    MyModule myModule;
    app.use(myModule);  // 显式注册
    
    app.init();
    app.run();
    return 0;
}

带配置的模块

// my_module.h
#pragma once

#include <extra2d/core/module.h>
#include <string>

struct MyModuleConfig {
    std::string greeting = "Hello!";
    int repeatCount = 1;
};

class MyModule : public extra2d::Module {
public:
    MyModule();
    ~MyModule() override;
    
    const char* getName() const override { return "MyModule"; }
    int getPriority() const override { return 1000; }
    
    void setupModule() override;
    void destroyModule() override;
    void onUpdate(extra2d::UpdateContext& ctx) override;
    
    void setConfig(const MyModuleConfig& config) { config_ = config; }
    void sayHello() const;

private:
    MyModuleConfig config_;
    float time_ = 0.0f;
};
// my_module.cpp
#include "my_module.h"
#include <extra2d/utils/logger.h>

MyModule::MyModule() : Module() {}

MyModule::~MyModule() {
    if (isInitialized()) {
        destroyModule();
    }
}

void MyModule::setupModule() {
    if (isInitialized()) return;
    
    setInitialized(true);
    E2D_LOG_INFO("MyModule initialized");
    E2D_LOG_INFO("  Greeting: {}", config_.greeting);
    E2D_LOG_INFO("  Repeat Count: {}", config_.repeatCount);
    
    sayHello();
}

void MyModule::destroyModule() {
    if (!isInitialized()) return;
    
    E2D_LOG_INFO("MyModule shutdown");
    setInitialized(false);
}

void MyModule::onUpdate(extra2d::UpdateContext& ctx) {
    if (!isInitialized()) {
        ctx.next();
        return;
    }
    
    time_ += ctx.getDeltaTime();
    
    if (time_ >= 5.0f) {
        sayHello();
        time_ = 0.0f;
    }
    
    ctx.next();
}

void MyModule::sayHello() const {
    for (int i = 0; i < config_.repeatCount; ++i) {
        E2D_LOG_INFO("[MyModule] {}", config_.greeting);
    }
}

内置模块

Config 模块

职责:管理 ConfigManager 和应用配置

优先级0

extra2d::AppConfig config;
config.appName = "My Application";
config.appVersion = "1.0.0";

Logger 模块

职责:日志系统初始化

优先级-1最先初始化

extra2d::LoggerModule loggerModule;
loggerModule.setLogLevel(extra2d::LogLevel::Debug);
loggerModule.setFileOutput("app.log");
app.use(loggerModule);

Platform 模块

职责:平台检测和平台特定初始化

优先级10

支持平台

  • Windows
  • Linux
  • macOS
  • Nintendo Switch

Window 模块

职责:窗口创建和管理

优先级20

后端:统一使用 SDL2

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);

Input 模块

职责:输入设备管理(键盘、鼠标、手柄)

优先级30

extra2d::InputModule inputModule;
extra2d::InputConfigData inputConfig;
inputConfig.deadzone = 0.15f;
inputConfig.enableVibration = true;
inputModule.setInputConfig(inputConfig);
app.use(inputModule);

Render 模块

职责:渲染器初始化和管理

优先级40

extra2d::RenderModule renderModule;
extra2d::RenderModuleConfig renderConfig;
renderConfig.vsync = true;
renderConfig.multisamples = 4;
renderModule.setRenderConfig(renderConfig);
app.use(renderModule);

服务系统

IService

服务接口基类:

class IService {
public:
    virtual ~IService() = default;
    
    virtual ServiceInfo getServiceInfo() const = 0;
    virtual bool initialize() = 0;
    virtual void shutdown() = 0;
    virtual void update(float deltaTime);
};

内置服务

服务 用途 优先级
EventService 事件分发 100
TimerService 计时器 200
SceneService 场景管理 300
CameraService 相机系统 400

使用服务

auto& app = extra2d::Application::get();

// 获取服务
auto sceneService = app.scenes();
auto timerService = app.timers();
auto eventService = app.events();
auto cameraService = app.camera();

// 使用场景服务
sceneService->pushScene(myScene);

// 使用计时器服务
timerService->addTimer(1.0f, []() {
    E2D_LOG_INFO("Timer fired!");
});

// 使用事件服务
eventService->addListener(extra2d::EventType::KeyPress, [](extra2d::Event& e) {
    auto& keyEvent = std::get<extra2d::KeyEvent>(e.data);
    E2D_LOG_INFO("Key pressed: {}", keyEvent.keyCode);
});

输入事件系统

事件类型

enum class EventType {
    // 键盘
    KeyPress,
    KeyRelease,
    KeyRepeat,
    
    // 鼠标
    MousePress,
    MouseRelease,
    MouseMove,
    MouseScroll,
    
    // 手柄
    GamepadConnect,
    GamepadDisconnect,
    GamepadPress,
    GamepadRelease,
    
    // 触摸
    TouchBegin,
    TouchMove,
    TouchEnd,
    
    // 窗口
    WindowResize,
    WindowClose,
};

事件监听

auto eventService = app.events();

// 监听键盘事件
eventService->addListener(EventType::KeyPress, [](Event& e) {
    auto& key = std::get<KeyEvent>(e.data);
    if (key.scancode == static_cast<int>(Key::Escape)) {
        Application::get().quit();
    }
});

// 监听鼠标事件
eventService->addListener(EventType::MousePress, [](Event& e) {
    auto& mouse = std::get<MouseEvent>(e.data);
    E2D_LOG_INFO("Click at ({}, {})", mouse.position.x, mouse.position.y);
});

场景图系统

Node 基类

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 形状节点

// 创建形状节点
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 parent = makeShared<Node>();
parent->setPos(100, 100);
parent->setRotation(45);

auto child = makeShared<Node>();
child->setPos(50, 0);  // 相对于父节点
parent->addChild(child);

// child 会随 parent 一起旋转

视口适配系统

ViewportAdapter

enum class ViewportMode {
    AspectRatio,  // 保持宽高比,可能有黑边
    Stretch,      // 拉伸填满整个窗口
    Center,       // 居中显示,不缩放
    Custom        // 自定义缩放和偏移
};

使用 CameraService 配置视口

auto cameraService = app.camera();
if (cameraService) {
    extra2d::ViewportConfig vpConfig;
    vpConfig.logicWidth = 1280.0f;
    vpConfig.logicHeight = 720.0f;
    vpConfig.mode = extra2d::ViewportMode::AspectRatio;
    
    cameraService->setViewportConfig(vpConfig);
    cameraService->updateViewport(windowWidth, windowHeight);
}

示例

完整示例请参考:


最佳实践

1. 模块优先级

// 核心模块使用低优先级
class LoggerModule : public Module {
    int getPriority() const override { return -1; }
};

// 用户模块使用高优先级
class MyModule : public Module {
    int getPriority() const override { return 1000; }
};

2. 链式调用

void onUpdate(UpdateContext& ctx) override {
    // 执行更新逻辑
    doSomething();
    
    // 继续下一个模块
    ctx.next();
}

3. 检查初始化状态

void onUpdate(UpdateContext& ctx) override {
    if (!isInitialized()) {
        ctx.next();
        return;
    }
    
    // 执行更新逻辑
    ctx.next();
}