You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Unity中创建无协程/Invoke的延迟SetActive自定义函数

实现带延迟的SetActive功能(非协程/Invoke方案&GameObject扩展)

一、非协程/Invoke的实现方案

如果不想依赖Unity自带的协程或Invoke,可以自己实现一个延迟任务管理器,通过单例维护任务队列,每一帧检查任务是否到期执行。这种方式完全可控,不需要依赖Unity的协程系统。

代码实现

首先创建单例管理器:

using System.Collections.Generic;
using UnityEngine;

public class DelayActionManager : MonoBehaviour
{
    private static DelayActionManager _instance;
    private List<DelayTask> _delayTasks = new List<DelayTask>();

    private struct DelayTask
    {
        public GameObject targetObj;
        public bool targetState;
        public float executeTime;
    }

    public static DelayActionManager Instance
    {
        get
        {
            if (_instance == null)
            {
                GameObject managerObj = new GameObject("DelayActionManager");
                _instance = managerObj.AddComponent<DelayActionManager>();
                DontDestroyOnLoad(managerObj);
            }
            return _instance;
        }
    }

    private void Update()
    {
        // 倒序遍历避免移除元素时索引混乱
        for (int i = _delayTasks.Count - 1; i >= 0; i--)
        {
            var task = _delayTasks[i];
            if (Time.time >= task.executeTime)
            {
                if (task.targetObj != null) // 避免对象已被销毁
                {
                    task.targetObj.SetActive(task.targetState);
                }
                _delayTasks.RemoveAt(i);
            }
        }
    }

    public void AddSetActiveTask(GameObject obj, bool state, float delay)
    {
        if (obj == null) return;
        _delayTasks.Add(new DelayTask
        {
            targetObj = obj,
            targetState = state,
            executeTime = Time.time + delay
        });
    }
}

然后实现目标函数:

public static void SetActiveWithDelay(GameObject obj, bool stateToChange, float delayTime)
{
    DelayActionManager.Instance.AddSetActiveTask(obj, stateToChange, delayTime);
}

调用方式完全符合你的需求:

SetActiveWithDelay(GameObjectToPass, true, 2f);

二、协程/Invoke是否为更优方案?

其实协程是更推荐的方案,原因如下:

  • 协程是Unity原生支持的轻量级异步方案,底层由Unity管理,性能开销极低,远低于自己维护队列的开销(尤其是任务量小时)。
  • 代码更简洁,不需要额外维护管理器类,直接在MonoBehaviour中写协程即可:
    public static IEnumerator SetActiveWithDelayCoroutine(GameObject obj, bool state, float delay)
    {
        yield return new WaitForSeconds(delay);
        if (obj != null) obj.SetActive(state);
    }
    
    // 调用时:
    StartCoroutine(SetActiveWithDelayCoroutine(GameObjectToPass, true, 2f));
    
  • Invoke本质是反射调用,性能略差于协程,且灵活性低(无法传递复杂参数、中途取消麻烦),一般只用于简单的延迟调用。

如果你的顾虑是“和直接用协程没区别”——其实这正是协程的优势:它就是专门解决这类帧延迟/时间延迟问题的,没必要为了“不用协程”而额外造轮子,除非你有大量统一管理延迟任务的需求。

三、扩展GameObject类实现直接调用

用C#的扩展方法就能实现GameObject.SetActiveWithDelay(true,2f)的调用方式,代码非常简单:

using UnityEngine;

public static class GameObjectExtensions
{
    public static void SetActiveWithDelay(this GameObject obj, bool state, float delay)
    {
        // 这里可以选择用协程或者上面的管理器方案,比如用协程的话:
        if (obj == null) return;
        obj.StartCoroutine(SetActiveCoroutine(obj, state, delay));
    }

    private static IEnumerator SetActiveCoroutine(GameObject obj, bool state, float delay)
    {
        yield return new WaitForSeconds(delay);
        if (obj != null) obj.SetActive(state);
    }
}

现在你可以直接在任何GameObject对象上调用:

GameObjectToPass.SetActiveWithDelay(true, 2f);

如果不想用协程,也可以把扩展方法里的逻辑换成调用DelayActionManager的方法。

内容的提问来源于stack exchange,提问作者IcyThug

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 12:01:02