Files
XCEngine/docs/api/_guides/Editor/Editor-Architecture-And-Workflow.md

14 KiB
Raw Blame History

Editor Architecture And Workflow

先建立正确心智模型

docs/api/XCEngine/Editor/** 对应的是编辑器应用层,而不是引擎运行时 public API。

当前这套 Editor 已经不是“只有窗口壳和几块早期 ImGui 面板”的状态。按 editor/src/**project/ 和当前脚本程序集目录来看,它已经形成了一条真实闭环:

Application -> EditorContext -> Panels / Actions / Commands -> ViewportHostService -> Rendering / ResourceManager / ScriptEngine

同时还和项目目录结构直接对接:

  • Project.xcproject
  • Assets/
  • .meta
  • Library/SourceAssetDB
  • Library/ArtifactDB
  • Library/Artifacts
  • Library/ScriptAssemblies

因此这组文档的重点,不是“介绍一个理想中的未来编辑器”,而是解释当前 checkout 下这条链路已经怎么工作。

顶层宿主是怎样启动的

Application 是编辑器进程的组合根。

它的启动顺序大致是:

  1. 安装崩溃过滤器并把日志重定向到可执行目录。
  2. 通过 ResolveEditorProjectRootUtf8() 解析项目根目录。
  3. 若命令行带 --project-p,优先使用覆盖路径。
  4. 否则在工作目录或可执行目录向上查找 workspace 根,再优先落到 <workspace>/project/
  5. 初始化主窗口渲染器、ResourceManagerEditorContext、脚本运行时、ImGui 和 ViewportHostService
  6. 最后挂接 EditorLayer,开始每帧驱动。

这里最关键的一点是:项目根目录不是由某个面板临时决定,而是宿主层在启动时统一解析并固定下来。当前仓库默认就是随仓维护的 project/ 示例工程。

当前 Editor 的六层结构

可以把当前实现理解成六层。

1. 宿主层

  • Application
  • Platform
  • D3D12WindowRenderer
  • ImGuiBackendBridge
  • ImGuiSession

这一层负责窗口、SwapChain、ImGui 帧边界、项目切换和脚本运行时重载。它不直接实现 Hierarchy、Inspector 或 Project Browser 的业务规则。

2. 上下文与共享服务层

  • EditorContext
  • EventBus
  • SelectionManager
  • SceneManager
  • ProjectManager
  • UndoManager

EditorContext 是当前 Editor 的状态容器。它把事件、选择、场景、项目、撤销和 ViewportHostService 指针聚合起来,供面板和动作层统一访问。

3. 面板与布局层

  • PanelCollection
  • HierarchyPanel
  • ProjectPanel
  • InspectorPanel
  • SceneViewPanel
  • GameViewPanel
  • ConsolePanel

这一层负责“用户看到什么”,以及把即时模式 UI 交互采样成更稳定的意图。

其中 GameViewPanel 还有一条额外职责:它会把视口里的键鼠状态采样成 GameViewInputFrameEvent,再通过 PlaySessionController 桥接到运行时 InputManager。这一条链路可以单独看:

4. 动作与命令层

  • Actions::*ActionRouter
  • Commands::*

这层负责把“菜单点击、快捷键、右键菜单、拖放、行内重命名”翻译成统一的编辑语义。典型例子是:

  • HierarchyActionRouter
  • EntityCommands
  • ProjectCommands

5. 视口宿主层

  • ViewportHostService
  • SceneViewportOverlayProviders
  • SceneViewportOverlayBuilder
  • SceneViewportEditorOverlayPass
  • SceneViewportPicker

它把 Scene/Game 视口真正接到引擎的 Rendering + RHI 上,同时承接 object-id picking、outline、grid 和世界空间 overlay。

6. 引擎与资源层

  • Rendering
  • ResourceManager
  • AssetDatabase
  • ScriptEngine
  • MonoScriptRuntime

这一层不属于 Editor 自身,但 Editor 当前已经大量依赖它们提供真实能力,而不是自己维护一套独立“简化版运行时”。

EditorActionRoute 为什么重要

很多旧文档容易忽略 EditorActionRoute,但它对当前 Inspector 和焦点行为非常关键。

当前只有三个 route

  • None
  • Hierarchy
  • Project

这不是菜单状态,而是“最近一次明确的编辑焦点来自哪里”。

它直接影响 InspectorPanel 的检查对象:

  • route 是 Hierarchy 时,优先检查当前选中实体。
  • route 是 Project 时,优先检查当前选中资源。
  • 当前只有 Material 资源拥有专用 inspector其他资源走 unsupported fallback。

所以 Inspector 现在已经不是“只给 GameObject 用”的面板,而是一个带 subject 切换能力的通用检查器。

一条典型的 Hierarchy 链路

以“在 Hierarchy 中重命名实体”为例,当前真实链路大致是:

  1. HierarchyPanel 绘制树节点和行内编辑框。
  2. HierarchyActionRouter 负责解释点击、右键、拖放和 rename 请求。
  3. RequestEntityRename(...) 通过 EventBus 发布 EntityRenameRequestedEvent
  4. 行内编辑结束后,CommitEntityRename(...) 调用 EntityCommands
  5. EntityCommands::RenameEntity(...) 通过 UndoUtils::ExecuteSceneCommand(...) 进入撤销栈。
  6. SceneManager 完成真正的场景修改。
  7. 其他面板通过选择状态或事件观察到变化。

这条链路体现了当前 editor 的一个硬边界:

  • 面板负责呈现和采样。
  • router 负责交互语义。
  • commands 负责稳定的编辑命令。
  • manager 负责真正的数据结构修改。

一条典型的 Project 链路

ProjectPanel 不是 AssetDatabase 本身,而是 IProjectManager 的浏览器前端。

当前它的主链路是:

  1. 左侧目录树用 TreeView 绘制 folder hierarchy。
  2. 右侧浏览区用 DrawAssetTile(...) 绘制当前目录条目。
  3. 顶部工具栏只做当前目录内搜索,不做全项目索引。
  4. 面包屑通过 NavigateToIndex(...) 回退到任意路径段。
  5. 重命名、创建文件夹、创建材质、移动资源、删除资源都交给 ProjectCommands
  6. 打开资源、开始拖拽、背景点击等交互交给 Actions 层 helper。

因此当前 ProjectPanel 的定位很清楚:

  • 它负责浏览和编辑入口。
  • 它不自己维护资源数据库。
  • 它依赖 project manager 和命令层维持真实状态。

还要建立一个新的工作流边界认知:

  • Project 面板负责资源浏览与资源级编辑
  • 项目级保存和脚本程序集重建仍由主菜单触发
  • 旧文档里提到的场景资源批量迁移入口已经不在当前 editor 工作流中

Inspector 已经不只是组件列表

InspectorPanel 现在至少有四种 subject mode

  • None
  • GameObject
  • MaterialAsset
  • UnsupportedAsset

GameObject 模式

这一支仍然是最传统的 Inspector

  • 遍历实体组件列表。
  • 通过 ComponentEditorRegistry 找到对应 IComponentEditor
  • BeginComponentSection(...) 组织 section UI。
  • 组件 editor 返回 true 时,标记场景 dirty。
  • Add Component 弹窗走 DeferredPopupStateActions helper。

Material 资源模式

这一支是旧文档最容易写错的地方。当前实现已经支持直接检查材质资源:

  • ProjectManager 取当前选中的 AssetItem
  • 仅当 item->type == "Material" 时进入专用 inspector。
  • 通过项目相对路径调用 ResourceManager::Load<Material>()
  • 当前可编辑:
    • Shader 路径
    • Shader Pass
    • Render Queue
    • Render State
    • Tags
  • 每次修改都会立刻写回材质文件,并尽量把变更同步到已加载的 Material 资源实例。

所以现在 Inspector 已经承担了一部分资源编辑器职责,而不是单纯的 scene component inspector。

ScriptComponent 不是缺席状态

旧表述里常见“Inspector 还没有脚本组件”之类的说法,但当前这已经不成立。

ScriptComponentEditor 已经接入当前 Inspector 链路,支持:

  • 脚本类选择
  • 字段模型读取
  • 字段 override 写回
  • 运行时缺失 / 程序集缺失状态提示
  • Rebuild Scripts / Reload Scripts 入口

脚本程序集链路已经落地

当前脚本相关的关键目录是:

  • managed/XCEngine.ScriptCore/
  • managed/GameScripts/
  • project/Assets/**/*.cs
  • project/Library/ScriptAssemblies/

Application 在启动、切换项目和显式 reload 时,都会把脚本程序集目录固定到:

  • <Project>/Library/ScriptAssemblies/XCEngine.ScriptCore.dll
  • <Project>/Library/ScriptAssemblies/GameScripts.dll
  • <Project>/Library/ScriptAssemblies/mscorlib.dll

若程序集缺失:

  • Editor 不会直接崩溃。
  • ScriptEngine runtime 会被清空。
  • ScriptRuntimeStatus 会保留错误消息,供 Inspector 和其他 UI 展示。

若用户触发 RebuildScriptingAssemblies()

  1. EditorScriptAssemblyBuilder 重建项目程序集。
  2. 成功后立即 ReloadScriptingRuntime()
  3. 新 runtime 会重新注册到 ScriptEngine

这条链路说明当前脚本系统已经进入“编辑器真实工作流的一部分”,而不是实验性质的旁路功能。

视口链路也已经从面板里抽出来了

旧版 editor 很容易把 SceneView 相关逻辑直接堆进面板文件里,但当前实现已经明显走向规范化。

按当前结构,典型流程是:

  1. Application::Render() 建立主帧边界。
  2. LayerStack 和具体面板产生 Scene / Game 视口请求。
  3. ViewportHostService::BeginFrame() 清理并准备本帧视口状态。
  4. RenderRequestedViewports(...) 真正调用引擎 SceneRenderer
  5. Scene View 额外接入:
    • 编辑器私有相机
    • object-id surface
    • outline
    • grid
    • SceneViewportOverlayProviders
    • SceneViewportOverlayBuilder
    • SceneViewportEditorOverlayPass

这意味着:

  • editor 是渲染宿主,不是第二套 renderer。
  • 新的 Scene overlay / gizmo 功能应优先沿 overlay provider -> overlay builder -> overlay pass 扩展。
  • 不应再把世界空间绘制逻辑继续回堆到 SceneViewPanel 的临时 ImGui 路径。

更细一层的 Scene View 输入仲裁、pivot / center、global / local、HUD/world overlay 分层以及 gizmo overlay state 传播关系,可继续看 SceneView Interaction And Gizmo Model

当前项目目录结构不是“伪工作流”

当前工程目录的几个部分都已经参与真实工作流:

  • project/Assets/
  • project/Assets.meta
  • project/Library/
  • project/Library/ScriptAssemblies/
  • Project.xcproject

尤其是 project/Library/,在当前实现里并不只是可以无脑忽略的临时目录。它至少承接:

  • 资源数据库缓存
  • artifact 缓存
  • 脚本程序集输出

因此在 editor 文档里,不应再把 .metaLibrary/ 描述成“未来才会用到”的规划项。

如果要继续扩展当前 Editor应该从哪层下手

新增一个面板

优先顺序通常是:

  1. panels/ 下新建 panel。
  2. 复用已有 PanelWindowScopePanelToolbarScopePanelContentScope
  3. 若面板要参与焦点切换,再接 EditorActionRoute
  4. 若面板会修改项目或场景,优先接命令层而不是直改 manager。

给 Inspector 新增一种检查对象

优先顺序通常是:

  1. 先确认新的 subject 应由哪条 route 驱动。
  2. InspectorPanel::SyncSubject() 里定义切换条件。
  3. 为该资源或组件准备独立渲染函数。
  4. 若涉及文件写回或场景写回,尽量复用现有 Commands / ResourceManager / ScriptEngine

给 Hierarchy / Project 增加新动作

优先顺序通常是:

  1. Actions 层描述入口。
  2. 在 router 里解释 UI 交互。
  3. Commands 层集中写实际修改逻辑。
  4. 需要后续 UI 同步时,再通过 EventBus 发布事件。

现在不该再沿用的旧说法

以下说法已经不再适合当前活跃文档:

  • “Project 面板还不是完整资产导入系统”
  • “Inspector 还没有脚本组件”
  • “Editor 仍然只是轻量编辑器壳层”
  • “脚本程序集和项目目录结构还在规划中”

更准确的描述应当是:

  • 当前 Editor 仍在持续重构,但主链路已经真实落地。
  • 高层结构已经能覆盖项目根目录解析、资源浏览、材质资源检查、脚本程序集重建与重载、Scene/Game 视口宿主和 object-id picking。
  • 接下来的工作重点是继续把这些现有能力文档化、模块化,而不是再把它描述成只有方向没有实现的雏形。

推荐阅读顺序

如果要快速建立当前 Editor 的整体认识,推荐按这个顺序读:

  1. Editor
  2. Application
  3. EventBus
  4. ProjectPanel
  5. InspectorPanel
  6. HierarchyActionRouter
  7. EntityCommands
  8. ViewportHostService
  9. SceneView Interaction And Gizmo Model
  10. ScriptComponentEditor

这样读下来,基本就能把“宿主层、共享状态、面板、动作命令、视口和脚本”这几条主线拼起来。