欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Unity 封装 倒计时(计时器,CountDownTimer),实现周期更新、技能冷却等功能

程序员文章站 2022-04-03 08:55:16
...

Unity 封装 倒计时(计时器,CountDownTimer),实现周期更新、技能冷却等功能

  在做游戏时,时常会用到计时器,例如技能冷却,物体一定时间后Inactive,还有周期更新(如一些连网请求,比较耗性能,不需要每一帧请求)等。网上也有不少关于这个计时器的文章,但基本都是需要配合Mono的(即需要绑定到MonoBehaviour组件上,需要Update(),或者协程来更新)。这里就实现基本独立的倒计时器。

  具体实现方法:

  1. 初始化:持续时间,是否循环,是否自动开始。
  2. 在每次需要查看计时器是否时间到,或者获取计时器剩余时间前,才更新计时器(当前时间 = 上一次更新计时器的时间 - 这一次更新的时间(Time.time))。
  3. 在每次更新前,会判断剩余时间是否已经小于等于0,
    1. 如果是
      1. 如果设定了循环计时,重置计时器时间,并返回重置前时间(小于等于0,时间到。)
      2. 如果没有设定计时器,停止更新,返回保持当前剩余时间。
    2. 如果不是,更新时间(每一帧至多更新一次,使用(Time.frameCount))。

Unity 封装 倒计时(计时器,CountDownTimer),实现周期更新、技能冷却等功能
(使用计时器更新冷却时间)


完整代码如下:

using UnityEngine;

/// <summary>
/// 倒计时器。
/// </summary>
public sealed class CountDownTimer
{
    public bool IsAutoCycle { get; private set; }                   // 是否自动循环(小于等于0后重置)
    public bool IsStoped { get; private set; }                      // 是否是否暂停了
    public float CurrentTime { get { return UpdateCurrentTime(); } }// 当前时间
    public bool IsTimeUp { get { return CurrentTime <= 0; } }       // 是否时间到
    public float Duration { get; private set; }                     // 计时时间长度

    private float lastTime;                                         // 上一次更新的时间
    private int lastUpdateFrame;                                    // 上一次更新倒计时的帧数(避免一帧多次更新计时)
    private float currentTime;                                      // 当前计时器剩余时间

    /// <summary>
    /// 构造倒计时器
    /// </summary>
    /// <param name="duration">起始时间</param>
    /// <param name="autocycle">是否自动循环</param>
    public CountDownTimer(float duration, bool autocycle = false, bool autoStart = true)
    {
        IsStoped = true;
        Duration = Mathf.Max(0f, duration);
        IsAutoCycle = autocycle;
        Reset(duration, !autoStart);
    }

    /// <summary>
    /// 更新计时器时间
    /// </summary>
    /// <returns>返回剩余时间</returns>
    private float UpdateCurrentTime()
    {
        if (IsStoped || lastUpdateFrame == Time.frameCount)         // 暂停了或已经这一帧更新过了,直接返回
            return currentTime;
        if (currentTime <= 0)                                       // 小于等于0直接返回,如果循环那就重置时间
        {
            if (IsAutoCycle)
                Reset(Duration,false);
            return currentTime;
        }
        currentTime -= Time.time - lastTime;
        UpdateLastTimeInfo();
        return currentTime;
    }

    /// <summary>
    /// 更新时间标记信息
    /// </summary>
    private void UpdateLastTimeInfo()
    {
        lastTime = Time.time;
        lastUpdateFrame = Time.frameCount;
    }

    /// <summary>
    /// 开始计时,取消暂停状态
    /// </summary>
    public void Start()
    {
        Reset(Duration,false);
    }

    /// <summary>
    /// 重置计时器
    /// </summary>
    /// <param name="duration">持续时间</param>
    /// <param name="isStoped">是否暂停</param>
    public void Reset(float duration,bool isStoped = false)
    {
        UpdateLastTimeInfo();
        Duration = Mathf.Max(0f, duration);
        currentTime = Duration;
        IsStoped = isStoped;
    }

    /// <summary>
    /// 暂停
    /// </summary>
    public void Pause()
    {
        UpdateCurrentTime();    // 暂停前先更新一遍
        IsStoped = true;
    }

    /// <summary>
    /// 继续(取消暂停)
    /// </summary>
    public void Continue()
    {
        UpdateLastTimeInfo();   // 继续前先更新当前时间信息
        IsStoped = false;
    }

    /// <summary>
    /// 终止,暂停且设置当前值为0
    /// </summary>
    public void End()
    {
        IsStoped = true;
        currentTime = 0f;
    }

    /// <summary>
    /// 获取倒计时完成率(0为没开始计时,1为计时结束)
    /// </summary>
    /// <returns></returns>
    public float GetPercent()
    {
        UpdateCurrentTime();
        if (currentTime <= 0 || Duration <= 0)
            return 1f;
        return 1f - currentTime / Duration;
    }

}

具体实现:

技能释放与冷却:

  实现上图的技能冷却,先初始化创建计时器。

    cdTimer = new CountDownTimer(coolDownTime);     // 创建计时器,coolDownTime为持续时间,默认不自动循环,默认创建后自动开始。

  在点击攻击时,如果计时器时间没结束,直接返回。否则就是结束冷却时间了,释放技能且重新开始计时器

    if (!cdTimer.IsTimeUp)          // 计时器没结束,直接返回
        return;
    cdTimer.Start();                // 重新计时
    Attack();                       // 攻击

技能按钮扇区图片的更新:

  更新技能按钮的扇区图片时,调用GetPercent(),来改变扇区裁剪度。

    if (!coolDownTimer.IsTimeUp)
    {
        buttonFullImg.fillAmount = 1 - coolDownTimer.GetPercent();  // 填充从1开始递减到0
        return false;
    }
    buttonFullImg.fillAmount = 0;   // 时间到设为0
    button.interactable = true;     // 按钮设为可以交互(在点击按钮时(释放技能请求)被设为不可交互)
    return true;

其他实现功能:

AI的周期检测判断(构造计时器时第二个参数为true。如每隔5秒判断是否与前5秒时的距离过近,如果是就认为是卡死状态,变换下一个巡逻点。)

AI的一定时间内追踪敌人(超过时间后停止追踪。)

周期连接网络请求(如每隔1秒做一次连网请求,比每一帧请求可以提高不少性能)


相关标签: unity 性能