Tag Archives: How to Implement Timer

Unity: How to Implement Timer

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();
        //}
    }
}