using System.Collections; using System.Collections.Generic; using System; using UnityEngine; using Ichni.RhythmGame; namespace Ichni.Editor.Commands { public class ChangeValueCommand : ICommand { private IBaseElement target; private string path; private object oldValue; private object newValue; private float timestamp; // 合并窗口期:同一字段 1 秒内的接连覆盖,只算作一次编辑行为 private const float MERGE_WINDOW = 1f; public ChangeValueCommand(IBaseElement target, string path, object newValue) { this.target = target; this.path = path; this.newValue = newValue; this.oldValue = ReflectionHelper.GetDeepValue(target, path); this.timestamp = Time.realtimeSinceStartup; } public void Execute() { if (target == null) return; ReflectionHelper.SetDeepValue(target, path, newValue); } public void Undo() { // 防暴毙机制:如果所附着的对象因为其它原因在场景中已被销毁(如删除该 Note),自动拦截撤销并停止访问底层数据。 if (target == null) return; ReflectionHelper.SetDeepValue(target, path, oldValue); target.Refresh(); } public void Redo() { if (target == null) return; ReflectionHelper.SetDeepValue(target, path, newValue); target.Refresh(); } public bool TryMerge(ICommand other) { if (other is ChangeValueCommand otherCommand) { // 若对象、路径完全一致,则尝试吞并它的新值 if (System.Object.ReferenceEquals(this.target, otherCommand.target) && this.path == otherCommand.path && (otherCommand.timestamp - this.timestamp) < MERGE_WINDOW) { this.newValue = otherCommand.newValue; this.timestamp = otherCommand.timestamp; return true; } } return false; } private void RefreshInspectorIfMatched() { var inspector = EditorManager.instance.uiManager.inspector; if (inspector != null && inspector.connectedGameElement != null) { // 由于目前 DynamicUI 未实现细粒度的数据绑定事件派发,发生撤回时,我们重新装载选择一次右侧属性窗面板以完成显示同步。 // 借由我们之前的对象池机制,这个 SetInspector 消耗会在低于 1ms 的时间内无感重构。 inspector.SetInspector(inspector.connectedGameElement); } } } }