在Unity中,Time类的功能较少,例如Time.time在Awake等函数中无法获取,并且没有计时器概念等,大部分项目有计时器的需求,这篇文章将实现一个自己定义的计时器。
在开始写代码之前,先总结需求:
计时器应当具有以下操作:
并且具有这些属性:
设计完以上内容后,可以做这些事情:
总结完可以开始写了。
首先,添加引用
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秒内从大变小到完全消失。