射击游戏项目开发笔记 (三)
定时器是游戏中必备的支持之一,而且也属于代码运行频率比较高的部分.所以一个设计精良的定时器绝对是地基里的一块好砖头.
在Unity中有很多种方式可以使用定时器.
Update中计算时间
最简单的可能就是在Update中获取deltaTime
,然后累计时间触发了,如果要隔一秒调用某个函数,使用Update代码类似如下
private float m_fTimeTotal = 0.0f; void Update () { m_fTimeTotal += Time.deltaTime; if (m_fTimeTotal > 1000f) { m_fTimeTotal = 0.0f; // do something } }
但是这种方式非常不好看.
使用每个MonoBehaviour中的Invoke
每个MonoBehaviour类中我们发现都有Invoke函数,这几个函数的最要作用就是作为延时调用而设计的.简单的几个例子如下
//------------------------------------------------------------------------- void LaunchProjectile() { if (null != m_DesLabel) { m_DesLabel.text += "\n执行:LaunchProjectile - " + Time.time; Debug.Log("LaunchProjectile - " + Time.time); } } //------------------------------------------------------------------------- // 单次 public void OnTimer1() { CancelInvoke(); if (null != m_DesLabel && !IsInvoking("LaunchProjectile")) { m_DesLabel.text = "2秒后执行,开始:OnTimer1 - " + Time.time; Debug.Log("OnTimer1 - " + Time.time); Invoke("LaunchProjectile", 2); } } //------------------------------------------------------------------------- // 重复 public void OnTimer2() { CancelInvoke(); if (null != m_DesLabel && !IsInvoking("LaunchProjectile")) { m_DesLabel.text = "1秒后每隔一秒执行一次,开始:OnTimer2 - " + Time.time; Debug.Log("OnTimer2 - " + Time.time); InvokeRepeating("LaunchProjectile", 1, 1F); } }
有单次调用和重复调用两种方式,基本能满足一般的定时器需求.唯一的缺点是它他依赖MonoBehaviour了.
使用协程Coroutine
协程的主要作用是控制代码在特定的时机执行.协程不是线程,也不是异步执行的。协程和 MonoBehaviour 的 Update函数一样也是在MainThread中执行的.使用协程依然可以实现定时器效果,如下代码实现了一个1s的定时器.
//------------------------------------------------------------------------- IEnumerator __TimerHandle() { yield return new WaitForSeconds(1); m_DesLabel.text += "\n执行:__TimerHandle - " + Time.time; } //------------------------------------------------------------------------- public void OnTimer3() { CancelInvoke(); StopAllCoroutines(); if (null != m_DesLabel) { m_DesLabel.text = "开始:OnTimer3 - " + Time.time; Debug.Log("OnTimer3 - " + Time.time); StartCoroutine("__TimerHandle"); } }
协程对于定时器而言作用非常有限.它依然是依赖于MonoBehaviour.
自行实现一个
考虑前面几种定时器的情况,我决定自己实现一个.想法参照了上一个项目的C++版定时器.
简单例子:
//************************************************************************* // 创建日期: 2015-7-8 // 文件名称: TimerSample.cs // 创 建 人: Rect // 版权所有: MIT // 说 明: //************************************************************************* //------------------------------------------------------------------------- using UnityEngine; using System.Collections; using CoreLib.Timer; public class TimerSample : MonoBehaviour { public UILabel m_DesLabel; private CTimerSystem m_TimerSystem = null; //-------------------------------------------------------------------------- void Update () { if (null != m_TimerSystem) { m_TimerSystem.UpdateTimer(); } } //-------------------------------------------------------------------------- void OnDestroy() { if (null != m_TimerSystem) { m_TimerSystem.Destroy(); } } //-------------------------------------------------------------------------- public void OnCreateTimer4() { CancelInvoke(); if (null != m_DesLabel) { m_DesLabel.text = "开始:OnCreateTimer4 - " + Time.time * 1000; Debug.Log("OnCreateTimer4 - " + Time.time * 1000); } if (null == m_TimerSystem) { m_TimerSystem = new CTimerSystem(); m_TimerSystem.Create(); } m_TimerSystem.CreateTimer(1, 1000, OnTimeTest1); m_TimerSystem.CreateTimer(2, 1200, OnTimeTest1); m_TimerSystem.CreateTimer(1, 1000, OnTimeTest2); } //------------------------------------------------------------------------- public void OnDistoryTimer() { if (null != m_TimerSystem) { m_TimerSystem.DestroyTimer(1, OnTimeTest1); m_TimerSystem.DestroyTimer(2, OnTimeTest1); m_TimerSystem.DestroyTimer(1, OnTimeTest2); } } //------------------------------------------------------------------------- public void OnTimeTest1(uint nTimeID) { Debug.Log("OnTimeTest1 - " + Time.time * 1000); m_DesLabel.text += "\n执行:OnTimeTest1 - " + Time.time * 1000 + " TimeID = " + nTimeID; } public void OnTimeTest2(uint nTimeID) { Debug.Log("OnTimeTest2 - " + Time.time * 1000); m_DesLabel.text += "\n执行:OnTimeTest2 - " + Time.time * 1000 + " TimeID = " + nTimeID; } }
说明:
- 根据ID和传入的函数哈希值来绑定一个定时器.
- 需要手动CreateTimer 和 DestroyTimer(用C++久了果然什么都要手动..)
- 相同的定时器回调函数,可以注册N个不同ID的定时器.
- 当调用次数为0的时候自动回收.