timeScale Lerp for Unity 3D: Custom Time Manager

timeScale Lerp – Custom Time Manager

By now I need a timeScale Lerp and this need to be Time.deltaTime independent so, for anyone who needs this too, here’s what I’ve got (C#). You can also use it to create slow-motion effects and control the play /pause of your game (assuming that you are using a Time.timeScale dependent approach):

/// <summary>
/// Time Manager.
/// You can also use this Manager as a Pause / Play Manager
/// Time.deltaTime independent Time.timeScale Lerp.
/// Dependencies: A Singleton class
/// Author: Fliperamma
/// </summary>
///
/// 
using UnityEngine;
using System.Collections;

namespace Fliperamma
{
    public class FTimeManager : FSingleton<FTimeManager>
	{

		public delegate void StatusChange (bool isPaused,bool willPause);

		public static event StatusChange OnStatusChanged;

		/// <summary>
		/// FTimeManager is Paused or not
		/// </summary>
		[SerializeField]
		private bool _isPaused = false;
		/// <summary>
		/// FTimeManager is Fading (from _minScale to _scale or vice-versa)
		/// </summary>
		[SerializeField]
		private bool _isFading = false;
		/// <summary>
		/// FTimeManager will pause after fading (is going from _scale to _minScale)
		/// </summary>
		[SerializeField]
		private bool _willPause = false;
		[SerializeField]
		private float _scale = 1f;
		private float _fadeToScaleDifference = 0f;
		private float _scaleToFade = 0f;
		[SerializeField]
		private float _deltaTime = 0f;
		private float _lastTime = 0f;
		private float _maxScale = 3f;
		private float _minScale = 0f;
		private bool _fadeToScaleIsGreater = false;
		private Coroutine _faderCoroutine;

		
		void Awake ()
		{
			Scale = Time.timeScale;
            Instance.StartCoroutine (UpdateDeltaTime ());
		}


		public bool WillPause {
			get {
				return _willPause;
			}
		}

		public bool IsFading {
			get {
				return _isFading;
			}
		}

		public bool IsPaused {
			get {
				return _isPaused;
			}
		}


		/// <summary>
		/// Time.timeScale independent deltaTime
		/// </summary>
		/// <value>
		/// time.timeScale independent Delta Time
		/// </value>
		public float DeltaTime {
			get {
				return _deltaTime;
			}
		}

		/// <summary>
		/// Getter and Setter for the FTimeManager Scale (time.timeScale). This will set IsPaused automatically
		/// </summary>
		/// <value>
		/// Scale (Time.timeScale)
		/// </value>
		public float Scale {
			get {
				return _scale;
			}
			set {
				_scale = value;
				_scale = _scale < _minScale ? _minScale : _scale > _maxScale ? _maxScale : _scale;
				Time.timeScale = _scale;
				_isPaused = _scale <= 0.001f;
				if (_isPaused) {
					_scale = 0f;
					_willPause = false;

					FireStatusChangeEvent ();
				}
			}
		}

		void FireStatusChangeEvent ()
		{
			if (OnStatusChanged != null) {
				OnStatusChanged (_isPaused, _willPause);
			}
		}

		/// <summary>
		/// Pause toggle (Changes the "IsPaused" flag from true to false and vice-versa)
		/// </summary>
		/// </param>
		/// <param name='time'>
		/// Time until Pause or Play
		/// </param>
		/// <param name='playScale'>
		/// Play scale.
		/// </param>
		public void TogglePause (float time = 0f, float playScale = 1f)
		{
			StopFader ();
			// WillPause == true means that a "Pause" was already called: this will make "WillPause" change to false and call "Play" function. 
			// Else just toggle.
			_willPause = _willPause != true && !_isPaused;
			if (_willPause) {
				Pause (time);
			} else {
				Play (time, playScale);
			}
		}

		void StopFader ()
		{
			if (_faderCoroutine != null) {
				StopCoroutine (_faderCoroutine);
			}
		}

		/// <summary>
		/// FTimeManager Pause
		/// </summary>
		/// <param name='time'>Fading time until Time.timeScale == 0f</param>
		public void Pause (float time = 0f)
		{
			if (time == 0f) {
				StopFader ();
				_willPause = false;
				Scale = 0f;
			} else {
				_willPause = true;
				FadeTo (0f, time);
			}

			FireStatusChangeEvent ();
		}

		/// <summary>
		/// FTimeManager Play 
		/// </summary>
		/// <param name='time'>
		/// Fading time until Time.timeScale == scale param
		/// </param>
		/// <param name='scale'>
		/// Final scale for Time.timeScale
		/// </param>
		public void Play (float time = 0f, float scale = 1f)
		{
			if (time == 0f) {
				StopFader ();
				Scale = scale;
			} else {
				FadeTo (scale, time);
			}

			FireStatusChangeEvent ();
		}

		/// <summary>
		/// FTimeManager Scale Fading.
		/// </summary>
		/// <param name='scale'>
		/// The final Time.timeScale
		/// </param>
		/// <param name='time'>
		/// The transition time to reach the desired scale
		/// </param>
		public void FadeTo (float scale, float time)
		{
			StopFader ();
			_scaleToFade = scale;
			_fadeToScaleDifference = scale - _scale;
			_fadeToScaleIsGreater = _fadeToScaleDifference > 0f;
			float scalePerFrame = _fadeToScaleDifference / time;
			_faderCoroutine = Instance.StartCoroutine (FadeStepper (scalePerFrame));
		}

		/// <summary>
		/// Coroutine to fade the Unity's timeScale
		/// </summary>
		IEnumerator FadeStepper (float scalePerFrame)
		{
			bool achieved = false;
			_isFading = true;
			while (achieved == false) {
				Scale += scalePerFrame * _deltaTime;
				if (_fadeToScaleIsGreater) {
					achieved = _scale >= _scaleToFade;
				} else {
					achieved = _scale <= _scaleToFade;
				}
				yield return new WaitForEndOfFrame ();
			}
			Scale = _scaleToFade;
			_isFading = false;
			_willPause = false;
		}

		/// <summary>
		/// Updating our internal _deltaTime
		/// </summary>
		IEnumerator UpdateDeltaTime ()
		{
			while (true) {
				float timeSinceStartup = Time.realtimeSinceStartup;
				_deltaTime = timeSinceStartup - _lastTime;
				_lastTime = timeSinceStartup;
				yield return null;
			}
		}

	}
}
FTimeManager