Unity中的延迟调用方法详解
目录
1. Invoke系列方法
InvokeRepeating方法(定时重复调用)
Invoke方法(单次延迟调用)
CancelInvoke方法(取消调用)
2. 协程(Coroutine)方法
基础协程实现
周期性协程实现
3. 方法对比与选择指南
详细对比表格
选择建议
4. 协程管理高级技巧
协程的生命周期控制
协程的实用模式
5. 性能优化建议
6.补充
1. Invoke系列方法
InvokeRepeating方法(定时重复调用)
InvokeRepeating
是Unity提供的一个非常实用的方法,它允许你在特定时间间隔重复调用某个方法。这个方法特别适合需要周期性执行的任务,如敌人AI的决策、游戏数值的更新等。
完整示例:
using UnityEngine;public class TimerExample : MonoBehaviour
{void Start(){// 参数说明:// 1. 方法名(字符串形式)// 2. 首次调用延迟时间(2秒)// 3. 重复调用间隔(1秒)InvokeRepeating("UpdateGameState", 2f, 1f);}void UpdateGameState(){// 这里可以执行游戏状态更新逻辑Debug.Log("Game state updated at: " + Time.time);// 实际应用中可能包含:// - 更新NPC行为// - 刷新资源再生// - 检查任务完成状态}
}
Invoke方法(单次延迟调用)
当只需要延迟执行一次方法时,可以使用Invoke
方法。这在游戏开发中常用于实现技能冷却、延迟特效等场景。
扩展示例:
using UnityEngine;public class SkillSystem : MonoBehaviour
{public GameObject explosionEffect;void Start(){// 3秒后释放技能Invoke("CastFireball", 3f);}void CastFireball(){// 创建爆炸特效Instantiate(explosionEffect, transform.position, Quaternion.identity);// 伤害计算// DealDamage(50);Debug.Log("Fireball casted at: " + Time.time);}
}
CancelInvoke方法(取消调用)
CancelInvoke
用于取消通过Invoke
或InvokeRepeating
设置的调用计划。这在角色死亡、游戏暂停等需要中断定时逻辑的场景中非常有用。
增强示例:
using UnityEngine;public class EnemyAI : MonoBehaviour
{void Start(){// 每2秒执行一次AI决策InvokeRepeating("MakeDecision", 0f, 2f);}void MakeDecision(){if(GetComponent<Health>().IsDead){CancelInvoke("MakeDecision");return;}// AI决策逻辑Debug.Log("AI making decision at: " + Time.time);}void OnDestroy(){// 确保对象销毁时取消所有调用CancelInvoke();}
}
2. 协程(Coroutine)方法
基础协程实现
协程提供了更灵活的时间控制方式,特别适合需要复杂时序控制的场景。
详细示例:
using UnityEngine;
using System.Collections;public class CoroutineExample : MonoBehaviour
{IEnumerator Start(){Debug.Log("Coroutine started at: " + Time.time);// 等待2秒yield return new WaitForSeconds(2f);// 执行延迟方法ShowMessage();// 可以继续添加更多延迟逻辑yield return new WaitForSeconds(1f);Debug.Log("Additional action after 1 more second");}void ShowMessage(){Debug.Log("Message shown after delay at: " + Time.time);}
}
周期性协程实现
通过while循环和yield return结合,可以实现精确的周期性调用。
增强实现:
using UnityEngine;
using System.Collections;public class PeriodicUpdater : MonoBehaviour
{[SerializeField] private float updateInterval = 0.5f;private bool isRunning = true;IEnumerator Start(){while(isRunning){// 执行更新逻辑UpdateGameLogic();// 等待指定间隔yield return new WaitForSeconds(updateInterval);}}void UpdateGameLogic(){// 这里可以放置需要周期性执行的逻辑Debug.Log("Periodic update at: " + Time.time);// 例如:// - 更新UI显示// - 检查游戏条件// - 自动保存游戏}void OnDisable(){// 当组件被禁用时停止协程isRunning = false;}
}
3. 方法对比与选择指南
详细对比表格
特性 | Invoke/InvokeRepeating | 协程(Coroutine) |
---|---|---|
语法复杂度 | 简单 | 中等 |
时间精度 | 依赖帧率 | 高精度 |
代码可读性 | 一般 | 优秀 |
复杂逻辑支持 | 有限 | 优秀 |
多步骤延迟 | 不支持 | 支持 |
条件等待 | 不支持 | 支持(yield return waitUntil) |
内存开销 | 较低 | 略高 |
选择建议
-
简单延迟/周期任务:优先考虑Invoke系列
- 技能冷却
- 简单计时器
- 一次性延迟效果
-
复杂时序控制:使用协程
- 需要等待多个条件
- 包含多个步骤的动画序列
- 资源加载流程
- 复杂的AI行为树
-
高精度定时:使用协程
- 音乐节奏游戏
- 精确的物理模拟
- 网络同步
4. 协程管理高级技巧
协程的生命周期控制
using UnityEngine;
using System.Collections;public class CoroutineManager : MonoBehaviour
{private Coroutine activeCoroutine;void Start(){// 存储协程引用以便后续管理activeCoroutine = StartCoroutine(ComplexRoutine());}IEnumerator ComplexRoutine(){Debug.Log("Starting complex routine");yield return new WaitForSeconds(1f);// 分段执行yield return StartCoroutine(SubRoutine1());yield return StartCoroutine(SubRoutine2());Debug.Log("Complex routine completed");}IEnumerator SubRoutine1(){yield return new WaitForSeconds(0.5f);Debug.Log("Sub-routine 1 executed");}IEnumerator SubRoutine2(){yield return new WaitForSeconds(0.3f);Debug.Log("Sub-routine 2 executed");}void OnDisable(){// 安全停止协程if(activeCoroutine != null){StopCoroutine(activeCoroutine);}}
}
协程的实用模式
- 超时模式:
IEnumerator LoadWithTimeout(float timeout)
{AsyncOperation operation = SceneManager.LoadSceneAsync("MainScene");float startTime = Time.time;while(!operation.isDone){if(Time.time - startTime > timeout){Debug.LogError("Loading timed out!");yield break; // 提前退出协程}yield return null;}Debug.Log("Scene loaded successfully");
}
- 并行协程:
IEnumerator RunParallelCoroutines()
{// 同时启动多个协程Coroutine coroutineA = StartCoroutine(TaskA());Coroutine coroutineB = StartCoroutine(TaskB());// 等待所有协程完成yield return coroutineA;yield return coroutineB;Debug.Log("All parallel tasks completed");
}
- 条件等待:
IEnumerator WaitForCondition()
{// 等待玩家按下空格键yield return new WaitUntil(() => Input.GetKeyDown(KeyCode.Space));// 等待敌人数量少于3yield return new WaitWhile(() => GameObject.FindGameObjectsWithTag("Enemy").Length >= 3);Debug.Log("Conditions met, proceeding...");
}
5. 性能优化建议
- 避免频繁创建/销毁协程:复用现有的协程
- 谨慎使用无限循环:确保有退出条件
- 减少字符串方法名调用:Invoke使用字符串方法名会有反射开销
- 协程分组管理:对于大量协程,考虑使用专用管理器
- 注意垃圾回收:yield return新建对象会产生GC
通过合理选择和组合这些延迟调用方法,可以构建出既高效又易于维护的游戏逻辑系统。
6.补充
这里有两个序列化的UnityEvent,可能看代码不是很直观,直接上图。
是不是感觉很眼熟。对就是,像我们经常看到的Button下边的OnClick其实就是这种东西。
我们为这个东西挂上我们自己的测试脚本。
但是这时候我们想要调用测试脚本的方法了,这时候就用到了Invoke。
这里会自动调用UnityEvent下的脚本的指定方法。