Files
XCEngine/docs/used/Editor重构计划.md

15 KiB
Raw Blame History

Editor 重构计划

一、当前问题总结

1.1 架构问题

问题 位置 严重程度
Panel 滥用 Core::Layer 继承 editor/src/panels/Panel.h P0
单例模式滥用 所有 Manager 类 P1
直接使用 GameObject* 裸指针 各 Panel 文件 P1
EditorSceneManager 职责过重 editor/src/Managers/SceneManager.h P1
无统一事件总线 整体架构 P1

1.2 功能缺失

功能 当前状态
Undo/Redo 完全缺失
场景序列化/反序列化 完全缺失
资源引用系统 完全缺失
Play/Edit 模式切换 只有框架
Transform Gizmos 完全缺失
Inspector 组件扩展 仅支持 TransformComponent
场景/游戏视图相机控制 完全缺失

1.3 代码质量

  • 硬编码路径(崩溃日志路径)
  • 调试日志残留InspectorPanel 中大量无意义的 Debug 日志)
  • 内存泄漏风险raw new 无智能指针)
  • 异常处理硬编码

二、重构目标

2.1 短期目标(原型验证)

  • 解决 P0 级架构问题
  • 实现基础 Undo/Redo
  • 实现场景序列化
  • 分离引擎核心与编辑器状态

2.2 中期目标(功能完善)

  • 实现 Transform Gizmos
  • 实现 Inspector 组件扩展机制
  • 实现场景/游戏视图相机控制
  • 实现 Play/Edit 模式切换
  • 实现资源引用系统

2.3 长期目标(生产可用)

  • 编辑器配置序列化
  • 插件系统
  • 多场景编辑
  • 性能优化

三、重构方案

3.1 架构重构

3.1.1 Panel 独立化

现状

class Panel : public Core::Layer {  // ❌ 编辑器面板不是游戏层级

重构

namespace XCEngine {
namespace Editor {

class Panel {
public:
    Panel(const std::string& name) : m_name(name), m_isOpen(true) {}
    virtual ~Panel() = default;
    
    virtual void OnAttach() {}
    virtual void OnDetach() {}
    virtual void OnUpdate(float dt) {}
    virtual void OnEvent(void* event) {}
    virtual void Render() = 0;
    
    bool IsOpen() const { return m_isOpen; }
    void SetOpen(bool open) { m_isOpen = open; }
    
protected:
    std::string m_name;
    bool m_isOpen;
};

} // namespace Editor
} // namespace XCEngine

理由Core::Layer 设计用于游戏层级系统UI/World/Background编辑器面板只是 UI 控件,不应继承它。

3.1.2 单例改造为依赖注入

现状

class SelectionManager {
public:
    static SelectionManager& Get() { ... }  // ❌ 单例
};

重构

class IEditorContext {
public:
    virtual ~IEditorContext() = default;
    virtual ISelectionManager& GetSelectionManager() = 0;
    virtual ISceneManager& GetSceneManager() = 0;
    virtual IProjectManager& GetProjectManager() = 0;
    virtual IUndoRedoSystem& GetUndoRedoSystem() = 0;
};

class EditorLayer {
public:
    void SetContext(std::shared_ptr<IEditorContext> context) { m_context = context; }
    
private:
    std::shared_ptr<IEditorContext> m_context;
};

3.1.3 引入事件总线

新增 EditorEventBus

class EditorEventBus {
public:
    template<typename T>
    void Subscribe(std::function<void(const T&)> handler);
    template<typename T>
    void Unsubscribe(uint64_t id);
    template<typename T>
    void Publish(const T& event);
    
private:
    std::unordered_map<size_t, std::vector<std::function<void(void*)>>> m_handlers;
    std::unordered_map<size_t, uint64_t> m_nextId;
};

定义事件类型

struct SelectionChangedEvent {
    std::vector<GameObjectID> selectedObjects;
};

struct EntityCreatedEvent {
    GameObjectID entityId;
};

struct EntityDestroyedEvent {
    GameObjectID entityId;
};

3.1.4 GameObject 生命周期管理

现状

::XCEngine::Components::GameObject* m_selectedGameObject = nullptr;  // ❌ 裸指针

重构

using GameObjectHandle = ResourceHandle<GameObject>;

struct SelectionState {
    std::vector<GameObjectHandle> selectedObjects;
    GameObjectHandle primarySelection;  // 最后选中的
};

3.2 功能重构

3.2.1 Undo/Redo 系统

Command Pattern 实现

class ICommand {
public:
    virtual ~ICommand() = default;
    virtual void Execute() = 0;
    virtual void Undo() = 0;
    virtual std::string GetName() const = 0;
};

class CreateEntityCommand : public ICommand {
public:
    CreateEntityCommand(ISceneManager& scene, GameObjectHandle parent, std::string name);
    void Execute() override;
    void Undo() override;
    std::string GetName() const override { return "Create " + m_name; }
    
private:
    ISceneManager& m_scene;
    GameObjectHandle m_parent;
    std::string m_name;
    GameObjectHandle m_createdEntity;
};

class DeleteEntityCommand : public ICommand {
public:
    DeleteEntityCommand(ISceneManager& scene, GameObjectHandle entity);
    void Execute() override;
    void Undo() override;
    std::string GetName() const override { return "Delete " + m_entityName; }
    
private:
    ISceneManager& m_scene;
    GameObjectHandle m_entity;
    std::string m_entityName;
    std::vector<GameObjectHandle> m_children;
    GameObjectHandle m_parent;
    // 存储删除前的完整状态用于恢复
};

class TransformCommand : public ICommand {
public:
    TransformCommand(GameObjectHandle entity, const Transform& oldState, const Transform& newState);
    void Execute() override { Apply(m_newState); }
    void Undo() override { Apply(m_oldState); }
    
private:
    void Apply(const Transform& t);
    GameObjectHandle m_entity;
    Transform m_oldState;
    Transform m_newState;
};

Undo/Redo Manager

class UndoRedoSystem {
public:
    void Execute(std::unique_ptr<ICommand> command);
    void Undo();
    void Redo();
    bool CanUndo() const { return m_undoStack.size() > m_historyIndex + 1; }
    bool CanRedo() const { return m_historyIndex >= 0; }
    
private:
    std::vector<std::unique_ptr<ICommand>> m_undoStack;
    int m_historyIndex = -1;
    static constexpr size_t MAX_HISTORY = 100;
};

3.2.2 场景序列化

Scene 序列化接口

class ISceneSerializer {
public:
    virtual ~ISceneSerializer() = default;
    virtual bool Serialize(IScene* scene, const std::string& path) = 0;
    virtual bool Deserialize(IScene* scene, const std::string& path) = 0;
};

class JsonSceneSerializer : public ISceneSerializer {
public:
    bool Serialize(IScene* scene, const std::string& path) override;
    bool Deserialize(IScene* scene, const std::string& path) override;
    
private:
    nlohmann::json GameObjectToJson(GameObject* go);
    GameObject* JsonToGameObject(const nlohmann::json& j, GameObject* parent);
};

序列化格式JSON

{
    "version": "1.0",
    "name": "MainScene",
    "entities": [
        {
            "id": "1234567890",
            "name": "Player",
            "components": [
                {
                    "type": "TransformComponent",
                    "position": [0, 0, 0],
                    "rotation": [0, 0, 0],
                    "scale": [1, 1, 1]
                },
                {
                    "type": "MeshRendererComponent",
                    "mesh": "Assets/Meshes/cube.mesh",
                    "material": "Assets/Materials/default.mat"
                }
            ],
            "children": [
                {
                    "id": "9876543210",
                    "name": "Weapon",
                    "components": [...]
                }
            ]
        }
    ]
}

3.2.3 Inspector 组件扩展机制

抽象 Component 编辑器

class IComponentEditor {
public:
    virtual ~IComponentEditor() = default;
    virtual std::string GetComponentTypeName() const = 0;
    virtual bool CanEdit(Component* component) const = 0;
    virtual void Render(Component* component) = 0;
};

class TransformComponentEditor : public IComponentEditor {
public:
    std::string GetComponentTypeName() const override { return "TransformComponent"; }
    bool CanEdit(Component* component) const override { 
        return dynamic_cast<TransformComponent*>(component) != nullptr; 
    }
    void Render(Component* component) override;
};

class InspectorPanel {
public:
    void RegisterComponentEditor(std::unique_ptr<IComponentEditor> editor);
    void RenderGameObject(GameObject* gameObject);
    
private:
    std::vector<std::unique_ptr<IComponentEditor>> m_editors;
    std::unique_ptr<IComponentEditor> GetEditorFor(Component* component);
};

注册默认编辑器

void InspectorPanel::Initialize() {
    RegisterComponentEditor(std::make_unique<TransformComponentEditor>());
    // 后续添加更多编辑器
}

3.2.4 资源引用系统

AssetHandle

template<typename T>
class AssetHandle {
public:
    AssetHandle() = default;
    explicit AssetHandle(AssetID id, std::shared_ptr<T> asset = nullptr);
    
    AssetID GetID() const { return m_id; }
    T* Get() const { return m_asset.get(); }
    T* operator->() const { return m_asset.get(); }
    explicit operator bool() const { return m_asset != nullptr; }
    
private:
    AssetID m_id;
    std::shared_ptr<T> m_asset;
};

class MeshRendererComponent {
public:
    AssetHandle<Mesh> GetMesh() const { return m_mesh; }
    void SetMesh(AssetHandle<Mesh> mesh) { m_mesh = mesh; }
    
private:
    AssetHandle<Mesh> m_mesh;
    AssetHandle<Material> m_material;
};

3.3 界面重构

3.3.1 SceneView 实现

相机控制器

class SceneViewCameraController {
public:
    enum class Mode { Pan, Orbit, Zoom };
    
    void OnUpdate(float dt);
    void OnMouseMove(int x, int y);
    void OnMouseWheel(int delta);
    
    Matrix4 GetViewMatrix() const;
    Matrix4 GetProjectionMatrix() const;
    
private:
    Mode m_mode = Mode::Orbit;
    Vector3 m_target = Vector3::Zero();
    float m_distance = 10.0f;
    float m_pitch = 0.0f;
    float m_yaw = 0.0f;
    
    bool m_isDragging = false;
    ImVec2 m_lastMousePos;
};

Gizmos 渲染

class GizmosRenderer {
public:
    void DrawTranslateGizmo(GameObjectHandle target);
    void DrawRotateGizmo(GameObjectHandle target);
    void DrawScaleGizmo(GameObjectHandle target);
    
    enum class Axis { X, Y, Z, XY, XZ, YZ, XYZ };
    std::optional<Axis> HandleInput(const Ray& ray);
};

3.3.2 Editor 模式状态机

enum class EditorMode { Edit, Play, Paused };

class EditorStateMachine {
public:
    void SetMode(EditorMode mode);
    EditorMode GetMode() const { return m_mode; }
    
    void Update(float dt);
    
private:
    EditorMode m_mode = EditorMode::Edit;
    
    void EnterEditMode();
    void ExitEditMode();
    void EnterPlayMode();
    void ExitPlayMode();
};

四、重构实施计划

阶段一:架构重构(第 1-2 周)

任务 依赖 状态
Panel 类独立化 - 完成
引入事件总线 - 完成
EditorContext 接口定义 Panel 独立化 完成
SelectionManager 改造 事件总线 完成
EditorLayer 重构 上述全部 完成

已完成的文件

  • editor/src/panels/Panel.h/cpp - 独立的 Panel 基类
  • editor/src/Core/EventBus.h - 类型安全事件总线
  • editor/src/Core/EditorEvents.h - 编辑器事件类型定义
  • editor/src/Core/ISelectionManager.h - 选择管理器接口
  • editor/src/Core/SelectionManager.h - 选择管理器实现
  • editor/src/Core/IEditorContext.h - 编辑器上下文接口
  • editor/src/Core/EditorContext.h - 编辑器上下文实现
  • editor/src/Core/EditorConsoleSink.h/cpp - Editor 专用日志 Sink

2026-03-25 更新

  • editor/src/panels/HierarchyPanel.h/cpp - 已迁移至 IEditorContext
  • editor/src/panels/InspectorPanel.h/cpp - 已迁移至 IEditorContext + EventBus
  • 移除了所有 Panel 中的 SelectionManager::Get()EditorSceneManager::Get() 单例调用
  • SceneManager::RenameEntity 实现已添加
  • 日志系统统一:删除了独立的 LogSystem 单例Editor 和 Engine 共用 Debug::Logger
  • ConsolePanel 改用 EditorConsoleSink 从 Logger 读取日志
  • 删除了 editor/src/Managers/LogSystem.h/cppeditor/src/Core/LogEntry.h
  • 命名规范修复SelectionManagerImpl → SelectionManagerEditorContextImpl → EditorContext
  • 删除了未使用的 SceneManagerImpl 和 ISceneManager
  • ProjectManager 单例移除:创建了 IProjectManager 接口ProjectManager 实现该接口EditorContext 拥有实例
  • ISceneManager 接口:创建了 ISceneManager 接口SceneManager 实现该接口
  • IEditorContext::GetSceneManager() 返回类型:从 void* 改为 ISceneManager&
  • HierarchyPanel EventBus 订阅HierarchyPanel 现在订阅 SelectionChangedEvent

阶段二:核心功能(第 3-4 周)

任务 依赖 状态
Undo/Redo 系统 事件总线 TODO
Command 基类 - TODO
常用 Command 实现 Undo/Redo 系统 TODO
场景序列化 Command 基类 TODO
SceneManager 重构 事件总线、Command TODO

阶段三Inspector 改进(第 5-6 周)

任务 依赖 状态
ComponentEditor 接口 阶段一完成 TODO
TransformComponentEditor ComponentEditor 接口 TODO
MeshRendererComponentEditor AssetHandle 系统 TODO
动态添加组件 UI ComponentEditor 接口 TODO

阶段四SceneView 实现(第 7-8 周)

任务 依赖 状态
CameraController - TODO
GizmosRenderer CameraController TODO
Selection Outline GizmosRenderer TODO
Grid Renderer - TODO

阶段五Play/Edit 模式(第 9-10 周)

任务 依赖 状态
EditorStateMachine 阶段一完成 TODO
场景状态保存/恢复 序列化 TODO
GameView 集成 StateMachine TODO
工具栏 UI GizmosRenderer TODO

五、注意事项

5.1 向后兼容

  • 保持 GameObjectComponent 等核心类接口不变
  • 新增接口使用 virtual 或默认实现
  • 序列化格式预留 version 字段

5.2 性能考虑

  • Undo/Redo 栈限制最大容量
  • 事件总线使用 std::functionstd::any 存储避免类型擦除开销
  • Inspector 使用 std::unordered_map 加速组件编辑器查找

5.3 测试计划

  • 单元测试Command、Serializer、EventBus
  • 集成测试Undo/Redo 完整流程
  • UI 测试:手动测试为主

六、代码规范

6.1 命名规范

  • Editor 相关类放在 XCEngine::Editor 命名空间
  • 接口类前缀 I(如 ISelectionManager
  • Editor 内部实现后缀 Impl(如 SelectionManagerImpl

6.2 文件组织

editor/src/
├── Core/
│   ├── EditorContext.h/cpp
│   ├── EventBus.h/cpp
│   └── UndoRedo.h/cpp
├── Commands/
│   ├── Command.h
│   ├── CreateEntityCommand.h/cpp
│   ├── DeleteEntityCommand.h/cpp
│   └── TransformCommand.h/cpp
├── Panels/
│   ├── Panel.h          # 基础类
│   ├── HierarchyPanel.h/cpp
│   ├── InspectorPanel.h/cpp
│   └── ...
└── UI/
    ├── Gizmos.h/cpp
    └── CameraController.h/cpp

6.3 禁止事项

  • 禁止在 Editor 代码中使用 static 单例模式
  • 禁止直接持有 GameObject* 裸指针
  • 禁止在 Release 版本保留 Debug::Logger 调用
  • 禁止硬编码任何路径