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/9ZS 著作权归作者所有。请勿转载和采集!