Unity计时器设计 - 基础计时器

Unity计时器设计 - 基础计时器

在Unity中,Time类的功能较少,例如Time.time在Awake等函数中无法获取,并且没有计时器概念等,大部分项目有计时器的需求,这篇文章将实现一个自己定义的计时器。


在开始写代码之前,先总结需求:

计时器应当具有以下操作:

  • 设置计时器
  • 开始和取消计时器
  • 暂停和继续计时器
  • 重置计时器
  • 一系列事件(OnStart,OnUpdate,OnCancel,OnEnd等)。

并且具有这些属性:

  • 计时器时间:开始、剩余、到时、总时长。
  • 计时器状态:未开始,运行中,暂停中。

设计完以上内容后,可以做这些事情:

  • 延时函数
  • 一切基于时间执行的概念,例如技能,类似cocos2d-x的动作系统等。
  • 很多情况下,计时器不希望随挂载对象被销毁时消失,所以Timer不继承于MonoBehaviour。

总结完可以开始写了。
首先,添加引用

using UnityEngine;
using System;

可以运行的最基础的计时器:

//注意不继承于MonoBehavior
public class Timer
{
    //开始时间
    public float StartTime { get; private set; }
    //持续时间
    public float Duration { get; private set; }
    //结束时间
    public float EndTime { get; private set; }
    //当前时间
    public float CurTime { get; private set; }

    //运行标识
    public bool IsStart { get; private set; }

    //开始和结束事件,这里直接用System.Action(相当于空返回值无参数委托)
    public Action OnStart { get; set; }
    public Action OnEnd { get; set; }
    public Action OnUpdate { get; set; }

    //构造函数,设置计时器
    public Timer(float duration)
    {
        Duration = duration;
    }

    //开始计时 Timer类不继承于MonoBehaviour,该方法不会在任何对象开始时被调用。
    public void Start()
    {
        IsStart = true;
        StartTime = Time.time;
        CurTime = StartTime;
        EndTime = StartTime + Duration;
        if(OnStart != null) OnStart();
    }

    //更新时间,并检查状态。Timer类不继承于MonoBehaviour,该方法将在中心计时器每帧调用。
    public void Update()
    {
        if (!IsStart) return;
        CurTime += Time.deltaTime;
        if(CurTime > EndTime)
        {
            End();
        }
        else if (OnUpdate != null) OnUpdate();
    }

    //计时器结束
    void End()
    {
        IsStart = false;
        if(OnEnd!= null) OnEnd();
    }
}
  • 新增取消
//取消事件
public Action OnCancel { get; set; }

//取消接口。
public void Cancel()
{
    IsStart = false;
    if(OnCancel != null) OnCancel();
}
  • 新增暂停和继续
//事件
public Action OnPause { get; set; }
public Action OnContinue { get; set; }

//属性
public bool IsPause { get; private set; }

//取消接口
public void Pause()
{
    IsPause = false;
    if(OnCancel != null) OnCancel();
}

//继续接口
public void Continue()
{
    IsPause = true;
    if(OnContinue!= null) OnContinue();
}

//Update逻辑需要更改
public void Update()
{
    if (!IsStart) return;
    CurTime += Time.deltaTime;
    if(IsPause) EndTime += Time.deltaTime;
    if(CurTime > EndTime)
    {
        End();
    }
    else if (OnUpdate != null) OnUpdate();
}
  • 新增重置
//重置接口
public void Reset()
{
    IsStart = false;
    IsPause = false;
}

一个简单的计时器就这样完成了。
但是我们往往需要获取计时器的各种属性,下面新增一些public属性:

  • 计时器已经完成的百分比,常用于
public float Ratio
{
    get
    {
        if(!IsStart)
        {
            return 0;
        }
        else
        {
            return 1 - (EndTime - CurTime) / Duration;
        }
    }
}
  • 计时器次数,可用于循环计时、检测是否已经完成等。(常规循环计时推荐新定义循环计时器)
//开始次数
public int StartCount { get; private set; }
//完成次数
public int FinishCount { get; private set; }

//Start修改
public void Start()
{
    IsStart = false;
    StartTime = Time.time;
    CurTime = StartTime;
    EndTime = StartTime + Duration;
    //新增这一行↓
    StartCount++;
    if(OnStart != null) OnStart();
}

//End修改
void End()
{
    IsStart = true;
    //新增这一行↓
    FinishCount++;
    if(OnEnd!= null) OnEnd();
}

简单的计时器设计完成啦!
但是还不可以运行,毕竟Update还不会主动调用。

我们可以先简单写一个中心计时器来解决问题:
新建CenterTimer.cs:

using UnityEngine;
using System.Collections.Generic;

//因为中心计时器应该只有一个,所以所有新定义的成员都应该为静态成员。
public Class CenterTimer : MonoBehaviour
{
    static List timers = new List();

    //添加计时器
    public static void AddTimer(Timer timer)    
    {
        timers.Add(timer);
    }

    //移除计时器
    public static void RemoveTimer(Timer timer)
    {
        timers.Remove(timer);
    }

    //每帧更新
    void Update()
    {
        foreach(var timer in timers)
        {
            timer.Update();
        }
    }
}

同时对Timer类做一点点修改:

public void Start()
{
    IsStart = true;
    StartTime = Time.time;
    CurTime = StartTime;
    EndTime = StartTime + Duration;
    //新增这一行↓
    CenterTime.AddTimer(this);
    StartCount++;
    if(OnStart!= null) OnStart();
}

//计时器结束
void End()
{
    IsStart = false;
    if(OnEnd!= null) OnEnd();
    //新增这一行↓
    CenterTimer.RemoveTimer(this);
}

public void Reset()
{
    IsStart = false;
    IsPause = false;
    //新增这一行↓
    CenterTimer.RemoveTimer(this);
}

使用方法如下:
Test.cs 挂在一个GameObject上
CenterTimer.cs 挂在摄像头上。

using UnityEngine;

public class Test : MonoBehaviour
{
    Timer timer;
    void Update()
    {
        if(Input.GetKeyDown(KeyCode.T))
        {
            timer = new Timer(3f);
            timer.OnUpdate += SetScale;
            timer.OnEnd += Hide;
            timer.Start();
        }
    }

    //缩放动画
    void SetScale()
    {
        float scale = 1 - timer.Ratio;
        gameObject.transform.localScale = new Vector3(scale, scale, scale);
    }

    void Hide()
    {
        gameObject.SetActive(false);
    }
}

执行游戏并按下T键,我们可以看到该对象在3秒内从大变小到完全消失。

你可能感兴趣的:(Unity)