推薦閱讀
- CSDN主頁
- GitHub開源地址
- Unity3D插件分享
- 簡書地址
- 我的個人博客
- QQ群:1040082875
一、前言
今天,來分享一下如何實作定時器,
實作定時器需要搞明白二個問題:
一、定時器如何實作
二、為什么制作定時器系統
首選,我們來了解一下如何在Unity里面實作定時器,
二、Unity 定時器的三種實作方法
在Unity開發程式的時候,會學習到很多實作定時器效果的方法,比如說:
2-1、使用Time.deltaTime累加方式
在Update里面,使用Time.deltaTime實作:
using UnityEngine;
public class Test : MonoBehaviour
{
public float timer = 0f;
void Update()
{
timer += Time.deltaTime;
if (timer >= 2)
{
doSomething();
timer = 0f; // 定時2秒
}
}
void doSomething()
{
Debug.Log("每2秒執行一次");
}
}
或者通過Time.time來實作:
using UnityEngine;
public class Test : MonoBehaviour
{
public float timer = 0f;
void Start()
{
timer = Time.time;
}
void Update()
{
timer += Time.deltaTime;
if (Time.time - timer >= 2)// 定時2秒
{
doSomething();
timer = Time.time;
}
}
void doSomething()
{
Debug.Log("每2秒執行一次");
}
}
2-2、使用延遲呼叫函式
using UnityEngine;
public class Test : MonoBehaviour
{
void Start()
{
//0秒后,每2秒執行一次doSomething
InvokeRepeating("doSomething", 0, 2);
}
void doSomething()
{
Debug.Log("每2秒執行一次");
}
}
2-3、使用協程
using System;
using System.Collections;
using UnityEngine;
public class Test : MonoBehaviour
{
void Start()
{
//每2秒執行一次doSomething
StartCoroutine(UpdateTimer(2f, doSomething));
}
void doSomething()
{
Debug.Log("每2秒執行一次");
}
IEnumerator UpdateTimer(float timer,Action callBack)
{
var wait = new WaitForSeconds(timer);
while (true)
{
yield return wait;
callBack();
}
}
}
提示:場景內協程多了將會出現明顯的卡頓,
可以看出來,這三種實作定時器的方法都很簡單,在專案體量不是太大,并且代碼量也還算簡潔的時候,使用這三種實作方法是沒有問題的,
但是,當代碼量多的時候,再使用這些方法就會顯得冗余雜亂,
這時候就需要一個定時器系統來管理這些定時的任務,這就是為何要制作定時器系統的原因,
三、實作定時器系統
實作原理:
每個定時器物件是一個Timer, 添加的時候交給定時器管理系統, 定時器系統負責管理所有的定時器物件,每次游戲Update的時候,遍歷里面的每個定時器物件,
把它們過去的時間增加Time.deltaTime, 當過去的時間達到定時器觸發時間的時候,觸發定時器呼叫, 如果達到Timer觸發次數,就把這個Timer移除,
否則就重置時間,繼續直到下一個時間的觸發,
代碼:
一個TimerMgr,Timer管理類,用來管理定時器系統:
using System.Collections.Generic;
using UnityEngine;
class TimerNode
{
public TimerMgr.TimerHandler callback;
public float repeatRate; // 定時器觸發的時間間隔;
public float time; // 第一次觸發要隔多少時間;
public int repeat; // 你要觸發的次數;
public float passedTime; // 這個Timer過去的時間;
public bool isRemoved; // 是否已經洗掉了
public int timerId; // 標識這個timer的唯一Id號;
}
public class TimerMgr
{
public delegate void TimerHandler();
private Dictionary<int, TimerNode> timers = null;//存放Timer物件
private List<TimerNode> removeTimers = null;//新增Timer快取佇列
private List<TimerNode> newAddTimers = null;//洗掉Timer快取佇列
private int autoIncId = 1;//每個Timer的唯一標示
//初始化Timer管理器
public void Init()
{
timers = new Dictionary<int, TimerNode>();
autoIncId = 1;
removeTimers = new List<TimerNode>();
newAddTimers = new List<TimerNode>();
}
/// <summary>
/// 以秒為單位呼叫方法methodName,然后在每個repeatRate重復呼叫,
/// </summary>
/// <param name="methodName">回呼函式</param>
/// <param name="time">延遲呼叫</param>
/// <param name="repeatRate">時間間隔</param>
/// <param name="repeat">重復呼叫的次數 小于等于0表示無限觸發</param>
public int Schedule(TimerHandler methodName, float time, float repeatRate, int repeat=0)
{
TimerNode timer = new TimerNode();
timer.callback = methodName;
timer.repeat = repeat;
timer.repeatRate = repeatRate;
timer.time = time;
timer.passedTime = timer.repeatRate; // 延遲呼叫
timer.isRemoved = false;
timer.timerId = autoIncId;
autoIncId++;
newAddTimers.Add(timer); // 加到快取佇列里面
return timer.timerId;
}
//移除Timers
public void Unschedule(int timerId)
{
if (!timers.ContainsKey(timerId))
{
return;
}
TimerNode timer = timers[timerId];
timer.isRemoved = true; // 先標記,不直接洗掉
}
//在Update里面呼叫
public void Update()
{
float dt = Time.deltaTime;
// 添加新的Timers
for (int i = 0; i < newAddTimers.Count; i++)
{
timers.Add(newAddTimers[i].timerId, newAddTimers[i]);
}
newAddTimers.Clear();
foreach (TimerNode timer in timers.Values)
{
if (timer.isRemoved)
{
removeTimers.Add(timer);
continue;
}
timer.passedTime += dt;
if (timer.passedTime >= (timer.time + timer.repeatRate))
{
// 做一次觸發
timer.callback();
timer.repeat--;
timer.passedTime -= (timer.time + timer.repeatRate);
timer.time = 0;
if (timer.repeat == 0)
{
// 觸發次數結束,將該洗掉的加入佇列
timer.isRemoved = true;
removeTimers.Add(timer);
}
}
}
// 清理掉要洗掉的Timer;
for (int i = 0; i < removeTimers.Count; i++)
{
timers.Remove(removeTimers[i].timerId);
}
removeTimers.Clear();
}
}
呼叫:
using System;
using System.Collections;
using UnityEngine;
public class Test : MonoBehaviour
{
TimerMgr timer;
int TimerID;
void Start()
{
//初始化
timer = new TimerMgr();
timer.Init();
//啟動定時器
TimerID = timer.Schedule(doSomething, 0, 2);
}
void Update()
{
timer.Update();
if (Input.GetKeyDown(KeyCode.W))
{
//關閉定時器
timer.Unschedule(TimerID);
}
}
private void doSomething()
{
Debug.Log("每2秒執行一次");
}
}
四、后言
這個定時器系統,可以用來處理在開發中可能用到的定時器需求,
不用再去Time.deltatime或者用協程去制作定時器了,
制作完定時器后,在其他專案也可以使用,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/292443.html
標籤:其他
