记录一篇unity c#计时器的编写,使用方法

                            记录一篇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里面进行了。如果是定时重复执行的计时器,不需要执行是也可以删除计时器。

说明:之前在别人的博客上看到的,现在找不到那篇博客了,就自己记录一下,如果原作者看到,联系可以删除。非原创。

猜你喜欢

转载自blog.csdn.net/weixin_42129718/article/details/108821266