- Created EditorConsoleSink (implements ILogSink interface) - EditorConsoleSink stores logs in memory buffer (max 1000 entries) - Added to Debug::Logger in Application::Initialize() - ConsolePanel now reads from EditorConsoleSink via static GetInstance() - Removed separate LogSystem singleton - Removed editor/src/Core/LogEntry.h (no longer needed) Now Editor and Engine share the same Debug::Logger, with ConsolePanel displaying logs via EditorConsoleSink.
1056 lines
38 KiB
Markdown
1056 lines
38 KiB
Markdown
# UI-Editor 设计与实现文档
|
||
|
||
> **版本**: 1.0
|
||
> **日期**: 2026-03-20
|
||
> **目标**: 为 XCEngine 游戏引擎搭建 UI 编辑器框架
|
||
|
||
---
|
||
|
||
## 第一章 概述
|
||
|
||
### 1.1 UI编辑器目标与定位
|
||
|
||
UI 编辑器是 XCEngine 游戏引擎的编辑器前端,用于可视化编辑游戏场景、场景层级、检视组件属性、控制台输出等核心功能。
|
||
|
||
**核心目标**:
|
||
1. 提供独立的 UI 编辑器可执行程序(基于 ImGui + D3D12)
|
||
2. UI 编辑器的数据结构与引擎核心类型对齐
|
||
3. 引擎核心已有的类型直接复用,引擎核心未实现的则在 UI 编辑器中临时实现但保持接口一致
|
||
4. 支持场景层级管理、实体创建删除、组件编辑等基础功能
|
||
|
||
**定位**:
|
||
- UI 编辑器作为引擎的独立模块,不直接耦合引擎渲染管线
|
||
- 通过 Engine 核心的抽象接口与引擎通信
|
||
- 当前阶段以独立开发为主,待引擎核心完善后进行对接
|
||
|
||
### 1.2 与引擎核心的对齐策略
|
||
|
||
| Engine 类型 | 状态 | UI 编辑器策略 |
|
||
|------------|------|-------------|
|
||
| `XCEngine::Core::Event<T>` | ✅ 已实现 | 直接复用 |
|
||
| `XCEngine::Core::uint64` | ✅ 已实现 | 直接复用 |
|
||
| `XCEngine::Debug::Logger` | ✅ 已实现 | 直接复用 |
|
||
| `XCEngine::Debug::LogLevel` | ✅ 已实现 | 直接复用 |
|
||
| `XCEngine::Debug::LogEntry` | ✅ 已实现 | 直接复用 |
|
||
| `XCEngine::Math::Vector3` 等 | 仅有设计文档 | UI 编辑器临时实现但接口对齐 |
|
||
| `Component` | 仅有设计文档 | UI 编辑器自行实现 `UI::Component` |
|
||
| `GameObject` | 仅有设计文档 | UI 编辑器自行实现 `UI::Entity` |
|
||
| `TransformComponent` | 仅有设计文档 | UI 编辑器自行实现 `UI::TransformComponent` |
|
||
| `Scene` / `SceneManager` | 仅有设计文档 | UI 编辑器自行实现 `UI::SceneManager` |
|
||
|
||
**对齐原则**:
|
||
- Engine 已有类型:直接 `#include <XCEngine/...>` 并使用
|
||
- Engine 暂无类型:在 UI 编辑器中实现,但类名、成员变量、成员函数命名与设计文档保持一致
|
||
- Engine 实现后:UI 编辑器可平滑迁移到 Engine 类型
|
||
|
||
### 1.3 技术选型
|
||
|
||
**渲染后端:D3D12**
|
||
- 与引擎核心渲染管线技术栈一致
|
||
- 支持多窗口、多渲染目标
|
||
- 提供高性能的 GPU 命令提交
|
||
|
||
**UI 框架:Dear ImGui**
|
||
- 轻量级、可扩展的即时模式 UI
|
||
- 支持主题定制
|
||
- 成熟的 D3D12 集成示例
|
||
- 高效的 UI 渲染管线
|
||
|
||
**架构模式:模块化 Manager + Panel**
|
||
- Manager:负责数据管理和业务逻辑
|
||
- Panel:负责 UI 展示和用户交互
|
||
- 清晰的职责分离便于维护和扩展
|
||
|
||
---
|
||
|
||
## 第二章 架构设计
|
||
|
||
### 2.1 整体架构图
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
||
│ UI 编辑器架构 │
|
||
└─────────────────────────────────────────────────────────────────────────────────────┘
|
||
|
||
┌─────────────────┐
|
||
│ Application │ 应用程序入口
|
||
│ (D3D12 + ImGui)│
|
||
└────────┬────────┘
|
||
│
|
||
┌─────────────────────────────────┼─────────────────────────────────┐
|
||
│ │ │
|
||
▼ ▼ ▼
|
||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||
│ Managers │ │ Panels │ │ Core Types │
|
||
│ │ │ │ │ │
|
||
│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │
|
||
│ │SceneManager │ │ │ │HierarchyPanel│ │ │ │ Entity │ │
|
||
│ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │
|
||
│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │
|
||
│ │SelectionMgr │ │◀──────────────│ │InspectorPanel│ │ │ │ Component │ │
|
||
│ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │
|
||
│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │
|
||
│ │ LogSystem │ │ │ │SceneViewPanel│ │ │ │ Theme │ │
|
||
│ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │
|
||
│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ │
|
||
│ │ProjectManager│ │ │ │GameViewPanel │ │ │ │
|
||
│ └─────────────┘ │ │ └─────────────┘ │ │ │
|
||
│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ │
|
||
│ │ AssetItem │ │ │ │ConsolePanel │ │ │ │
|
||
│ └─────────────┘ │ │ └─────────────┘ │ │ │
|
||
│ │ │ ┌─────────────┐ │ │ │
|
||
│ │ │ │ ProjectPanel│ │ │ │
|
||
│ │ │ └─────────────┘ │ │ │
|
||
│ │ │ ┌─────────────┐ │ │ │
|
||
│ │ │ │ MenuBar │ │ │ │
|
||
│ │ │ └─────────────┘ │ │ │
|
||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||
│ │
|
||
│ ┌───────────────────────┘
|
||
│ │
|
||
▼ ▼
|
||
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
||
│ Engine Core (XCEngine) │
|
||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │Event<T> │ │Logger │ │LogLevel │ │LogEntry │ │
|
||
│ │(已实现复用) │ │(已实现复用) │ │(已实现复用) │ │(已实现复用) │ │
|
||
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
|
||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │Math Types │ │Component │ │GameObject │ │Scene │ │
|
||
│ │(设计文档) │ │(设计文档) │ │(设计文档) │ │(设计文档) │ │
|
||
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 2.2 目录结构
|
||
|
||
```
|
||
ui_editor/
|
||
├── CMakeLists.txt # 构建配置
|
||
├── bin/ # 编译输出
|
||
│ └── Release/
|
||
│ └── XCVolumeRendererUI2.exe
|
||
├── build/ # 中间文件
|
||
└── src/
|
||
├── main.cpp # 程序入口
|
||
├── Application.h/cpp # 应用程序类(D3D12 + ImGui)
|
||
│
|
||
├── Core/ # 核心数据类型
|
||
│ ├── GameObject.h # Entity、Component、TransformComponent
|
||
│ ├── LogEntry.h # 日志条目(复用 Engine LogLevel)
|
||
│ └── AssetItem.h # 资产条目
|
||
│
|
||
├── Managers/ # 管理器系统
|
||
│ ├── SceneManager.h/cpp # 场景管理
|
||
│ ├── SelectionManager.h # 选择管理
|
||
│ ├── LogSystem.h/cpp # 日志系统
|
||
│ └── ProjectManager.h/cpp # 项目管理
|
||
│
|
||
├── panels/ # UI 面板
|
||
│ ├── Panel.h/cpp # 面板基类
|
||
│ ├── MenuBar.h/cpp # 菜单栏
|
||
│ ├── HierarchyPanel.h/cpp # 层级面板
|
||
│ ├── InspectorPanel.h/cpp # 检视面板
|
||
│ ├── SceneViewPanel.h/cpp # 场景视图
|
||
│ ├── GameViewPanel.h/cpp # 游戏视图
|
||
│ ├── ConsolePanel.h/cpp # 控制台面板
|
||
│ └── ProjectPanel.h/cpp # 项目面板
|
||
│
|
||
└── Theme.h/cpp # 主题样式
|
||
```
|
||
|
||
### 2.3 核心类型与引擎核心的对齐映射表
|
||
|
||
| UI 编辑器类型 | 文件位置 | 对齐的 Engine 类型 | 说明 |
|
||
|-------------|---------|------------------|------|
|
||
| `UI::Entity` | Core/GameObject.h | `XCEngine::GameObject` | 实体,包含 ID、名称、层级关系、组件 |
|
||
| `UI::Component` | Core/GameObject.h | `XCEngine::Component` | 组件基类 |
|
||
| `UI::TransformComponent` | Core/GameObject.h | `XCEngine::TransformComponent` | 变换组件 |
|
||
| `UI::MeshRendererComponent` | Core/GameObject.h | `XCEngine::RenderMeshComponent` | 网格渲染组件 |
|
||
| `UI::EntityID` | Core/GameObject.h | `uint64_t` | 实体唯一标识符 |
|
||
| `UI::INVALID_ENTITY_ID` | Core/GameObject.h | N/A | 无效实体 ID 常量 |
|
||
| `UI::SceneManager` | Managers/SceneManager.h | `XCEngine::SceneManager` | 场景管理器 |
|
||
| `UI::LogSystem` | Managers/LogSystem.h | `XCEngine::Debug::Logger` | 日志系统 |
|
||
| `UI::LogEntry` | Core/LogEntry.h | `XCEngine::Debug::LogEntry` | 日志条目 |
|
||
| `XCEngine::Core::Event<T>` | Engine/include/... | N/A | 直接复用引擎事件系统 |
|
||
|
||
---
|
||
|
||
## 第三章 核心数据类型
|
||
|
||
### 3.1 Entity(对齐 Engine GameObject)
|
||
|
||
`Entity` 是 UI 编辑器中的核心对象类型,对齐 Engine 的 `GameObject` 设计。
|
||
|
||
**设计文件**:`ui_editor/src/Core/GameObject.h`
|
||
|
||
```cpp
|
||
namespace UI {
|
||
|
||
using EntityID = uint64_t;
|
||
constexpr EntityID INVALID_ENTITY_ID = 0;
|
||
|
||
class Entity {
|
||
public:
|
||
EntityID id = INVALID_ENTITY_ID;
|
||
std::string name;
|
||
EntityID parent = INVALID_ENTITY_ID; // 父实体 ID
|
||
std::vector<EntityID> children; // 子实体 ID 列表
|
||
std::vector<std::unique_ptr<Component>> components;
|
||
bool selected = false;
|
||
|
||
template<typename T, typename... Args>
|
||
T* AddComponent(Args&&... args) {
|
||
auto comp = std::make_unique<T>(std::forward<Args>(args)...);
|
||
comp->m_entity = this;
|
||
T* ptr = comp.get();
|
||
components.push_back(std::move(comp));
|
||
return ptr;
|
||
}
|
||
|
||
template<typename T>
|
||
T* GetComponent() {
|
||
for (auto& comp : components) {
|
||
if (auto casted = dynamic_cast<T*>(comp.get())) {
|
||
return casted;
|
||
}
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
template<typename T>
|
||
std::vector<T*> GetComponents() {
|
||
std::vector<T*> result;
|
||
for (auto& comp : components) {
|
||
if (auto casted = dynamic_cast<T*>(comp.get())) {
|
||
result.push_back(casted);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
};
|
||
|
||
} // namespace UI
|
||
```
|
||
|
||
**关键设计决策**:
|
||
|
||
1. **EntityID 类型**:使用 `uint64_t` 作为实体唯一标识,与 Engine 的 `uint64_t` 一致
|
||
2. **父子关系**:通过 `parent` ID 和 `children` ID 列表维护层级关系,避免循环引用
|
||
3. **组件存储**:使用 `std::vector<std::unique_ptr<Component>>` 存储组件,支持多态
|
||
4. **选择状态**:`selected` 成员用于追踪实体是否被选中
|
||
|
||
**与 Engine GameObject 的接口对齐**:
|
||
|
||
| UI::Entity 接口 | Engine::GameObject 接口 | 对齐状态 |
|
||
|---------------|------------------------|---------|
|
||
| `id` | (无直接对应,Engine 用指针) | 接口不同但功能一致 |
|
||
| `name` | `name` | ✅ 完全对齐 |
|
||
| `parent` | `GetParent()/SetParent()` | ✅ 功能对齐 |
|
||
| `children` | `GetChildren()` | ✅ 功能对齐 |
|
||
| `AddComponent<T>()` | `AddComponent<T>()` | ✅ 接口对齐 |
|
||
| `GetComponent<T>()` | `GetComponent<T>()` | ✅ 接口对齐 |
|
||
|
||
### 3.2 Component(对齐 Engine Component)
|
||
|
||
**设计文件**:`ui_editor/src/Core/GameObject.h`
|
||
|
||
```cpp
|
||
class Component {
|
||
public:
|
||
virtual ~Component() = default;
|
||
virtual std::string GetName() const = 0;
|
||
|
||
virtual void Awake() {}
|
||
virtual void Start() {}
|
||
virtual void Update(float deltaTime) {}
|
||
virtual void OnDestroy() {}
|
||
|
||
class Entity* GetEntity() const { return m_entity; }
|
||
bool IsEnabled() const { return m_enabled; }
|
||
void SetEnabled(bool enabled) { m_enabled = enabled; }
|
||
|
||
protected:
|
||
class Entity* m_entity = nullptr;
|
||
bool m_enabled = true;
|
||
|
||
friend class Entity;
|
||
};
|
||
```
|
||
|
||
**生命周期函数**:对齐 Engine Component 的设计,包含 `Awake()`、`Start()`、`Update()`、`OnDestroy()` 等。
|
||
|
||
**TransformComponent 实现**:
|
||
|
||
```cpp
|
||
class TransformComponent : public Component {
|
||
public:
|
||
float position[3] = {0.0f, 0.0f, 0.0f};
|
||
float rotation[3] = {0.0f, 0.0f, 0.0f};
|
||
float scale[3] = {1.0f, 1.0f, 1.0f};
|
||
|
||
std::string GetName() const override { return "Transform"; }
|
||
};
|
||
```
|
||
|
||
**MeshRendererComponent 实现**:
|
||
|
||
```cpp
|
||
class MeshRendererComponent : public Component {
|
||
public:
|
||
std::string materialName = "Default-Material";
|
||
std::string meshName = "";
|
||
|
||
std::string GetName() const override { return "Mesh Renderer"; }
|
||
};
|
||
```
|
||
|
||
### 3.3 ComponentRegistry(组件注册表)
|
||
|
||
组件注册表用于将组件类型与对应的 ImGui 检视函数关联。
|
||
|
||
```cpp
|
||
using ComponentInspectorFn = std::function<void(Component*)>;
|
||
|
||
struct ComponentInspectorInfo {
|
||
std::string name;
|
||
ComponentInspectorFn renderFn;
|
||
};
|
||
|
||
class ComponentRegistry {
|
||
public:
|
||
static ComponentRegistry& Get() {
|
||
static ComponentRegistry instance;
|
||
return instance;
|
||
}
|
||
|
||
template<typename T>
|
||
void RegisterComponent(const std::string& name, ComponentInspectorFn inspectorFn) {
|
||
m_inspectors[name] = {name, inspectorFn};
|
||
m_factories[name] = []() -> std::unique_ptr<Component> {
|
||
return std::make_unique<T>();
|
||
};
|
||
}
|
||
|
||
ComponentInspectorInfo* GetInspector(const std::string& name) {
|
||
auto it = m_inspectors.find(name);
|
||
if (it != m_inspectors.end()) {
|
||
return &it->second;
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
private:
|
||
ComponentRegistry() = default;
|
||
std::unordered_map<std::string, ComponentInspectorInfo> m_inspectors;
|
||
std::unordered_map<std::string, std::function<std::unique_ptr<Component>()>> m_factories;
|
||
};
|
||
```
|
||
|
||
**注册示例**(在 `InspectorPanel.cpp` 中):
|
||
|
||
```cpp
|
||
ComponentRegistry::Get().RegisterComponent<TransformComponent>(
|
||
"Transform",
|
||
[](Component* comp) {
|
||
auto* transform = dynamic_cast<TransformComponent*>(comp);
|
||
ImGui::InputFloat3("Position", transform->position);
|
||
ImGui::InputFloat3("Rotation", transform->rotation);
|
||
ImGui::InputFloat3("Scale", transform->scale);
|
||
}
|
||
);
|
||
```
|
||
|
||
### 3.4 SceneManager(对齐 Engine SceneManager)
|
||
|
||
**设计文件**:`ui_editor/src/Managers/SceneManager.h`
|
||
|
||
```cpp
|
||
class SceneManager {
|
||
public:
|
||
static SceneManager& Get() {
|
||
static SceneManager instance;
|
||
return instance;
|
||
}
|
||
|
||
EntityID CreateEntity(const std::string& name, EntityID parent = INVALID_ENTITY_ID);
|
||
|
||
Entity* GetEntity(EntityID id) {
|
||
auto it = m_entities.find(id);
|
||
if (it != m_entities.end()) {
|
||
return &it->second;
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
const std::vector<EntityID>& GetRootEntities() const {
|
||
return m_rootEntities;
|
||
}
|
||
|
||
void DeleteEntity(EntityID id);
|
||
void RenameEntity(EntityID id, const std::string& newName);
|
||
void CopyEntity(EntityID id);
|
||
EntityID PasteEntity(EntityID parent = INVALID_ENTITY_ID);
|
||
EntityID DuplicateEntity(EntityID id);
|
||
void MoveEntity(EntityID id, EntityID newParent);
|
||
void CreateDemoScene();
|
||
|
||
XCEngine::Core::Event<EntityID> OnEntityCreated;
|
||
XCEngine::Core::Event<EntityID> OnEntityDeleted;
|
||
XCEngine::Core::Event<EntityID> OnEntityChanged;
|
||
XCEngine::Core::Event<> OnSceneChanged;
|
||
|
||
private:
|
||
EntityID m_nextEntityId = 1;
|
||
std::unordered_map<EntityID, Entity> m_entities;
|
||
std::vector<EntityID> m_rootEntities;
|
||
std::optional<ClipboardData> m_clipboard;
|
||
};
|
||
```
|
||
|
||
**关键设计决策**:
|
||
|
||
1. **Singleton 模式**:通过 `static SceneManager& Get()` 提供全局访问点
|
||
2. **实体存储**:`std::unordered_map<EntityID, Entity>` 提供 O(1) 的实体查找
|
||
3. **根实体列表**:`m_rootEntities` 维护所有顶层实体,用于层级面板展示
|
||
4. **剪贴板支持**:`std::optional<ClipboardData>` 支持复制/粘贴操作
|
||
5. **事件系统**:复用 Engine 的 `Event<T>` 实现观察者模式
|
||
|
||
**事件类型说明**:
|
||
|
||
| 事件 | 签名 | 触发时机 |
|
||
|-----|------|---------|
|
||
| `OnEntityCreated` | `Event<EntityID>` | 创建新实体时 |
|
||
| `OnEntityDeleted` | `Event<EntityID>` | 删除实体时 |
|
||
| `OnEntityChanged` | `Event<EntityID>` | 实体属性变化时(重命名、父子关系变化) |
|
||
| `OnSceneChanged` | `Event<>` | 场景整体变化时 |
|
||
|
||
---
|
||
|
||
## 第四章 面板系统
|
||
|
||
### 4.1 Panel 基类
|
||
|
||
所有面板继承自 `Panel` 基类,提供统一的接口。
|
||
|
||
**设计文件**:`ui_editor/src/panels/Panel.h`
|
||
|
||
```cpp
|
||
class Panel {
|
||
public:
|
||
virtual ~Panel() = default;
|
||
|
||
virtual void Initialize() {}
|
||
virtual void Render() = 0;
|
||
virtual void OnSceneChanged() {}
|
||
|
||
const char* GetName() const { return m_name; }
|
||
bool IsVisible() const { return m_visible; }
|
||
void SetVisible(bool visible) { m_visible = visible; }
|
||
|
||
protected:
|
||
const char* m_name = "Panel";
|
||
bool m_visible = true;
|
||
};
|
||
```
|
||
|
||
**ImGui 窗口包装示例**:
|
||
|
||
```cpp
|
||
void HierarchyPanel::Render() {
|
||
if (!m_visible) return;
|
||
|
||
ImGui::PushID(GetName());
|
||
if (ImGui::Begin(GetName(), &m_visible, ImGuiWindowFlags_NoCollapse)) {
|
||
RenderContent();
|
||
}
|
||
ImGui::End();
|
||
ImGui::PopID();
|
||
}
|
||
```
|
||
|
||
### 4.2 HierarchyPanel(层级面板)
|
||
|
||
**职责**:展示场景层级结构,支持实体的创建、删除、拖拽排序、右键菜单。
|
||
|
||
**设计文件**:`ui_editor/src/panels/HierarchyPanel.h`
|
||
|
||
```cpp
|
||
class HierarchyPanel : public Panel {
|
||
public:
|
||
void Initialize() override;
|
||
void OnSceneChanged() override;
|
||
|
||
private:
|
||
void RenderContent();
|
||
void RenderEntity(EntityID entity, int& nodeIndex);
|
||
void RenderContextMenu(EntityID entity);
|
||
void RenderCreateMenu();
|
||
};
|
||
```
|
||
|
||
**核心功能**:
|
||
|
||
1. **树形结构展示**:递归渲染实体及其子实体
|
||
2. **选中高亮**:选中的实体以高亮显示
|
||
3. **拖拽排序**:支持通过拖拽改变实体顺序和父子关系
|
||
4. **右键菜单**:创建、删除、复制、粘贴、重命名等操作
|
||
5. **搜索过滤**:支持按名称搜索实体
|
||
|
||
**节点渲染流程**:
|
||
|
||
```
|
||
RenderEntity(EntityID id)
|
||
│
|
||
├── 获取实体指针 GetEntity(id)
|
||
│
|
||
├── 构建节点标签 [图标] [名称] [子节点数量]
|
||
│
|
||
├── 选中状态处理
|
||
│ └── 点击时调用 SelectionManager::Select(id)
|
||
│
|
||
├── 展开/折叠状态
|
||
│
|
||
├── 渲染子节点(递归)
|
||
│ └── for child in entity.children: RenderEntity(child)
|
||
│
|
||
└── 渲染右键菜单(如果右键)
|
||
```
|
||
|
||
### 4.3 InspectorPanel(检视面板)
|
||
|
||
**职责**:展示选中实体的所有组件及其属性,支持编辑。
|
||
|
||
**设计文件**:`ui_editor/src/panels/InspectorPanel.h`
|
||
|
||
```cpp
|
||
class InspectorPanel : public Panel {
|
||
public:
|
||
void Initialize() override;
|
||
void OnSelectionChanged();
|
||
|
||
private:
|
||
void RenderContent();
|
||
void RenderEntityHeader(Entity* entity);
|
||
void RenderComponent(Component* comp);
|
||
void RenderAddComponentMenu();
|
||
};
|
||
```
|
||
|
||
**核心功能**:
|
||
|
||
1. **实体头信息**:显示实体名称,支持直接编辑
|
||
2. **组件列表**:遍历实体的所有组件
|
||
3. **组件检视**:调用 `ComponentRegistry` 获取检视函数并渲染
|
||
4. **添加组件**:显示可添加的组件类型列表
|
||
|
||
**组件检视函数注册示例**:
|
||
|
||
```cpp
|
||
void InspectorPanel::Initialize() {
|
||
ComponentRegistry::Get().RegisterComponent<TransformComponent>(
|
||
"Transform",
|
||
[](Component* comp) {
|
||
auto* transform = dynamic_cast<TransformComponent*>(comp);
|
||
ImGui::InputFloat3("Position", transform->position);
|
||
if (ImGui::InputFloat3("Rotation", transform->rotation)) {
|
||
SceneManager::Get().GetEntity(transform->GetEntity()->id)
|
||
->name = transform->GetEntity()->name; // 触发变更事件
|
||
}
|
||
ImGui::InputFloat3("Scale", transform->scale, 1.0f);
|
||
}
|
||
);
|
||
|
||
ComponentRegistry::Get().RegisterComponent<MeshRendererComponent>(
|
||
"Mesh Renderer",
|
||
[](Component* comp) {
|
||
auto* mesh = dynamic_cast<MeshRendererComponent*>(comp);
|
||
ImGui::InputText("Mesh", &mesh->meshName);
|
||
ImGui::InputText("Material", &mesh->materialName);
|
||
}
|
||
);
|
||
}
|
||
```
|
||
|
||
### 4.4 SceneViewPanel(场景视图)
|
||
|
||
**职责**:渲染场景视图窗口,提供视口控制。
|
||
|
||
**设计文件**:`ui_editor/src/panels/SceneViewPanel.h`
|
||
|
||
**当前实现状态**:基础框架已搭建,待扩展。
|
||
|
||
**计划功能**:
|
||
- 视口渲染(通过 Engine 渲染管线)
|
||
- 相机控制(平移、旋转、缩放)
|
||
- Gizmos 渲染(变换、旋转、缩放工具)
|
||
- 网格和辅助线显示
|
||
|
||
### 4.5 GameViewPanel(游戏视图)
|
||
|
||
**职责**:显示游戏运行时的画面。
|
||
|
||
**设计文件**:`ui_editor/src/panels/GameViewPanel.h`
|
||
|
||
**当前实现状态**:基础框架已搭建,待与 Engine 渲染管线对接。
|
||
|
||
### 4.6 ConsolePanel(控制台)
|
||
|
||
**职责**:显示日志系统收集的日志消息。
|
||
|
||
**设计文件**:`ui_editor/src/panels/ConsolePanel.h/cpp`
|
||
|
||
```cpp
|
||
class ConsolePanel : public Panel {
|
||
public:
|
||
void Initialize() override;
|
||
void Clear();
|
||
void OnLogMessage(const LogEntry& entry);
|
||
|
||
private:
|
||
void RenderContent();
|
||
void RenderLogEntry(const LogEntry& entry);
|
||
void RenderFilterBar();
|
||
|
||
std::vector<LogEntry> m_logs;
|
||
std::vector<LogEntry> m_filteredLogs;
|
||
bool m_autoScroll = true;
|
||
bool m_wordWrap = false;
|
||
XCEngine::Debug::LogLevel m_minLevel = XCEngine::Debug::LogLevel::Verbose;
|
||
};
|
||
```
|
||
|
||
**核心功能**:
|
||
|
||
1. **日志显示**:显示所有收集的日志条目
|
||
2. **级别过滤**:通过下拉菜单筛选日志级别
|
||
3. **自动滚动**:新日志自动滚动到底部
|
||
4. **时间戳显示**:显示每条日志的时间戳
|
||
5. **双击跳转**:双击日志可定位到相关实体(预留)
|
||
|
||
### 4.7 ProjectPanel(项目面板)
|
||
|
||
**职责**:浏览项目资源文件。
|
||
|
||
**设计文件**:`ui_editor/src/panels/ProjectPanel.h/cpp`
|
||
|
||
**当前实现状态**:基础框架已搭建,待扩展资源导入功能。
|
||
|
||
---
|
||
|
||
## 第五章 管理器系统
|
||
|
||
### 5.1 SelectionManager(选择管理器)
|
||
|
||
**设计文件**:`ui_editor/src/Managers/SelectionManager.h`
|
||
|
||
```cpp
|
||
class SelectionManager {
|
||
public:
|
||
static SelectionManager& Get() {
|
||
static SelectionManager instance;
|
||
return instance;
|
||
}
|
||
|
||
void Select(EntityID entity);
|
||
void Deselect(EntityID entity);
|
||
void ToggleSelection(EntityID entity);
|
||
void ClearSelection();
|
||
void SetSelection(const std::vector<EntityID>& entities);
|
||
|
||
bool IsSelected(EntityID entity) const;
|
||
const std::vector<EntityID>& GetSelection() const { return m_selection; }
|
||
size_t GetSelectionCount() const { return m_selection.size(); }
|
||
|
||
XCEngine::Core::Event<EntityID> OnSelected;
|
||
XCEngine::Core::Event<EntityID> OnDeselected;
|
||
XCEngine::Core::Event<> OnSelectionCleared;
|
||
|
||
private:
|
||
SelectionManager() = default;
|
||
std::vector<EntityID> m_selection;
|
||
std::unordered_set<EntityID> m_selectionSet;
|
||
};
|
||
```
|
||
|
||
**关键设计决策**:
|
||
|
||
1. **双向存储**:`m_selection` 保持顺序,`m_selectionSet` 提供 O(1) 查询
|
||
2. **事件系统**:选择变化时触发对应事件
|
||
3. **多选支持**:通过 `SetSelection` 支持批量选择
|
||
|
||
### 5.2 LogSystem(日志系统)
|
||
|
||
**设计文件**:`ui_editor/src/Managers/LogSystem.h/cpp`
|
||
|
||
```cpp
|
||
class LogSystem {
|
||
public:
|
||
static LogSystem& Get() {
|
||
static LogSystem instance;
|
||
return instance;
|
||
}
|
||
|
||
void Initialize();
|
||
void Shutdown();
|
||
|
||
void Log(XCEngine::Debug::LogLevel level, const std::string& message,
|
||
const std::string& file = "", int line = 0);
|
||
|
||
void Info(const std::string& message) { Log(LogLevel::Info, message); }
|
||
void Warning(const std::string& message) { Log(LogLevel::Warning, message); }
|
||
void Error(const std::string& message) { Log(LogLevel::Error, message); }
|
||
|
||
XCEngine::Core::Event<const LogEntry&> OnLog;
|
||
|
||
private:
|
||
LogSystem() = default;
|
||
std::vector<LogEntry> m_logs;
|
||
};
|
||
```
|
||
|
||
**日志流向**:
|
||
|
||
```
|
||
XCEngine::Debug::Logger (Engine)
|
||
│
|
||
▼ (通过自定义 LogSink 拦截)
|
||
UI::LogSystem::OnLog
|
||
│
|
||
▼
|
||
ConsolePanel::OnLogMessage
|
||
│
|
||
▼
|
||
显示到 ConsolePanel
|
||
```
|
||
|
||
### 5.3 ProjectManager(项目管理器)
|
||
|
||
**设计文件**:`ui_editor/src/Managers/ProjectManager.h/cpp`
|
||
|
||
**职责**:
|
||
- 管理项目根目录
|
||
- 扫描和索引资源文件
|
||
- 提供资源查找接口
|
||
|
||
---
|
||
|
||
## 第六章 事件系统
|
||
|
||
### 6.1 引擎 Event<T> 的复用
|
||
|
||
UI 编辑器直接复用 Engine 的事件系统:
|
||
|
||
```cpp
|
||
#include <XCEngine/Core/Event.h>
|
||
|
||
// 使用方式
|
||
XCEngine::Core::Event<EntityID> OnEntityCreated;
|
||
XCEngine::Core::Event<EntityID> OnEntityDeleted;
|
||
XCEngine::Core::Event<EntityID> OnEntityChanged;
|
||
XCEngine::Core::Event<> OnSceneChanged;
|
||
```
|
||
|
||
**订阅示例**:
|
||
|
||
```cpp
|
||
// 在 Panel 初始化时订阅
|
||
SceneManager::Get().OnEntityCreated.AddLambda([this](EntityID id) {
|
||
// 处理实体创建
|
||
});
|
||
```
|
||
|
||
### 6.2 UI 编辑器事件类型
|
||
|
||
| 事件源 | 事件名 | 签名 | 说明 |
|
||
|-------|-------|------|------|
|
||
| `SceneManager` | `OnEntityCreated` | `Event<EntityID>` | 实体创建 |
|
||
| `SceneManager` | `OnEntityDeleted` | `Event<EntityID>` | 实体删除 |
|
||
| `SceneManager` | `OnEntityChanged` | `Event<EntityID>` | 实体属性变化 |
|
||
| `SceneManager` | `OnSceneChanged` | `Event<>` | 场景变化 |
|
||
| `SelectionManager` | `OnSelected` | `Event<EntityID>` | 实体被选中 |
|
||
| `SelectionManager` | `OnDeselected` | `Event<EntityID>` | 实体取消选中 |
|
||
| `SelectionManager` | `OnSelectionCleared` | `Event<>` | 选择被清空 |
|
||
| `LogSystem` | `OnLog` | `Event<const LogEntry&>` | 日志消息 |
|
||
|
||
### 6.3 事件处理时机
|
||
|
||
**同一帧内的事件处理**:
|
||
|
||
```cpp
|
||
void Application::Render() {
|
||
// 1. 处理用户输入
|
||
// 2. 更新 Managers
|
||
SceneManager::Get().Update(deltaTime);
|
||
|
||
// 3. 清空pending列表(ProcessUnsubscribes)
|
||
// 4. 渲染 ImGui UI
|
||
RenderUI();
|
||
|
||
// 5. 渲染 D3D12
|
||
RenderD3D12();
|
||
}
|
||
```
|
||
|
||
**注意**:`Event<T>::ProcessUnsubscribes()` 应在每帧适当位置调用,以确保安全的订阅取消。
|
||
|
||
---
|
||
|
||
## 第七章 渲染集成
|
||
|
||
### 7.1 D3D12 初始化流程
|
||
|
||
**设计文件**:`ui_editor/src/Application.cpp`
|
||
|
||
```
|
||
Initialize(HWND hwnd)
|
||
│
|
||
├── CreateDevice()
|
||
│ ├── D3D12CreateDevice()
|
||
│ ├── CreateCommandQueue()
|
||
│ ├── CreateCommandAllocator()
|
||
│ └── CreateCommandList()
|
||
│
|
||
├── CreateRenderTarget()
|
||
│ ├── CreateDescriptorHeap (RTV)
|
||
│ ├── CreateDescriptorHeap (SRV)
|
||
│ └── CreateRenderTargets (3个双缓冲)
|
||
│
|
||
└── CreateFence()
|
||
```
|
||
|
||
### 7.2 ImGui 渲染管线
|
||
|
||
**初始化**:
|
||
|
||
```cpp
|
||
ImGui_ImplWin32_Init(hwnd);
|
||
ImGui_ImplDX12_Init(
|
||
m_device,
|
||
3, // 双缓冲帧数
|
||
DXGI_FORMAT_R8G8B8A8_UNORM, // 格式
|
||
m_srvHeap,
|
||
m_srvHeap->GetCPUDescriptorHandleForHeapStart(),
|
||
m_srvHeap->GetGPUDescriptorHandleForHeapStart()
|
||
);
|
||
```
|
||
|
||
**渲染**:
|
||
|
||
```cpp
|
||
ImGui_ImplDX12_NewFrame();
|
||
ImGui_ImplWin32_NewFrame();
|
||
ImGui::NewFrame();
|
||
|
||
// 渲染 ImGui UI
|
||
RenderUI();
|
||
|
||
ImGui::EndFrame();
|
||
ImGui::Render();
|
||
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), m_commandList);
|
||
```
|
||
|
||
### 7.3 渲染目标与交换链
|
||
|
||
**配置**:
|
||
|
||
```cpp
|
||
swapChainDesc.Width = width;
|
||
swapChainDesc.Height = height;
|
||
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||
swapChainDesc.BufferCount = 3; // 三重缓冲
|
||
swapChainDesc.SampleDesc.Count = 1;
|
||
```
|
||
|
||
**_present 流程**:
|
||
|
||
```cpp
|
||
m_swapChain->Present(1, 0);
|
||
m_fenceValue++;
|
||
m_commandQueue->Signal(m_fence, m_fenceValue);
|
||
```
|
||
|
||
---
|
||
|
||
## 第八章 主题与样式
|
||
|
||
### 8.1 Theme 系统
|
||
|
||
**设计文件**:`ui_editor/src/Theme.h/cpp`
|
||
|
||
```cpp
|
||
struct Theme {
|
||
ImVec4 colors[ImGuiCol_COUNT];
|
||
|
||
void Apply();
|
||
void LoadDefault();
|
||
void LoadDark();
|
||
void LoadLight();
|
||
};
|
||
|
||
extern Theme g_theme;
|
||
```
|
||
|
||
**默认深色主题配色**:
|
||
|
||
| 用途 | 颜色 (RGB) |
|
||
|-----|-----------|
|
||
| 背景 | (0.1, 0.1, 0.1) |
|
||
| 面板背景 | (0.15, 0.15, 0.15) |
|
||
| 边框 | (0.3, 0.3, 0.3) |
|
||
| 文字 | (0.9, 0.9, 0.9) |
|
||
| 选中 | (0.2, 0.4, 0.8) |
|
||
| 警告 | (0.9, 0.6, 0.2) |
|
||
| 错误 | (0.9, 0.2, 0.2) |
|
||
|
||
---
|
||
|
||
## 第九章 未来扩展计划
|
||
|
||
### 9.1 序列化/反序列化
|
||
|
||
**计划实现**:
|
||
|
||
```cpp
|
||
class SceneSerializer {
|
||
public:
|
||
bool Serialize(Scene* scene, const String& filePath);
|
||
bool Deserialize(Scene* scene, const String& filePath);
|
||
};
|
||
```
|
||
|
||
**文件格式**:JSON 或二进制(待定)
|
||
|
||
### 9.2 撤销/重做系统
|
||
|
||
**计划实现**:
|
||
|
||
```cpp
|
||
class UndoSystem {
|
||
public:
|
||
void PushAction(std::unique_ptr<IAction> action);
|
||
void Undo();
|
||
void Redo();
|
||
bool CanUndo() const;
|
||
bool CanRedo() const;
|
||
|
||
private:
|
||
std::vector<std::unique_ptr<IAction>> m_undoStack;
|
||
std::vector<std::unique_ptr<IAction>> m_redoStack;
|
||
};
|
||
```
|
||
|
||
**支持的操作**:
|
||
- 实体创建/删除
|
||
- 实体重命名
|
||
- 实体移动(父子关系变更)
|
||
- 组件添加/删除
|
||
- 属性修改
|
||
|
||
### 9.3 资源导入管线
|
||
|
||
**计划实现**:
|
||
|
||
1. 资源文件监控(文件变化自动检测)
|
||
2. 资源导入器(Mesh、Texture、Material 等)
|
||
3. 资源依赖解析
|
||
4. 资源热重载
|
||
|
||
### 9.4 预制体系统
|
||
|
||
**计划实现**:
|
||
|
||
```cpp
|
||
class Prefab {
|
||
public:
|
||
String name;
|
||
std::vector<std::unique_ptr<Component>> components;
|
||
std::vector<Prefab> children;
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## 附录 A:核心类型对齐详细对照表
|
||
|
||
| UI 编辑器类型/接口 | Engine 类型/接口 | 文件位置 | 对齐状态 |
|
||
|------------------|----------------|---------|---------|
|
||
| `UI::Entity` | `XCEngine::GameObject` | GameObject.h / GameObject.h | ✅ 接口对齐 |
|
||
| `UI::EntityID` | `uint64_t` | GameObject.h / Types.h | ✅ 完全一致 |
|
||
| `UI::INVALID_ENTITY_ID = 0` | N/A | GameObject.h | ✅ 自定义常量 |
|
||
| `UI::Component` | `XCEngine::Component` | GameObject.h / Component.h | ✅ 接口对齐 |
|
||
| `UI::Component::GetEntity()` | `Component::gameObject()` | GameObject.h / Component.h | ✅ 功能一致 |
|
||
| `UI::TransformComponent` | `XCEngine::TransformComponent` | GameObject.h / TransformComponent.h | ✅ 接口对齐 |
|
||
| `UI::TransformComponent::position` | `TransformComponent::GetPosition()` | GameObject.h / TransformComponent.h | ✅ 成员/方法对齐 |
|
||
| `UI::SceneManager` | `XCEngine::SceneManager` | SceneManager.h / SceneManager.h | ✅ 接口对齐 |
|
||
| `UI::SceneManager::CreateEntity()` | `SceneManager::CreateScene()` (不同) | SceneManager.h | ⚠️ 接口有差异 |
|
||
| `UI::LogSystem` | `XCEngine::Debug::Logger` | LogSystem.h / Logger.h | ✅ 功能复用 |
|
||
| `UI::LogEntry` | `XCEngine::Debug::LogEntry` | LogEntry.h / LogEntry.h | ✅ 复用 Engine |
|
||
| `XCEngine::Core::Event<T>` | N/A | Event.h | ✅ 直接复用 |
|
||
| `XCEngine::Debug::LogLevel` | N/A | LogLevel.h | ✅ 直接复用 |
|
||
|
||
---
|
||
|
||
## 附录 B:事件类型清单
|
||
|
||
### SceneManager 事件
|
||
|
||
| 事件名 | 类型 | 参数 | 触发时机 |
|
||
|-------|------|------|---------|
|
||
| `OnEntityCreated` | `Event<EntityID>` | 新实体的 ID | `CreateEntity()` 成功后 |
|
||
| `OnEntityDeleted` | `Event<EntityID>` | 被删实体的 ID | `DeleteEntity()` 成功后 |
|
||
| `OnEntityChanged` | `Event<EntityID>` | 变化实体的 ID | `RenameEntity()`、`MoveEntity()` 等 |
|
||
| `OnSceneChanged` | `Event<>` | 无 | 场景整体变化时 |
|
||
|
||
### SelectionManager 事件
|
||
|
||
| 事件名 | 类型 | 参数 | 触发时机 |
|
||
|-------|------|------|---------|
|
||
| `OnSelected` | `Event<EntityID>` | 被选实体的 ID | `Select()` 时 |
|
||
| `OnDeselected` | `Event<EntityID>` | 取消选中的实体 ID | `Deselect()` 时 |
|
||
| `OnSelectionCleared` | `Event<>` | 无 | `ClearSelection()` 时 |
|
||
|
||
### LogSystem 事件
|
||
|
||
| 事件名 | 类型 | 参数 | 触发时机 |
|
||
|-------|------|------|---------|
|
||
| `OnLog` | `Event<const LogEntry&>` | 日志条目引用 | 任意日志产生时 |
|
||
|
||
---
|
||
|
||
## 附录 C:面板布局配置
|
||
|
||
**默认布局**(通过 ImGui Dockspace 实现):
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────────────────────┐
|
||
│ MenuBar │
|
||
├──────────────┬─────────────────────────────────┬─────────────────┤
|
||
│ │ │ │
|
||
│ Hierarchy │ SceneView │ Inspector │
|
||
│ Panel │ Panel │ Panel │
|
||
│ │ │ │
|
||
│ (200px) │ (flex) │ (250px) │
|
||
│ │ │ │
|
||
├──────────────┼─────────────────────────────────┼─────────────────┤
|
||
│ │ │ │
|
||
│ Project │ GameView │ Console │
|
||
│ Panel │ Panel │ Panel │
|
||
│ │ │ │
|
||
│ (200px) │ (flex) │ (150px) │
|
||
│ │ │ │
|
||
└──────────────┴─────────────────────────────────┴─────────────────┘
|
||
```
|
||
|
||
**Dock 优先级**:
|
||
1. HierarchyPanel - 左上
|
||
2. SceneViewPanel - 中上
|
||
3. InspectorPanel - 右上
|
||
4. ProjectPanel - 左下
|
||
5. GameViewPanel - 中下
|
||
6. ConsolePanel - 右下
|
||
|
||
---
|
||
|
||
**文档结束**
|