编辑作用域:撤销分栈不等于拥有完整会话

自动关联目录:编辑作用域:撤销分栈不等于拥有完整会话

核心判断:场景与 Prefab 使用不同 Undo 栈避免了历史串台,但真正的编辑作用域还必须拥有选择、Gizmo、相机、预览 World、Dirty Revision 和异步 generation;只拆历史仍是半个会话模型。

一、业务场景击穿了“当前编辑器只有一个上下文”

用户可以在主场景、Prefab 隔离视图和运行预览之间切换。一次属性写入的对象、选择反馈、相机位置和撤销目标必须属于同一文档。若 Undo 已分栈,而选择、网格吸附、Gizmo 或数据通道仍是全局状态,切换后就会出现“撤销正确,但视图操作了另一个上下文”。

工程代码已经明确把场景与 Prefab 历史分开,也明确说明这个 scope 目前只约束 Undo,并不自动约束其他编辑状态。这是一个重要且诚实的边界,不应在复盘中把它包装成完整多文档架构。

二、目标模型:EditorSession 是状态所有者

struct EditorSession {
    EditSessionId id;
    Generation generation;
    DocumentId document;
    PreviewWorldHandle world;
    SelectionModel selection;
    GizmoState gizmo;
    CameraBookmark camera;
    GridPolicy grid;
    HistoryStack history;
    Revision savedRevision;
    Revision currentRevision;
};

所有前端命令必须显式解析到一个 Session;异步资产回调、属性面板事件和脚本工具也携带 SessionId + generation。Dirty 状态由 currentRevision != savedRevision 推导,不能由散落的布尔标记拼接。

三、模型、视图、命令的边界

内容模型只保存可发布事实;选择、相机和 Gizmo 是会话态;命令读取二者并产生结构化 Change;UE Actor、Component 和 Slate Widget 是可重建投影。Prefab 隔离编辑使用独立预览 World 时,不能把其运行时指针写回主文档。

四、代价与验证

显式会话会让许多过去无参获取“当前编辑器”的 API 增加参数,也会暴露隐藏的全局依赖。但迁移可以从日志化隐式访问开始,再逐步要求调用者传 Session。

验证应同时打开两个文档,交错执行选择、拖拽、Undo、异步加载和关闭重开,证明任何迟到事件都无法改变另一个 Session。复盘后的提升是:作用域不是一个枚举,而是一组必须同生共死的编辑状态。