所以假設我們有一個帶有 Transform 屬性的 MonoBehaviour 類:
public class Class : MonoBehaviour
{
public Transform Target; //An object is already referenced here.
}
問題很簡單:如果這個物件被洗掉(外部),有沒有辦法獲取事件或某種回呼? Unity 會將其顯示為“缺失”:
![(Unity) 當參考的物件實體被洗掉/丟失時,有沒有辦法得到通知?[有構建支持]](https://img.uj5u.com/2021/12/19/970ebc3431864eba87b12b0bd88c2d5f.png)
因為它只是一個變換,所以你不能OnDestroy()在這里真正使用,即使你可以使用,這也不是一個好的選擇,因為有多少個變換實體。
應該注意的是,我需要在構建時使用它,因此遺憾的是,僅在編輯器中的選項無法解決我的問題。
uj5u.com熱心網友回復:
這是一種方法。我認為這是一個部分解決方案,因為它目前在某些方面并不理想,包括在播放模式下無法編輯預制件,如果 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
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/385942.html
下一篇:如何使用腳本統一更改相機
