Time, TimeScale & DeltaTime in Editor

When you are building some editor tools for your games, you sometimes need to do some timer. Unfortunatly Unity doesn't provide any time managment solution in editor mode.

I have created a TimeEditor Class which reflects the behaviour of the Time class in editor mode. It allows us to have a deltaTime and a timeScale like in play mode. It works in editor, in play mode and in Build of course.

How to use:

In your project, change every occurence of
Time.time
by
TimeEditor.time
and that's it !

Simply put the script in an empty gameObject in the hierarchy in the scene where you use the TimeEditor.

Then at any time in a script (in an editor Script, or in a normal script), call

EditorTime.time
to have the timeScale Dependent time ! That means you can also change the timeScale in editor by doing
EditorTime.timeScale = 0.5f
. Note that you can set the timeScale to anything you want, but in play mode, a negatif timeScale is not allowed.


For Exemple, when I move a player at each Update, I usually use something like this:
_player.Move(speed * Time.deltaTime);

Now If I want my object to move also in editor, you just have to put
[ExecuteInEditMode]
just before my class, and use the TimeEditor class:
_player.Move(speed * TimeEditor.deltaTime);



The Script Execution Order of the
TimeEditor
class has to be before the default time of unity.

Go to

Edit > Project Settings
and add it to the list in the tab
Script Execution Order
, don't forget to press apply.




The TimeEditor script uses the class Singleton-Patern
SingletonMono<T>
. You do not necessary need it for this class to work, but it is convenient to have it. If you don't use it, replace
SingletonMono<TimeEditor>
by
Monobehaviour
, else download also the file
SingletonMono.cs
.



Scripts:

TimeEditor.cs
Download
Copy
´╗┐#if UNITY_EDITOR
    using UnityEditor;
#endif
using UnityEngine;

/// <summary>
/// Use this class instead of the Time class if you want to use time in editor
/// use TimeEditor.time instead of Time.time will work in editor AND in play mode
/// (and in runtime of course)
///
/// use TimeEditor.time instead of Time.time
/// use TimeEditor.timeScale instead of Time.timeScale
/// use TimeEditor.unscaledTime instead of Time.unscaledTime
/// use TimeEditor.deltaTime instead of Time.deltaTime
/// use TimeEditor.unscaledDeltaTime instead of Time.unscaledDeltaTime
/// 
/// In editor, you can set the timeScale to negative to backward time !
/// but warning: a negative timescale doen't work in play mode
/// </summary>
[ExecuteInEditMode]
public class TimeEditor : SingletonMono<TimeEditor>
{
    private static float _editModeLastUpdate;   //the last time the controller was updated while in Edit Mode
    private static float _timeScale = 1f;       //current timeScale in both editor & play mode
    private static float _currentTime;          //current time timeScale-dependent (like Time.time but in editMode)
    private static float _currentTimeIndependentTimeScale;  //current time timeScale-independent (like Time.unscaledTime but in editMode)
    private static float _currentDeltaTime;     //current deltaTime timeScale-dependent (like Time.deltaTime but in editMode)
    private static float _currentUnscaledDeltaTime; //current deltaTime timeScale-independent (like Time.unscaledDeltaTime but in editMode)

    #region public properties

    /// <summary>
    /// get the current timeScale
    /// </summary>
    public static float timeScale
    {
        get
        {
            return (_timeScale);
        }
        set
        {
            if (value != _timeScale)
            {
                _timeScale = value;
                Time.timeScale = Mathf.Max(0, _timeScale);  
            }
        }
    }
    /// <summary>
    /// the time (timeScale dependent) at the begening of this frame (Read only). This is the time in seconds since the start of the game / the editor compilation
    /// </summary>
    public static float time
    {
        get
        {
            return (_currentTime);
        }
    }

    /// <summary>
    /// The timeScale-independant time for this frame (Read Only). This is the time in seconds since the start of the game / the editor compilation
    /// </summary>
    public static float unscaledTime
    {
        get
        {
            return (_currentTimeIndependentTimeScale);
        }
    }

    /// <summary>
    /// The completion time in seconds since the last from (Read Only)
    /// </summary>
    public static float deltaTime
    {
        get
        {
            return (_currentDeltaTime);
        }
    }

    /// <summary>
    /// The timeScale-independent interval in seconds from the last frames to the curren tone
    /// </summary>
    public static float unscaledDeltaTime
    {
        get
        {
            return (_currentUnscaledDeltaTime);
        }
    }
    #endregion

    #region private functions

    /// <summary>
    /// subscribe to EditorApplication.update to be able to call EditorApplication.QueuePlayerLoopUpdate();
    /// We need to do it because unity doens't update when no even are triggered in the scene.
    /// 
    /// Then, Start the timer, this timer will increase every frame (in play or in editor mode), like the normal Time
    /// </summary>
    private void OnEnable()
    {
#if UNITY_EDITOR
        EditorApplication.update += EditorUpdate;
#endif
        _editModeLastUpdate = Time.realtimeSinceStartup;
        StartCoolDown();
    }

    protected void OnDisable()
    {
#if UNITY_EDITOR
        EditorApplication.update -= EditorUpdate;
#endif
    }

    /// <summary>
    /// at start, we initialize the current time
    /// </summary>
    private void StartCoolDown()
    {
        _currentTime = 0;
        _currentTimeIndependentTimeScale = 0;
        _editModeLastUpdate = Time.realtimeSinceStartup;
    }

    /// <summary>
    /// called every frame, add delta time to the current timer, with or not timeScale
    /// </summary>
    private void AddToTime()
    {
        _currentDeltaTime = (Time.realtimeSinceStartup - _editModeLastUpdate) * timeScale;
        _currentUnscaledDeltaTime = (Time.realtimeSinceStartup - _editModeLastUpdate);
        _currentTime += _currentDeltaTime;
        _currentTimeIndependentTimeScale += _currentUnscaledDeltaTime;
    }

    /// <summary>
    /// called every editurUpdate, tell unity to execute the Update() method even if no event are triggered
    /// in the scene.
    /// </summary>
#if UNITY_EDITOR
    private void EditorUpdate()
    {
        if (!Application.isPlaying)
        {
            EditorApplication.QueuePlayerLoopUpdate();
        }
    }
#endif

    /// <summary>
    /// called every frame in play and in editor, thanks to EditorApplication.QueuePlayerLoopUpdate();
    /// add to the current time, then save the current time for later.
    /// </summary>
    private void Update()
    {
        AddToTime();
        _editModeLastUpdate = Time.realtimeSinceStartup;
    }
    #endregion
}
SingletonMono.cs
Download
Copy
´╗┐using UnityEngine;

/// <summary>
/// Be aware this will not prevent a non singleton constructor
///   such as `T myT = new T();`
/// To prevent that, add `protected T () {}` to your singleton class.
/// 
/// As a note, this is made as MonoBehaviour because we need Coroutines.
/// 
/// </summary>
public class SingletonMono<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T _instance;

    private static object _lock = new object();

    public static T Instance
    {
        get
        {
            if (applicationIsQuitting)
            {
                if (Application.isPlaying)
                {
                    Debug.Log("is quitting ?");
                    return null;
                }
                else
                {
                    applicationIsQuitting = false;
                }
            }

            lock (_lock)
            {
                if (_instance == null)
                {
                    _instance = (T)FindObjectOfType(typeof(T));

                    if (FindObjectsOfType(typeof(T)).Length > 1)
                    {
                        Debug.LogWarning("[Singleton] Something went really wrong " +
                            " - there should never be more than 1 singleton!" +
                            " Reopening the scene might fix it.");

                        return _instance;
                    }

                    if (_instance == null)
                    {

                    }
                    else
                    {

                    }
                }

                return _instance;
            }
        }
        set
        {
            _instance = value;
        }
    }

    private static bool applicationIsQuitting = false;
    /// <summary>
    /// When Unity quits, it destroys objects in a random order.
    /// In principle, a Singleton is only destroyed when application quits.
    /// If any script calls Instance after it have been destroyed, 
    ///   it will create a buggy ghost object that will stay on the Editor scene
    ///   even after stopping playing the Application. Really bad!
    /// So, this was made to be sure we're not creating that buggy ghost object.
    /// </summary>

    private void OnApplicationQuit()
    {
        applicationIsQuitting = true;
    }
}



How it works:

First,
[ExecuteInEditMode]
allows us to call the update method of an Monobehaviour even in editor mode.

Then, we need to tell unity to update even if no event are fired in the scene. without it, unity don't update every frame as we might instinctively assume. To accomplish this, we need to call
EditorApplication.QueuePlayerLoopUpdate();
in the
EditorApplication.update
loop.

To achieve this, we subscribe our custom function EditorUpdate() to the internal editor loop of unity:
EditorApplication.update += EditorUpdate;


Then we need to have our own deltaTime if we want to have a timeScale in the editor. So every frame we save the deltaTime (it is the time difference in seconds between the last frame and the current frame), and we multiply it by the timeScale:
private void AddToTime()
{
_currentDeltaTime = (Time.realtimeSinceStartup - _editModeLastUpdate) * timeScale;
_currentUnscaledDeltaTime = (Time.realtimeSinceStartup - _editModeLastUpdate);
_currentTime += _currentDeltaTime;
_currentTimeIndependentTimeScale += _currentUnscaledDeltaTime;
}


After that, you have a TimeEditor class with the same behavior of the Time of Unity. You can do custom timers in your editor tools, and even do a slow motion, by changing the timeScale.
To test it, you can see bellow my timer & coolDown class, which use this EditorTime class.









See also:

Chrono and coolDown

For all your timer & Cooldown in unity. No more Invoke(), Coroutine() or testing the time in hard way inside Update().