这是一种方法。我认为这是一个部分解决方案,因为它目前在某些方面并不理想,包括在播放模式下无法编辑预制件,如果 AI 被破坏,则通知单一行为,不尊重编辑器中的撤消,可能还有其他。尽管如此,无论如何,这可能还是值得分享它的当前状态。
创建一个 MonoBehaviour,它将管理在删除拥有对象时调用的操作。我们有兴趣在编辑器和运行时完成这项工作,因此我为此使用了 UnityEvent api。
using UnityEngine;
using UnityEditor;
using UnityEngine.Events;
[System.Serializable]
public class DestroyEvent : UnityEvent { };
[ExecuteInEditMode]
public class DestructionNotifier : MonoBehaviour
{
public DestroyEvent OnDestroyed;
private void Awake()
{
if (OnDestroyed == null)
{
OnDestroyed = new DestroyEvent();
}
}
public void Register(UnityAction act)
{
#if UNITY_EDITOR
if (EditorApplication.isPlayingOrWillChangePlaymode)
{
// add non-persistent listener if in play mode
OnDestroyed.AddListener(act);
}
else
{
// add persistent listener if in edit mode
UnityEditor.Events.UnityEventTools.AddPersistentListener(
OnDestroyed, act);
OnDestroyed.SetPersistentListenerState(
OnDestroyed.GetPersistentEventCount() - 1,
UnityEventCallState.EditorAndRuntime);
}
#else
// add non-persistent listener
OnDestroyed.AddListener(act);
#endif
}
public void Deregister(UnityAction call)
{
// remove or disable matching persistent actions
for (int i = 0; i < OnDestroyed.GetPersistentEventCount(); i++)
{
if ((Object)call.Target == OnDestroyed.GetPersistentTarget(i))
{
#if UNITY_EDITOR
if (EditorApplication.isPlayingOrWillChangePlaymode)
{
OnDestroyed.SetPersistentListenerState(i,
UnityEventCallState.Off);
}
else
{
UnityEditor.Events.UnityEventTools
.RemovePersistentListener(OnDestroyed, i);
}
#else
OnDestroyed.SetPersistentListenerState(i,
UnityEventCallState.Off);
#endif
}
}
// remove matching non-persistent actions
OnDestroyed.RemoveListener(call);
// if in edit mode, remove self if no actions
#if UNITY_EDITOR
RemoveEmptyEvents();
if (!EditorApplication.isPlayingOrWillChangePlaymode
&& OnDestroyed.GetPersistentEventCount() == 0)
{
DestroyImmediate(this);
}
#endif
}
private void OnValidate()
{
RemoveEmptyEvents();
}
void RemoveEmptyEvents()
{
#if UNITY_EDITOR
for (int i = OnDestroyed.GetPersistentEventCount() - 1; i >= 0; i--)
{
if (OnDestroyed.GetPersistentTarget(i) == null)
{
UnityEditor.Events.UnityEventTools.RemovePersistentListener(
OnDestroyed, i);
}
}
#endif
}
void OnDestroy()
{
if (OnDestroyed != null)
{
OnDestroyed.Invoke();
}
}
}
然后,在您的 AI 脚本中,将您的转换转换为一个属性,该属性使用 DestructionNotifier 字段在目标更改时子/取消子到事件。使用自定义检查器允许在检查器中编辑属性:
using UnityEngine;
using UnityEditor;
public class Class : MonoBehaviour
{
void OnTargetDestroyed()
{
// Stuff to do when target is destroyed
Debug.Log($"{gameObject.name}: target destroyed.");
}
[SerializeField, HideInInspector] DestructionNotifier targetNotifier;
public Transform Target {
get { return targetNotifier == null ? null : targetNotifier.transform; }
set {
if (targetNotifier != null)
{
targetNotifier.Deregister(OnTargetDestroyed);
}
if (value == null)
{
targetNotifier = null;
return;
}
targetNotifier = value.GetComponent<DestructionNotifier>();
if (targetNotifier == null)
{
targetNotifier = value.gameObject
.AddComponent<DestructionNotifier>();
}
targetNotifier.Register(OnTargetDestroyed);
}
}
}
#if UNITY_EDITOR
[CustomEditor(typeof(Class)), CanEditMultipleObjects]
public class AIInspector : Editor
{
public override void OnInspectorGUI()
{
Class targetAI = (Class)target;
EditorGUI.BeginChangeCheck();
Transform newTarget = EditorGUILayout.ObjectField("Target",
targetAI.Target, typeof(Transform), true) as Transform;
if (EditorGUI.EndChangeCheck())
{
foreach (Class curTarget in targets)
{
curTarget.Target = newTarget;
}
}
DrawDefaultInspector();
}
}
#endif