Unity GameObject.BroadcastMessage()不适用于已停用的GameObjects

时间:2017-10-25 04:24:44

标签: c# unity3d

我注意到目标停用时GameObject.BroadcastMessage不起作用。如何调整它以使其在这种情况下工作。

显然,此EventOnMonoCallback.cs脚本已附加到TOOTHPANEL,它将在OnEnable()上触发,但_Food游戏对象本身无效。

似乎,BroadcastMessage不适用于已停用的孩子?

出于某种原因,TOOTHPANEL在其他脚本将TOOTHPANEL游戏对象设置为true之前无法处于活动状态。

enter image description here

enter image description here

EventOnMonoCallback.cs

public class EventOnMonoCallback : MonoBehaviour
{
    public enum MonoCallbackType
    {
        Awake,
        Start,
        Update,
        OnEnable,
        OnDisable,
        OnDestroy
    }

    public MonoCallbackType Type;
    public UnityEvent Do;

    void Awake()
    {
        if (Type == MonoCallbackType.Awake)
        {
            Do.Invoke();
        }
    }

    void Start()
    {
        if (Type == MonoCallbackType.Start)
        {
            Do.Invoke();
        }
    }

    void Update()
    {
        if (Type == MonoCallbackType.Update)
        {
            Do.Invoke();
        }
    }

    void OnEnable()
    {
        if (Type == MonoCallbackType.OnEnable)
        {
            Do.Invoke();
        }
    }

    void OnDisable()
    {
        if (Type == MonoCallbackType.OnDisable)
        {
            Do.Invoke();
        }
    }

    void OnDestroy()
    {
        if (Type == MonoCallbackType.OnDestroy)
        {
            Do.Invoke();
        }
    }
}

2 个答案:

答案 0 :(得分:3)

在Unity中激活/停用和启用/禁用之间存在差异。您可以激活和停用GameObjects。您可以启用和禁用脚本。不要混淆这两个。

即使具有目标功能的脚本已停用BroadcastMessage功能也始终可以,但 工作脚本附加到的GameObject是已停用

有两种简单的解决方法:

1 。使用Unity的内置Invoke功能。使用GetComponentsInChildren获取所有MonoBehaviour,然后在每个Invoke上调用GetComponentsInChildren。即使游戏对象已停用,调用也将始终有效。使用以List为参数的List<MonoBehaviour> monoList = new List<MonoBehaviour>(); void BroadcastMessageExt(string methodName) { targetObj.GetComponentsInChildren<MonoBehaviour>(true, monoList); for (int i = 0; i < monoList.Count; i++) { monoList[i].Invoke(methodName, 0); } } 重载版本,以便每次调用时都不会产生垃圾。

List<MonoBehaviour> monoList = new List<MonoBehaviour>();


void BroadcastMessageExt(string methodName, object value = null, SendMessageOptions options = SendMessageOptions.RequireReceiver)
{
    targetObj.GetComponentsInChildren<MonoBehaviour>(true, monoList);
    for (int i = 0; i < monoList.Count; i++)
    {
        try
        {
            Type type = monoList[i].GetType();

            MethodInfo method = type.GetMethod(methodName, BindingFlags.Instance |
                                            BindingFlags.NonPublic |
                                             BindingFlags.Public |
                                             BindingFlags.Static);

            method.Invoke(monoList[i], new object[] { value });
        }
        catch (Exception e)
        {
            //Re-create the Error thrown by the original SendMessage function
            if (options == SendMessageOptions.RequireReceiver)
                Debug.LogError("SendMessage " + methodName + " has no receiver!");

            //Debug.LogError(e.Message);
        }
    }
}

唯一不好的是你无法传递参数。有关解决方法,请参阅#2

2 。与#1 相同,但使用C#MethodInfo.Invoke函数代替Unity API中的版本。这应该允许您将参数传递给您正在调用的函数。

<label>

您可以将这些转换为extension methods,以使其更易于使用。

答案 1 :(得分:1)

你是对的,BroadcastMessage不适用于非活动对象。除了BroadcastMessage或SendMessage之外,您必须以某种方式启用它 如果您有对该对象的引用,则可以使用该对象启用。

如果你的目标只是在所有适用的孩子中调用一个函数,你可以尝试类似:

void Call_YOURFUNCTION_OnChildren(){
    Transform[] transforms = GetComponentsInChildren<Transform>(true);
    foreach (Transform t in transforms){
        if (t.GetComponent<YOURCLASS>()){
            t.GetComponent<YOURCLASS>().YOURFUNCTION();
        }
    }
}  

注意:这假设您的所有对象都已转换