层级修改:Parent 不是可独立写入的普通属性
自动关联目录:层级修改:Parent 不是可独立写入的普通属性
核心判断:修改
Parent会同时改变拓扑、Transform 语义、选择集、撤销记录和引用有效性。它必须是一条结构事务,不能退化成通用属性面板的一次赋值。
一、业务问题:多选重父不是循环写属性
创作者框选一个组及其子节点,再拖到新父节点。如果逐个执行 child.Parent = target,会出现三类错误:父子同时被选时子节点被移动两次;目标位于选区后代中时形成环;操作执行一半失败时只改了一部分树。
附着关系还不是内容层级的别名。内容 Parent 决定对象树、保存和销毁范围;AttachParent 决定空间跟随端点与局部姿态。把两者共用一套写属性逻辑,会把“移到文件夹”和“焊到物体上”混成同一种语义。
二、实际实现:以会话拓扑计算森林根
工程中的重父流程已经包含几个正确决策:先抓取当前编辑会话的完整拓扑,而不是使用主场景的过期视图;过滤会话成员、根节点、目标自身与成环候选;再只保留选区森林的独立根,并按拓扑顺序执行。这样父节点被选中时,其后代随父移动,不再重复写入。
操作进入独立撤销栈,并在写入前记录旧父、新父和兄弟顺序。Prefab 编辑会话使用孤儿根,若错误地从主场景拓扑做环检测,整套判断会失效;实际代码对此专门隔离,说明“拓扑属于哪个编辑域”也是事务前置条件。
三、仍有风险:校验正确不代表提交原子
若多个根逐项写入,第三个失败,前两个已经成功。仅仅把撤销录制标记为取消,并不天然保证底层写入回滚。类似地,附着会连续写 AttachOffset 与 AttachParent,第二步失败也可能留下半状态。
更稳妥的结构命令应先计算计划,再一次提交:
plan = BuildReparentPlan(snapshot, selection, target)
validateNoCycle(plan)
validateDomainAndLocks(plan)
before = captureAffectedSubgraph(plan)
if !applyAll(plan):
restoreExact(before)
fail
publishTopologyRevision(snapshot.revision + 1)应用阶段需要同时更新父子索引、Transform 继承、选择、空间索引和可持久化脏标记;观察者只能看到提交前或提交后的完整 Revision。
四、验证结论
必须测试父子同时选择、选择顺序变化、跨编辑会话、重父到后代、锁定节点、写到一半故障、撤销后对象 ID 重映射,以及 Parent 与 AttachParent 同时存在的对象。核心不变量是:树始终无环,每个节点只有一个内容父,任意失败不改变提交前拓扑。
好的经验是“按森林根执行”和“使用所属会话的权威拓扑”;需要继续提高的是把多次属性写入升级为真正的结构变更集。