Unity编辑器删除组件时同步移除关联组件的实现及报错解决
问题根源
报错的核心原因是Unity切换运行模式的流程触发了非预期的OnDestroy调用:
进入运行模式前,Unity会先销毁编辑态场景中所有带[ExecuteAlways]标记的组件实例,此时Application.isPlaying仍为false,你的逻辑会调用DestroyImmediate删除关联组件;运行模式初始化场景重新加载时,同个销毁逻辑可能二次触发,或是对已经销毁的对象重复调用销毁接口,最终抛出报错。
修复方案
方案1:快速修复现有逻辑
增加空值判断+播放模式切换状态判断,过滤掉切运行模式时的非预期销毁触发,同时用编辑器原生的撤销安全销毁接口优化体验:
[ExecuteAlways] public class ContainerDescriptor : MonoBehaviour { // ... 其他代码 public AttachedContainer attachedContainer; private void OnDestroy() { // 先判空,从根源避免重复销毁报错 if (attachedContainer == null) return; if (Application.isPlaying) { Destroy(attachedContainer); } else { #if UNITY_EDITOR // 排除即将切换到运行模式的场景,不执行销毁逻辑 if (UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode) return; // 用Undo封装的销毁接口,支持用户撤销操作,符合编辑器交互规范 UnityEditor.Undo.DestroyObjectImmediate(attachedContainer); #endif } } // ... 其他代码 }
注意编辑器相关代码需要用#if UNITY_EDITOR宏包裹,避免项目打包时找不到编辑器类报错。
方案2:更精准的编辑器事件监听方案
如果不想依赖OnDestroy生命周期的判断,还可以直接监听Unity编辑器的组件移除事件,完全避开运行模式切换的干扰,甚至可以去掉组件的[ExecuteAlways]属性。
你需要新建一个编辑器脚本放在项目的Editor目录下:
#if UNITY_EDITOR using UnityEditor; using UnityEngine; [InitializeOnLoad] public static class ContainerDescriptorRemoveListener { static ContainerDescriptorRemoveListener() { ObjectChangeEvents.changesPublished += OnEditorObjectChanged; } private static void OnEditorObjectChanged(ref ObjectChangeEventStream stream) { for (int i = 0; i < stream.length; i++) { if (stream.GetEventType(i) == ObjectChangeKind.RemoveComponent) { stream.GetRemoveComponentEvent(i, out RemoveComponentEvent data); var removedComp = EditorUtility.InstanceIDToObject(data.componentInstanceId) as ContainerDescriptor; if (removedComp != null && removedComp.attachedContainer != null) { Undo.DestroyObjectImmediate(removedComp.attachedContainer); } } } } } #endif
该方案仅在用户手动移除组件时触发销毁逻辑,不会被域重载、场景关闭、运行模式切换等场景误触发,稳定性更高。
内容的提问来源于stack exchange,提问作者nathan raynal
相关产品推荐
相关产品推荐

