Unity 时间轮算法定时器实现:高效精准的计时方案
Unity 中可以使用 'Time.deltaTime' 来计算每一帧的时间差,从而实现定时器功能。但是这种方式在游戏逻辑比较复杂的情况下可能会出现问题,因为不同的物体的逻辑帧数可能不同,而使用统一的时间差会导致定时器的误差增加。
为了避免这个问题,可以使用时间轮算法来实现定时器。时间轮算法是一种常用的定时器实现方式,它将时间划分成一定数量的时间槽,每个时间槽代表一个时间单位,比如 1 秒或者 10 毫秒。每个时间槽中存放一个链表,链表中保存需要在该时间点执行的所有任务。
每个时间槽都有一个指针,指向下一个时间槽。每当时间轮转动一格,就会执行当前时间槽中的任务,并将任务移到下一个时间槽中。这样就可以实现定时器的功能,而且不会出现误差累积的问题。
Unity 中可以使用 Coroutine 和 yield 来实现时间轮算法的定时器。具体的实现方式可以参考以下代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Timer
{
private LinkedList<Coroutine>[][] timeWheel;
private int currentSlot;
private int slotCount;
private float slotTime;
private MonoBehaviour monoBehaviour;
public Timer(MonoBehaviour monoBehaviour, int slotCount, float slotTime)
{
this.monoBehaviour = monoBehaviour;
this.slotCount = slotCount;
this.slotTime = slotTime;
timeWheel = new LinkedList<Coroutine>[slotCount][];
for(int i = 0; i < slotCount; i++)
{
timeWheel[i] = new LinkedList<Coroutine>[2];
timeWheel[i][0] = new LinkedList<Coroutine>();
timeWheel[i][1] = new LinkedList<Coroutine>();
}
currentSlot = 0;
monoBehaviour.StartCoroutine(Update());
}
public void AddTimer(float delay, System.Action callback)
{
int slot = Mathf.CeilToInt(delay / slotTime) % slotCount;
LinkedListNode<Coroutine> node = timeWheel[(currentSlot + slot) % slotCount][0].AddLast(null);
monoBehaviour.StartCoroutine(TimerCoroutine(delay, node, callback));
}
private IEnumerator TimerCoroutine(float delay, LinkedListNode<Coroutine> node, System.Action callback)
{
yield return new WaitForSeconds(delay);
callback?.Invoke();
node.Value = null;
}
private IEnumerator Update()
{
while (true)
{
yield return new WaitForSeconds(slotTime);
LinkedList<Coroutine>[] currentSlotList = timeWheel[currentSlot];
LinkedList<Coroutine>[] nextSlotList = timeWheel[(currentSlot + 1) % slotCount];
foreach (LinkedList<Coroutine> list in currentSlotList)
{
foreach (Coroutine coroutine in list)
{
if (coroutine != null)
{
monoBehaviour.StopCoroutine(coroutine);
}
}
list.Clear();
}
foreach (LinkedList<Coroutine> list in nextSlotList)
{
foreach (Coroutine coroutine in list)
{
if (coroutine != null)
{
list.AddLast(coroutine);
}
}
}
currentSlot = (currentSlot + 1) % slotCount;
}
}
}
在上面的代码中,我们使用了一个二维数组来表示时间轮,每个元素都是一个链表,表示当前时间槽中需要执行的任务。在 AddTimer 方法中,我们根据延迟时间计算出需要添加到哪个时间槽,在该时间槽中添加一个占位符 Task,并启动一个 Coroutine 并将其保存在 Task 中。
在 Update 方法中,我们首先清空当前时间槽中的所有 Task,然后将下一个时间槽中的所有 Task 移到当前时间槽中,并将占位符 Task 替换为对应的 Coroutine。
这样就实现了 Unity 中基于时间轮算法的定时器。
原文地址: https://www.cveoy.top/t/topic/mzXc 著作权归作者所有。请勿转载和采集!