最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Unity框架設(shè)計(jì):打造Timer定時(shí)器框架

2023-01-02 20:45 作者:博毅創(chuàng)為  | 我要投稿

1: 為什么我們要自己造輪子來做定時(shí)器系統(tǒng)

傳統(tǒng)的Unity做定時(shí)器的方式有三種,總結(jié)如下:

(1) 在組件類里面定義一個變量,每次Update的時(shí)候,累積時(shí)間,當(dāng)時(shí)間到達(dá)特定的閾值時(shí), 觸發(fā)函數(shù)調(diào)用。

void Update() { ? ? ? ?float dt = Time.deltaTime; ? ? ? ?this.passedTime += dt; ? ? ? ? if(this.passedTime >= 閾值) { ? ? ? ? ? ? ? ? ?doSomeThing(); ? ? ? ? ? ? ? ? ?this.passedTime -= 閾值; ? ? ? ? ?} }

(2) 使用協(xié)程,來做定時(shí)器等待。

yeild return new WaitForSecond(5.0f); // 5秒后做相關(guān)的事情 doSomeThing();

?

(3) 組件類MonoBehaviour 接口 Invoke,來做定時(shí)器 this.Invoke("doSomeThing", 5);

?

上面三種方法都有自己的缺點(diǎn),第(1)(2)種使用起來不方便, 第(3)種節(jié)點(diǎn)隱藏后不方便觸發(fā),不方便掉其他代碼里面的回調(diào)函數(shù),不方便傳遞參數(shù)。

所以我們在項(xiàng)目里面決定自己造輪子,來打造一個Timer模塊,處理游戲種的各種定時(shí)器需求。

?

2: 定時(shí)器系統(tǒng)的核心原理是什么?

每個定時(shí)器對象是一個Timer, 添加的時(shí)候交給定時(shí)器管理系統(tǒng), 定時(shí)器系統(tǒng)負(fù)責(zé)管理所有的定時(shí)器對象,每次游戲Update的時(shí)候,遍歷里面的每個定時(shí)器對象,

把它們過去的時(shí)間增加Time.deltaTime, 當(dāng)過去的時(shí)間達(dá)到定時(shí)器觸發(fā)時(shí)間的時(shí)候,觸發(fā)定時(shí)器調(diào)用, 如果達(dá)到Timer觸發(fā)次數(shù),就把這個Timer移除,

否則就重置時(shí)間,繼續(xù)直到下一個時(shí)間的觸發(fā)。

?

3: 如何設(shè)計(jì)的接口

(1) 編寫TimerMgr Timer管理的一個單例,在游戲初始化的時(shí)候創(chuàng)建出來。

(2) 添加Init()接口, 在初始化的時(shí)候調(diào)用Init接口,對TimerMgr所要的數(shù)據(jù)與狀態(tài)進(jìn)行初始化。

(3) 編寫Update函數(shù),在每次Update的時(shí)候迭代Timer,來負(fù)責(zé)Timer的觸發(fā)。

(4) 提供Schedule系列接口,來給用戶加一個定時(shí)器,Schedule系列接口如下:

【參數(shù)】: func Timer時(shí)間到了以后調(diào)用的回調(diào)函數(shù);

param: 用戶給Timer觸發(fā)回調(diào)函數(shù)傳遞的參數(shù), 類型object,可以傳任意類型的數(shù)據(jù);

repeat: timer 要重復(fù)調(diào)用的次數(shù);

duration: 每次timer調(diào)用的時(shí)間間隔;

delay: 第一次timer觸發(fā)在多少秒以后;

【返回值】: Timer ID號,根據(jù)這個ID號可以在TimerMgr中查到這個Timer對象,用來作為刪除Timer的憑證;

public int Schedule(TimerHandler func, object param, int repeat, float duration, float delay = 0.0f);

(5)提供Unschedule系列接口:

【參數(shù)】: timerId, Timer的唯一標(biāo)識Id號,根據(jù)這個Id號取消Timer

public void Unschedule(int timeId);

?

4: 具體實(shí)現(xiàn)

(1) Timer對象,每一個Timer就是一個Timer對象,這個Timer對象保存到TimerMgr里面,數(shù)據(jù)結(jié)構(gòu)定義如下:

class TimerNode { public TimerMgr.TimerHandler callback; public float duration; // 定時(shí)器觸發(fā)的時(shí)間間隔; public float delay; // 第一次觸發(fā)要隔多少時(shí)間; public int repeat; // 你要觸發(fā)的次數(shù); public float passedTime; // 這個Timer過去的時(shí)間; public object param; // 用戶要傳的參數(shù) public bool isRemoved; // 是否已經(jīng)刪除了 public int timerId; // 標(biāo)識這個timer的唯一Id號; }

(2) TimerMgr 定義一個委托類型,用來作為回調(diào)函數(shù)的類型:

public delegate void TimerHandler(object param);

(3) 定義一個字典,用來存放timeId到Timer對象的一個映射,存放游戲里面所有的Timer對象。

private Dictionary<int, TimerNode> timers = null;

(4) 兩個緩存隊(duì)列, 一個是新增Timer的緩存列表,一個是已刪除Timer的緩存列表,這個機(jī)制非常的重要,當(dāng)我們新增或刪除Timer的時(shí)候,

有可能在遍歷Timer 觸發(fā)回調(diào)的里面來添加或刪除一個Timer, 如果直接操作map里面的Timer,這樣就會改變遍歷時(shí)候的結(jié)構(gòu),導(dǎo)致出錯,所以我們添加或

刪除Timer的時(shí)候, 先放到緩存隊(duì)列,等Update里面Timer遍歷完了以后,再來添加或移除Timer到TimerMgr的map里面。

private List<TimerNode> removeTimers = null; private List<TimerNode> newAddTimers = null;

(5) 每個Timer有一個唯一的Id號,所以我們設(shè)置一個自增長的Id號機(jī)制,在TimerMgr里面加數(shù)據(jù)成員 autoIncId, 每次創(chuàng)建一個Timer,就把a(bǔ)utoIncId當(dāng)前的值作為Id號,并往后加1。

private int autoIncId = 1;

?

(6) 初始化TimerMgr:

public void Init() { this.timers = new Dictionary<int, TimerNode>(); this.autoIncId = 1; this.removeTimers = new List<TimerNode>(); this.newAddTimers = new List<TimerNode>(); }

?

(7) Schedule系列接口, 其余的都調(diào)用這個接口,傳特殊的參數(shù):

// [repeat < 0 or repeat == 0 表示的是無限觸發(fā)] ? ?public int Schedule(TimerHandler func, object param, int repeat, float duration, float delay = 0.0f) ? ?{ ? ? ? ?TimerNode timer = new TimerNode(); ? ? ? ?timer.callback = func; ? ? ? ?timer.param = param; ? ? ? ?timer.repeat = repeat; ? ? ? ?timer.duration = duration; ? ? ? ?timer.delay = delay; ? ? ? ?timer.passedTime = timer.duration; ?// 第一次只受delay影響,所以過去的時(shí)間為duration ? ? ? ?timer.isRemoved = false; ? ? ? ?timer.timerId = this.autoIncId; ? ? ? ?this.autoIncId++; ? ? ? ?// this.timers.Add(timer.timerId, timer); ? ? ? ?this.newAddTimers.Add(timer); // 加到緩存隊(duì)列里面,不直接加入到定時(shí)器Map ? ? ? ?return timer.timerId; ? ?}

(8) Unschedule系列接口:

public void Unschedule(int timerId) { if (!this.timers.ContainsKey(timerId)) { return; } TimerNode timer = this.timers[timerId]; timer.isRemoved = true; ?// 先標(biāo)記,不直接刪除 }

(9) TimerMgr每次Update迭代Timer,并觸發(fā)

private void Update() ? ?{ ? ? ? ?float dt = Time.deltaTime; ? ? ? ?// 把新加進(jìn)來的放入到我們的表里面來 ? ? ? ?for (int i = 0; i < this.newAddTimers.Count; i++) { ? ? ? ? ? ?this.timers.Add(this.newAddTimers[i].timerId, this.newAddTimers[i]); ? ? ? ?} ? ? ? ?this.newAddTimers.Clear(); ? ? ? ?// end ? ? ? ? ? ? ? ?// 遍歷迭代過程中不涉及添加or刪除,避免改變map結(jié)構(gòu)引發(fā)迭代錯誤 ? ? ? ?foreach (TimerNode timer in this.timers.Values) { ? ? ? ? ? ?if (timer.isRemoved) { ? ? ? ? ? ? ? ?this.removeTimers.Add(timer); ? ? ? ? ? ? ? ?continue; ? ? ? ? ? ?} ? ? ? ? ? ?timer.passedTime += dt; ? ? ? ? ? ?if (timer.passedTime >= (timer.delay + timer.duration)) { ? ? ? ? ? ? ? ?// 做一次觸發(fā) ? ? ? ? ? ? ? ?timer.callback(timer.param); ? ? ? ? ? ? ? ?timer.repeat--; ? ? ? ? ? ? ? ?timer.passedTime -= (timer.delay + timer.duration); ? ? ? ? ? ? ? ?timer.delay = 0; // 很重要; ? ? ? ? ? ? ? ?if (timer.repeat == 0) { // 觸發(fā)次數(shù)結(jié)束,刪除這個Timer, 但不馬上刪除; ? ? ? ? ? ? ? ? ? ?timer.isRemoved = true; ? ? ? ? ? ? ? ? ? ?this.removeTimers.Add(timer); ? ? ? ? ? ? ? ?} ? ? ? ? ? ? ? ?// end ? ? ? ? ? ?} ? ? ? ?} ? ? ? ?// 結(jié)束以后,清理掉要刪除的Timer; ? ? ? ?for (int i = 0; i < this.removeTimers.Count; i++) { ? ? ? ? ? ?this.timers.Remove(this.removeTimers[i].timerId); ? ? ? ?} ? ? ? ?this.removeTimers.Clear(); ? ? ? ?// end ? ?}


Unity框架設(shè)計(jì):打造Timer定時(shí)器框架的評論 (共 條)

分享到微博請遵守國家法律
雅江县| 杂多县| 敖汉旗| 勐海县| 蓝田县| 银川市| 民和| 奇台县| 阿合奇县| 龙山县| 石渠县| 盘锦市| 磴口县| 积石山| 湖州市| 高碑店市| 视频| 清镇市| 沿河| 中方县| 韩城市| 西林县| 垦利县| 饶阳县| 普安县| 博白县| 秦安县| 黎川县| 霍城县| 南昌县| 澄城县| 田东县| 右玉县| 永和县| 信宜市| 天镇县| 翁牛特旗| 大新县| 景谷| 长春市| 正阳县|