射击游戏项目开发笔记 (三)
定时器是游戏中必备的支持之一,而且也属于代码运行频率比较高的部分.所以一个设计精良的定时器绝对是地基里的一块好砖头.
在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的时候自动回收.