Unity中定时器的使用

射击游戏项目开发笔记 (三)

定时器是游戏中必备的支持之一,而且也属于代码运行频率比较高的部分.所以一个设计精良的定时器绝对是地基里的一块好砖头.

在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;
    }
}

说明:

  1. 根据ID和传入的函数哈希值来绑定一个定时器.
  2. 需要手动CreateTimer 和 DestroyTimer(用C++久了果然什么都要手动..)
  3. 相同的定时器回调函数,可以注册N个不同ID的定时器.
  4. 当调用次数为0的时候自动回收.

发表评论

邮箱地址不会被公开。 必填项已用*标注