记录一篇unity c#计时器的编写,使用方法
简介:采用工具式编写方法。主要思路就是先写一个时钟管理器脚本<TimerBaseManager>,用来处理计时,调用方法的处理。在写一个时钟脚本<TimerManager>继承时钟管理器脚本,最后在Updata里面调用这个时钟脚本就可以了。该脚本可以实现时间计时跟帧数计时两种。
1.首先是时间管理器脚本<TimerBaseManager>,里面主要是Time.unscaledTime这个数据,其他的都是简单的逻辑问题。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public interface IAnimatable {
void AdvanceTime();
}
/**时钟管理器[同一函数多次计时,默认会被后者覆盖,delay小于1会立即执行]*/
public abstract class TimerBaseManager<T> where T : new(){
private static T _instance;
public static T instance {
get {
if (_instance == null) {
_instance = new T();
}
return _instance;
}
}
private List<TimerHandler> _pool = new List<TimerHandler>();
/** 用数组保证按放入顺序执行*/
public List<TimerHandler> _handlers = new List<TimerHandler>();
private int _currFrame = 0;
private uint _index = 0;
public void AdvanceTime() {
_currFrame++;
for (int i = 0; i < _handlers.Count; i++) {
TimerHandler handler = _handlers[i];
long t = handler.userFrame ? _currFrame : currentTime;
if (t >= handler.exeTime) {
Delegate method = handler.method;
object[] args = handler.args;
if (handler.repeat) {
while (t >= handler.exeTime) {
handler.exeTime += handler.delay;
method.DynamicInvoke(args);
}
} else {
RemoveHandler(handler.method);
method.DynamicInvoke(args);
}
}
}
}
private object create(bool useFrame, bool repeat, bool cover, int delay, Delegate method, params object[] args) {
if (method == null) {
return null;
}
//如果执行时间小于1,直接执行
if (delay < 1) {
method.DynamicInvoke(args);
return -1;
}
TimerHandler handler;
TimerHandler coverHandler = _handlers.Find(han => han.method == method);
if (cover && coverHandler != null) {
handler = coverHandler;
_handlers.Remove(coverHandler);
} else {
if (_pool.Count > 0) {
handler = _pool[_pool.Count - 1];
_pool.Remove(handler);
} else {
handler = new TimerHandler();
}
}
handler.userFrame = useFrame;
handler.repeat = repeat;
handler.delay = delay;
handler.method = method;
handler.args = args;
handler.exeTime = delay + (useFrame ? _currFrame : currentTime);
_handlers.Add(handler);
return method;
}
/// /// <summary>
/// 定时执行一次(基于毫秒)
/// </summary>
/// <param name="delay">延迟时间(单位毫秒)</param>
/// <param name="method">结束时的回调方法</param>
/// <param name="cover">当method相同时是否覆盖</param>
/// <param name="args">回调参数</param>
public void DoOnce(int delay, Handler method, bool cover = false, params object[] args)
{
create(false, false, cover, delay, method, args);
}
public void DoOnce<T1>(int delay, Handler<T1> method, bool cover = false, params object[] args) {
create(false, false, cover, delay, method, args);
}
public void DoOnce<T1, T2>(int delay, Handler<T1, T2> method, bool cover = false, params object[] args) {
create(false, false, cover, delay, method, args);
}
public void DoOnce<T1, T2, T3>(int delay, Handler<T1, T2, T3> method, bool cover = false, params object[] args) {
create(false, false, cover, delay, method, args);
}
/// /// <summary>
/// 定时重复执行(基于毫秒)
/// </summary>
/// <param name="delay">延迟时间(单位毫秒)</param>
/// <param name="method">结束时的回调方法</param>
/// <param name="cover">当method相同时是否覆盖</param>
/// <param name="args">回调参数</param>
public void DoLoop(int delay, Handler method, bool cover = false, params object[] args) {
create(false, true, cover, delay, method, args);
}
public void DoLoop<T1>(int delay, Handler<T1> method, bool cover = false, params object[] args) {
create(false, true, cover, delay, method, args);
}
public void DoLoop<T1, T2>(int delay, Handler<T1, T2> method, bool cover = false, params object[] args) {
create(false, true, cover, delay, method, args);
}
public void DoLoop<T1, T2, T3>(int delay, Handler<T1, T2, T3> method, bool cover = false, params object[] args) {
create(false, true, cover, delay, method, args);
}
/// <summary>
/// 定时执行一次(基于帧率)
/// </summary>
/// <param name="delay">延迟时间(单位为帧)</param>
/// <param name="method">结束时的回调方法</param>
/// <param name="cover">当method相同时是否覆盖</param>
/// <param name="args">回调参数</param>
public void DoFrameOnce(int delay, Handler method, bool cover = false, params object[] args) {
create(true, false, cover, delay, method, args);
}
public void DoFrameOnce<T1>(int delay, Handler<T1> method, bool cover = false, params object[] args) {
create(true, false, cover, delay, method, args);
}
public void DoFrameOnce<T1, T2>(int delay, Handler<T1, T2> method, bool cover = false, params object[] args) {
create(true, false, cover, delay, method, args);
}
public void DoFrameOnce<T1, T2, T3>(int delay, Handler<T1, T2, T3> method, bool cover = false, params object[] args) {
create(true, false, cover, delay, method, args);
}
/// <summary>
/// 定时重复执行(基于帧率)
/// </summary>
/// <param name="delay">延迟时间(单位为帧)</param>
/// <param name="method">结束时的回调方法</param>
/// <param name="cover">当method相同时是否覆盖</param>
/// <param name="args">回调参数</param>
public void DoFrameLoop(int delay, Handler method, bool cover = false, params object[] args) {
create(true, true, cover, delay, method, args);
}
public void DoFrameLoop<T1>(int delay, Handler<T1> method, bool cover = false, params object[] args) {
create(true, true, cover, delay, method, args);
}
public void DoFrameLoop<T1, T2>(int delay, Handler<T1, T2> method, bool cover = false, params object[] args) {
create(true, true, cover, delay, method, args);
}
public void DoFrameLoop<T1, T2, T3>(int delay, Handler<T1, T2, T3> method, bool cover = false, params object[] args) {
create(true, true, cover, delay, method, args);
}
/// <summary>
/// 清理定时器
/// </summary>
/// <param name="method">method为回调函数本身</param>
public void RemoveHandler(Handler method) {
RemoveHandler(( Delegate )method);
}
public void RemoveHandler<T1>(Handler<T1> method) {
RemoveHandler(( Delegate )method);
}
public void RemoveHandler<T1, T2>(Handler<T1, T2> method) {
RemoveHandler(( Delegate )method);
}
public void RemoveHandler<T1, T2, T3>(Handler<T1, T2, T3> method) {
RemoveHandler(( Delegate )method);
}
private void RemoveHandler(Delegate method) {
List<TimerHandler> handler = _handlers.FindAll(t => t.method == method);
if (handler.Count > 0) {
handler.ForEach(a => {
_handlers.Remove(a);
a.clear();
if(Engine.Instance != null) {
Engine.Instance.StartCoroutine(AddPool(a));
} else {
_pool.Add(a);
}
});
}
}
public IEnumerator AddPool(TimerHandler handler) {
yield return new WaitForSeconds(1);
_pool.Add(handler);
}
/// <summary>
/// 清理所有定时器
/// </summary>
public void RemoveAllHandler() {
while (_handlers.Count > 0) {
RemoveHandler(_handlers[0].method);
}
}
/// <summary>
/// 游戏自启动运行时间(真实时间,不受加速限制),毫秒
/// </summary>
public virtual long currentTime {
get { return ( long )(Time.unscaledTime * 1000); }
}
/**定时处理器*/
public class TimerHandler {
/**执行间隔*/
public int delay;
/**是否重复执行*/
public bool repeat;
/**是否用帧率*/
public bool userFrame;
/**执行时间*/
public long exeTime;
/**处理方法*/
public Delegate method;
/**参数*/
public object[] args;
/**清理*/
public void clear() {
method = null;
args = null;
}
}
}
2.第二个是<TimerManager>脚本,该脚本继承时间管理器脚本<TimerBaseManager>,跟<IAnimatable>接口接口的实现方法在<TimerBaseManager>里面。接口方法也是调用计时的方法。
using System.Collections.Generic;
using UnityEngine;
public class TimerManager : TimerBaseManager<TimerManager>, IAnimatable {
public TimerManager() {
TimerManager.timerList.Add(this);
}
public static List<IAnimatable> timerList = new List<IAnimatable>();
public static long serviceTime;
public override long currentTime {
get { return ( long )(Time.unscaledTime * 1000); }
}
public static void RemoveTimer(IAnimatable timerMgr) {
timerList.Remove(timerMgr);
}
}
3.到这计时的逻辑计算写完了,剩下的计时调用了,最好把它挂在游戏场景中一直存在的物体上,最好try一下,看个人喜好。
using System;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Engine : MonoBehaviour {
void Awake()
{
}
void Start()
{
//在后台运行
Application.runInBackground = true;
//屏幕常亮
Screen.sleepTimeout = SleepTimeout.NeverSleep;
Application.targetFrameRate = 60;
//此对象在场景销毁的时候不删除
DontDestroyOnLoad(gameObject);
}
void Update()
{
TimerManager.timerList.ForEach(advance => {
#if !UNITY_EDITOR
try {
#endif
advance.AdvanceTime();
#if !UNITY_EDITOR
} catch (Exception err) {
Debug.LogError(err.Message);
}
#endif
});
}
}
4.最后添加一个调用方法,跟移除计时器的方法
//在100毫秒后调用改方法 ,调用方法答题上都差不多
TimerManager.instance.DoOnce(100,()=> { });
//移除该方法
TimerManager.instance.RemoveHandler(()=> { });
结尾:这样的好处可以类外创建一个ui计时器跟,战斗计时器。只要最后添加到TimerManager里面进行了。如果是定时重复执行的计时器,不需要执行是也可以删除计时器。
说明:之前在别人的博客上看到的,现在找不到那篇博客了,就自己记录一下,如果原作者看到,联系可以删除。非原创。