您现在的位置是:首页 >其他 >Unity3d 有关Invoke和 Coroutine 的执行条件的误解网站首页其他

Unity3d 有关Invoke和 Coroutine 的执行条件的误解

.卡 2024-07-22 12:01:02
简介Unity3d 有关Invoke和 Coroutine 的执行条件的误解

认识错误的点

之前一直以为在父物体未激活的状态下, invoke 和 Coroutine 都不会执行。这里面有一点误区。

正解

  1. Coroutine 在父物体未激活状态下,确实不会执行。并且如果在父物体(包括祖先节点)没有激活的情况下,直至调用 StartCoroutine 会抛出不可 catch 的错误
    在这里插入图片描述2. invoke 在父物体(包括祖先节点)未激活的情况下,如果被调用,依然会执行。

测试代码

代码下载:Test_CallInvoke_CallCoroutine_When_GameObject_Active.unitypackage

物体层级:
在这里插入图片描述在这里插入图片描述
父物体代码:

using UnityEngine;

public class ParentNode : MonoBehaviour
{
    [SerializeField]
    private ChildNode ChildNode;

    private void Awake()
    {
        ChildNode.SetParent(this);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.W))
        {
            Debug.Log("Get W Key Down");
            gameObject.SetActive(true);
            ChildNode.gameObject.SetActive(false);
            ChildNode.CallInvoke();
            ChildNode.CallCoroutine();
        }
    }

    [ContextMenu(nameof(Call_When_Inactive))]
    public void Call_When_Inactive()
    {
        gameObject.SetActive(false);
        ChildNode.gameObject.SetActive(false);
        ChildNode.CallInvoke();
        ChildNode.CallCoroutine();
    }

    [ContextMenu(nameof(Call_When_Active_Then_Inactive))]
    public void Call_When_Active_Then_Inactive()
    {
        gameObject.SetActive(true);
        ChildNode.gameObject.SetActive(true);
        ChildNode.CallInvoke();
        ChildNode.CallCoroutine();
        ChildNode.gameObject.SetActive(false);
    }
}

子物体的代码:

using System;
using System.Collections;
using UnityEngine;

public class ChildNode : MonoBehaviour
{
    private ParentNode _parentNode;

    public void SetParent(ParentNode parentNode)
    {
        _parentNode = parentNode;
    }
    
    private void Update()
    {
        Debug.Log(nameof(ChildNode));
    }

    public void CallCoroutine()
    {
        // if (!gameObject.activeInHierarchy)
        //     return;
        
        try
        {
            StartCoroutine(Internal_Called_From_Coroutine());
        }
        catch (Exception e)
        {
            // 注意,物体未激活,调用协程,异常不可捕捉
            Debug.LogError($"must can't catch error, {e}");
        }
    }
    
    public void CallInvoke()
    {
        Invoke(nameof(Internal_Called_From_Invoke), 3);
    }
    
    public void Internal_Called_From_Invoke()
    {
        Debug.Log(
            $"{nameof(Internal_Called_From_Invoke)}"
                  + $", 父物体激活状态: {(_parentNode.gameObject.activeSelf ? "true" : "false")}"
                  + $", 当前物体激活状态: {(gameObject.activeSelf ? "true" : "false")}"
            );
              
    }

    IEnumerator Internal_Called_From_Coroutine()
    {
        yield return new WaitForSeconds(3);
        
        Debug.Log(
                  $"{nameof(Internal_Called_From_Coroutine)}" 
                  + $", 父物体激活状态: {(_parentNode.gameObject.activeSelf ? "true" : "false")}"
                  + $", 当前物体激活状态: {(gameObject.activeSelf ? "true" : "false")}"
            );
    }
}

执行截图

  1. 父物体激活,脚本所在的物体未激活的情况:
    在这里插入图片描述2. 父物体和脚本所在的物体都未激活的情况:
    在这里插入图片描述3. 在父物体和子物体的都激活的情况,调用invoke 和 Coroutine,然后马上关闭子物体的激活状态:
    在这里插入图片描述
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。