在 Unity 开发中,我们经常需要在游戏对象的生命周期中进行一些异步操作,比如使用 Invoke
延时调用方法,或启动协程来处理复杂的逻辑。然而,当游戏对象被销毁或禁用时,这些操作会如何表现呢?
在这篇博客中,我将分享三种常见情况:延迟调用之前销毁游戏对象 和 延迟调用之前禁用游戏对象 和 延迟调用之前禁用当前脚本,并深入探讨它们对 Invoke
和协程执行的影响,以及如何避免由此引发的潜在问题。通过这些案例分析,你将更清晰地理解 Unity 中对象的生命周期管理,帮助你在项目中更有效地控制异步操作的执行。
情况一:延迟调用之前,游戏对象被Destroy
在此段代码中,游戏物体有两个延迟调用函数,一个是通过Invoke,另一个是通过协程,它们都是10秒后被调用,但是在5秒后物体就会被Destroy。
void Start()
{
Invoke(nameof(invokeFunction),10f);
StartCoroutine(coroutineFunction(10f));
Destroy(gameObject, 5f);
}
public void invokeFunction()
{
print("invoke函数执行了");
}
public IEnumerator coroutineFunction(float time)
{
yield return new WaitForSeconds(time);
print("coroutine函数执行了");
}
结果:Invoke函数和协程都不会执行。
情况二:延迟调用之前,游戏对象被禁用
此段代码与上面那种情况区别就是5秒后不再Destroy,而是使物体失活。
void Start()
{
Invoke(nameof(invokeFunction),10f);
StartCoroutine(coroutineFunction(10f));
StartCoroutine(EnableActive(5f));
}
public void invokeFunction()
{
print("invoke函数执行了");
}
public IEnumerator coroutineFunction(float time)
{
yield return new WaitForSeconds(time);
print("coroutine函数执行了");
}
public IEnumerator EnableActive(float time)
{
yield return new WaitForSeconds(time);
gameObject.SetActive(false);
}
结果:Invoke函数会执行,协程不执行。
情况三:延迟调用之前,脚本被禁用。
测试代码如下。
void Start()
{
Invoke(nameof(invokeFunction),10f);
StartCoroutine(coroutineFunction(10f));
StartCoroutine(EnableScriptActive(5f));
}
public void invokeFunction()
{
print("invoke函数执行了");
}
public IEnumerator coroutineFunction(float time)
{
yield return new WaitForSeconds(time);
print("coroutine函数执行了");
}
public IEnumerator EnableScriptActive(float time)
{
yield return new WaitForSeconds(time);
this.enabled = false;
}
结果:Invoke函数和协程都会执行。
Unity 内部处理逻辑:
1. 游戏对象销毁(Destroy(gameObject)
)
当 Destroy(gameObject)
被调用时,Unity 会在指定的时间之后完全销毁该游戏对象,以及它上面所有的组件,包括脚本。
- Invoke:游戏对象销毁后,所有由
Invoke
创建的函数调用都会被终止,因此Invoke
的回调不会执行。 - 协程:协程同样会在对象销毁时终止,所有关联的协程将停止,不会继续执行或恢复。
2. 游戏对象禁用(SetActive(false)
)
当你调用 SetActive(false)
禁用游戏对象时,该对象在场景中仍然存在,但它以及所有子对象都将被禁用(即非活动状态)。
-
Invoke:Invoke 方法是通过 Unity 的时间系统调度的,它不依赖于特定的 GameObject 或组件的激活状态。即使 GameObject 被禁用,Invoke 也会继续执行。
-
协程:当 GameObject 被禁用时,所有附加在该 GameObject 上的脚本的协程都会被停止。因此,coroutineFunction 不会继续执行,也就不会打印 "coroutine函数执行了"。
3. 脚本禁用(this.enabled = false
)
当 this.enabled = false
禁用某个脚本时,游戏对象仍然是激活状态,但该脚本的执行会被部分暂停。具体来说:
-
Invoke:同样,Invoke 方法不受脚本启用状态的影响,它仍然会执行。
-
协程:当脚本被禁用时,已经启动的协程会继续运行直到完成。这是因为协程一旦启动,就成为了独立的执行单元,不再直接依赖于脚本的启用状态。