【问题标题】:Unity GameObject.BroadcastMessage() not work on deactivated GameObjectsUnity GameObject.BroadcastMessage() 不适用于停用的游戏对象
【发布时间】:2017-10-25 15:46:58
【问题描述】:

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

显然,这个EventOnMonoCallback.cs 脚本附加到TOOTHPANEL,它会在OnEnable() 上触发,但_Food 游戏对象本身是不活动的。

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

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

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();
        }
    }
}

【问题讨论】:

  • 看起来您正在使用某种框架,但如果您不告诉我们它是什么,我们将无法帮助您。无论哪种方式,这都不是 Unity 问题,而是关于该框架的问题
  • @Mars 这是一个 Unity 问题,已明确标记为。
  • 这是两个问题——第一个是询问如何绕过广播消息不适用于不活跃的孩子,第二个问题是 OP 在帖子的第一行回答...
  • 但是谷歌搜索后,我找不到任何对 EventOnMonoCallback 的引用,所以我想这是一个自定义脚本......在这种情况下,第一个问题的答案会改变
  • 不管他是否有控制权,也没有提到组织核心,所以假设这里没有使用。 GameObject.BroadcastMessage 用于在编译时调用对象中的函数而不提供函数名。您的解决方案要求在编译期间绑定该函数。 GameObject.BroadcastMessage 只需要将函数名称作为字符串传递,我认为这就是 op 正在寻找的,但在禁用对象时也可以工作。

标签: c# unity3d


【解决方案1】:

在 Unity 中激活/停用和启用/禁用是有区别的。您可以激活和停用游戏对象。您可以启用和禁用脚本。不要混淆这两者。

BroadcastMessage 函数将始终工作,即使带有目标函数的脚本禁用,但它在 GameObject 时工作该脚本附加到已停用


有两种简单的解决方法:

1.使用Unity内置的Invoke函数。使用GetComponentsInChildren 获取所有MonoBehaviour,然后在每一个上调用Invoke。即使游戏对象停用,调用也将始终有效。使用以List为参数的GetComponentsInChildren的重载版本,这样每次调用时就不会产生垃圾。

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);
    }
}

唯一不好的是你不能给它传递参数。请参阅 #2 了解解决方法。


2。与 #1 执行相同的操作,但使用 C# MethodInfo.Invoke 函数而不是 Unity API 中的版本。这应该可以让您将参数传递给您正在调用的函数。

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);
        }
    }
}

您可以将它们转换为extension methods 以使其更易于使用。

【讨论】:

  • 我不太明白monoList,我需要在清醒状态下缓存所有的 MonoBehaviours 吗?关于参数,我可以把它放在value 吗?如果我有两个参数怎么办?
  • monoList 将在该函数中调用 GetComponentsInChildren&lt;MonoBehaviour&gt;(true, monoList); 时自动清除并重新填充。您不必在Awake 函数中缓存任何内容,但这个问题是为什么真的建议在发表评论之前尝试回答。它表明你关心你的问题和你收到的答案。
  • 两个参数,这个问题是关于BroadcastMessage 接受一个参数。我的答案也是采用一个参数,并希望它的行为与原始 BroadcastMessage 函数完全相同但在游戏对象停用时也可以工作。如果你想要其他东西,你需要自己修改它。查看method.Invoke 部分。
【解决方案2】:

你是对的,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();
        }
    }
}  

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

【讨论】:

  • 我也一直在想这个方法,但是如果我有其他不同的函数名怎么办?我想通过创建通用的东西来避免创建大量的脚本。所以如果我使用BroadcastMessage,我可以在字符串参数中放入任何东西来执行该方法。
  • 在这种情况下,您可以使用 Delegates 或 Interfaces 来尝试获得不同的效果,但我认为调用基本上还是一样的。
猜你喜欢
  • 2021-10-20
  • 2022-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多