Unity C# 如何实现无冻结等待 延迟重载游戏场景
障碍物触发后非阻塞延迟重载关卡实现
原有代码的问题根源
System.Threading.Thread.Sleep是强制阻塞主线程的同步方法,Unity 的物理模拟、玩家移动、相机渲染、输入响应全部运行在主线程上,调用该方法会直接暂停主线程的所有任务执行,导致整个游戏进程冻结,完全无法实现“保留玩家一小段时间移动状态”的效果。- 原代码在碰撞触发的第一时间就关闭了移动组件、相机组件,和“碰撞后先保留一段时间移动状态再重载”的需求逻辑错位,即使去掉Sleep调用,也会立刻停止玩家移动,达不到预期效果。
推荐实现方案
使用Unity原生的协程(Coroutine)实现非阻塞延迟等待。协程是Unity专门为分帧延迟执行设计的机制,等待过程中不会打断主线程的游戏逻辑运行,可以完整保留等待时段内的玩家移动、物理模拟效果,等到延迟时间结束后再执行重载逻辑。
实现步骤如下:
- 确保脚本引入所需命名空间:
using UnityEngine; using UnityEngine.SceneManagement; - 将原有碰撞回调中的阻塞逻辑替换为协程启动逻辑,调整组件关闭的时机到延迟结束之后:
void OnCollisionEnter(Collision inf) { if (inf.collider.CompareTag("Obstacle")) { // 启动协程,不阻塞主线程 StartCoroutine(DelayReloadLevel()); } } IEnumerator DelayReloadLevel() { // 等待1秒,等待过程中所有游戏逻辑正常运行 yield return new WaitForSeconds(1f); // 延迟结束后再执行原有的组件关闭、重载逻辑 movement.enabled = false; Camera.enabled = false; SceneManager.LoadScene("Level"); }
其他可选实现
如果不需要复杂的等待逻辑,也可以用MonoBehaviour自带的Invoke方法实现更轻量化的延迟调用,代码写法更简洁:
void OnCollisionEnter(Collision inf) { if (inf.collider.CompareTag("Obstacle")) { // 1秒后执行LoadLevel方法 Invoke(nameof(LoadLevel), 1f); } } void LoadLevel() { movement.enabled = false; Camera.enabled = false; SceneManager.LoadScene("Level"); }
注意事项
- 协程默认使用游戏时间计算等待时长,如果游戏存在时间缩放(比如慢动作、暂停逻辑),需要不受缩放影响的等待效果,可以将等待语句替换为
yield return new WaitForSecondsRealtime(1f)。 - 代码中判断标签推荐使用
CompareTag方法,相比直接字符串对比的tag属性写法,性能更好,还能避免拼写错误导致的运行时报错。 - 禁止在主线程使用各类线程阻塞方法(比如
Thread.Sleep),这类方法会直接打断Unity的游戏循环,造成游戏假死。
内容的提问来源于stack exchange,提问作者Itay123
相关产品推荐
相关产品推荐

