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中基于时间轮算法的定时器。

unity 基于时间轮算法的定时器

原文地址: https://www.cveoy.top/t/topic/9ZS 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录