The first step is to implement the UpdateRegister function, inherited from MonoBehavior, and use Unity’s own Update method. Functionality provided to external registration and deregistration
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; namespace JunFramework { public enum UpdateMode { Update, FixedUpdate, LateUpdate } public enum TimeMode { Scaled, UnScaled } public class UpdateRegister : MonoBehaviour { /// <summary> /// update /// </summary> private static Dictionary<int, Action> update = new Dictionary<int, Action>(); /// <summary> /// fixedupfate /// </summary> private static Dictionary<int, Action> fixedupdate = new Dictionary<int, Action>(); /// <summary> /// lateupdate /// </summary> private static Dictionary<int, Action> lateupdate = new Dictionary<int,Action>(); /// <summary> /// Globally unique update subscript /// </summary> private static int updateIndex = 0 ; /// <summary> /// Whether to initialize /// </summary> private static bool isInit = false ; /// <summary> /// has been initialized /// </summary> public static bool IsInit { get => isInit; } /// <summary> /// register /// </summary> /// <param name="mode"></param> /// <param name="handler"></param> /// <returns></returns> public static int Register(UpdateMode mode, Action handler) { ++updateIndex; switch (mode) { case UpdateMode.Update: update.Add(updateIndex, handler); break; case UpdateMode.FixedUpdate: fixedupdate.Add(updateIndex, handler); break; case UpdateMode.LateUpdate: lateupdate.Add(updateIndex, handler); break; } return updateIndex; } /// <summary> /// Unregister according to updateindex /// </summary> /// <param name="mode"></param> /// <param name="index"></param> public static void Cancle(UpdateMode mode, int index) { switch (mode) { case UpdateMode.Update: update.Remove(index); break; case UpdateMode.FixedUpdate: fixedupdate.Remove(index); break; case UpdateMode.LateUpdate: lateupdate.Remove(index); break; } } private void Awake() { isInit = true; } private void Update() { using (var e = update.GetEnumerator()) { if (e.MoveNext()) { e.Current.Value?.Invoke(); } } } private void FixedUpdate() { using (var e = fixedupdate.GetEnumerator()) { if (e.MoveNext()) { e.Current.Value?.Invoke(); } } } private void LateUpdate() { using (var e = lateupdate.GetEnumerator()) { if (e.MoveNext()) { e.Current.Value?.Invoke(); } } } } }
The second step is to implement the Timer object, store the information passed in by the caller, and get the Timer status (is it completed? Is it working? Current progress?). Callable start, end, pause, resume, reset, finish functions
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; namespace JunFramework.Timer { public class Timer { #region timer setting /// <summary> /// Timer is automatically released /// </summary > public static bool IsAutoRelease = true ; /// <summary> /// Timer update mode /// </summary> public static UpdateMode TimerUpdateMode = UpdateMode.FixedUpdate; /// <summary> /// Whether the Timer time is affected by Unity scaling /// </summary> public static TimeMode TimerScaledModel = TimeMode.UnScaled; #endregion private float currentTime = 0f; // Current time ( 0 -- steptime) private int currentRepeat = 0 ; // current repeat times private float stepTime = 0f; // interval private int repeatTimes = 0 ; // repeat times private bool isLoop = false; // Whether to loop private bool isDone = false ; // Whether to complete private bool isStart = false ; // Whether to start private int updateIndex = 0 ; // The subscript obtained by update registration is used to remove update registration private Action callback; // Callback private float timeAcceleration; // Increased time per frame /// <summary> /// Is it complete /// </summary> public bool IsDone { get =>isDone; } /// <summary> /// Current Progress /// </summary> public float Progress { get => currentTime / stepTime; } /// <summary> /// Is it working /// </ summary> summary> public bool IsWokring { get => !isDone && isStart; } /// <summary> /// /// </summary> /// <param name="stepTime"> Interval duration </param> // / <param name="repeatTimes">times of repetition</param> /// <param name="callback">call-back</param> public Timer(float stepTime, int repeatTimes, Action callback) { this.currentTime = 0f; this.currentRepeat = 0; this.stepTime = stepTime; this.repeatTimes = repeatTimes; this.isLoop = false; this.isDone = false; this.timeAcceleration = GetPerTime(); this.callback = callback; } /// <summary> /// /// </summary> /// <param name="stepTime">interval</param> /// <param name="callback">call-back</param> public Timer(float stepTime, Action callback) { this.currentTime = 0f; this.currentRepeat = 0; this.stepTime = stepTime; this.repeatTimes = 1; this.isLoop = false; this.isDone = false; this.timeAcceleration = GetPerTime(); this.callback = callback; } /// <summary> /// /// </summary> /// <param name="stepTime">Interval</param> /// <param name="isLoop">Loop or not</param> /// <param name="callback">call-back</param> public Timer(float stepTime, bool isLoop, Action callback) { this.currentTime = 0f; this.currentRepeat = 0; this.stepTime = stepTime; this.isLoop = isLoop; if (!isLoop) { this.repeatTimes = 1; } this.timeAcceleration = GetPerTime(); this.isDone = false; this.callback = callback; } /// <summary> /// Update every frame /// </summary> /// <param name="addTime"> Added time per frame </param> private void Update( float addTime) { if (isDone) return ; if (! isStart) return ; currentTime += addTime; if (currentTime >= stepTime) { callback ? .Invoke(); currentTime = 0f; if (!isLoop) { ++currentRepeat; if (currentRepeat >= repeatTimes) { isDone = true; if (IsAutoRelease) { Stop(); } } } } } /// <summary> /// TimerStart /// </summary> public void Start() { if(! UpdateRegister.IsInit) { Debug.LogWarning( " UpdateRegister is not init, so this action will not work, please init UpdateRegister first!!! " ); return ; } isStart = true ; this .updateIndex = UpdateRegister.Register(TimerUpdateMode , () => this .Update(timeAcceleration)); } /// <summary> /// Stop the timer (if you just want to pause the available Pause, the stop operation cannot be resumed) /// </summary> public void Stop( ) { isStart = false; isDone = true; UpdateRegister.Cancle(TimerUpdateMode, this.updateIndex); } /// <summary> /// Pause /// </summary> public void Pause() { isStart = false; } /// <summary> /// Continue /// </summary> public void Resume() { isStart = true; } /// <summary> /// Reset /// </summary> public void Reset() { isStart = false ; isDone = false ; currentTime = 0f; currentRepeat = 0 ; } /// <summary> / // No need to wait, complete directly /// </summary> public void Complete() { if (isLoop) return; isDone = true ; int tempRepeat = repeatTimes; while (tempRepeat > 0 ) { callback ? .Invoke(); -- tempRepeat; } Stop(); } /// <summary> /// Obtained by update mode and time mode Update time per frame /// </summary> /// <returns></returns> private float GetPerTime() { float resultTime = 0f; if (TimerScaledModel == TimeMode.Scaled && TimerUpdateMode == UpdateMode.FixedUpdate) { resultTime = Time.fixedDeltaTime; } else if (TimerScaledModel == TimeMode.UnScaled && TimerUpdateMode == UpdateMode.FixedUpdate) { resultTime = Time.fixedUnscaledDeltaTime; } else if (TimerScaledModel == TimeMode.Scaled && TimerUpdateMode == UpdateMode.Update) { resultTime = Time.deltaTime; } else if (TimerScaledModel == TimeMode.UnScaled && TimerUpdateMode == UpdateMode.FixedUpdate) { resultTime = Time.unscaledDeltaTime; } return resultTime; } } }
The third step is to simply write the test code
using System.Collections; using System.Collections.Generic; using UnityEngine; using JunFramework.Timer; public class TimerExample : MonoBehaviour { private Timer timer = null; // Start is called before the first frame update void Start() { Timer.IsAutoRelease = true; Timer.TimerScaledModel = JunFramework.TimeMode.UnScaled; Timer.TimerUpdateMode = JunFramework.UpdateMode.FixedUpdate; //Example1 //int i = 0; //Timer timer = new Timer(2, 2, () => Debug.Log(i++)); //timer.Start(); //Example2 //timer = new Timer(2, true, () => Debug.Log("Exeture")); //timer.Start(); //Example3 timer = new Timer(2, 3, () => Debug.Log("LookMe")); timer.Start(); timer.Complete(); timer.Reset(); timer.Start(); } // Update is called once per frame void Update() { //Example2 //if (Input.GetKeyDown(KeyCode.Space)) //{ // timer.Pause(); //} //else if (Input.GetKeyDown(KeyCode.A)) //{ // timer.Resume(); //} } }