#01 : Why to wrap UnityEngine.Time, and how

Why remake Time.deltaTime?

To have more precise control over object behavior in order to fulfill the requirements of your game logic.

Even if all you want is for a GameObject to stop falling while your logo continues to rise.

(It's not required, but I'd recommend you check out entry #00 if you would like some background on the intent and scope of this blog.)

Before we get to the juicy bits

and start controlling Time, I'm going to begin by introducing a design pattern which you may have heard of: the Singleton. If you have heard of this pattern, I think it is also very likely that you have been warned against using it excessively in your own code. Indeed, the pattern is perhaps rightfully maligned in environments where a static class would fulfill this pattern's logical purpose more accurately.

However, when working in Unity, where there is the need to join globally accessible functionality with 'concrete' instances of very specific objects existing in the UnityEngine scene space (while maintaining the encapsulation of each), I feel that the singleton is an appropriate design choice. Of course you can overdo it, but it is easier to remove Singletons and replace them with static classes and/or instanced references than it is to cleanly shove this pattern into your design later. The trick is to be reasonably confident that the instance of the singleton you are defining directly relates to a high level functional demand of your video game (the UI must keep moving while GameObjects stop).

(You might be thinking that that sounds more like a state machine, and you may be right, but I will get to that design pattern in another article. For now, I would like to encourage Unity developers to consider how they are using design patterns in general, so I have chosen to start with what I feel is one of the most contentious patterns that everyone uses anyway.)

C# Generics Singleton example
using UnityEngine;
namespace ratdev
{
   public class Singleton<T> : MonoBehaviour where T : Singleton<T>
   {
      public static T instance;
      public virtual void Awake()
      {
         if (!instance)
         {
            instance = this as T;
         }
         else
         {
            // multiple Singleton found
            Debug.LogWarning("Disabling : " + this.gameObject.name.ToString());
            this.enabled = false;
         }
      }
   }
}

Singleton.cs

Pretty straightforward, this script ensures that it is the only active copy of itself, and makes itself globally available via the static variable 'instance'.

So we wanted to control Time...

we'll get there! But first, let's have a look at a pseudo code implementation of the functionality which we are trying to achieve with this pattern. This way we'll have an idea of where we're headed before we get to wrapping Time.

using System.Collections.Generic;
namespace ratdev
{
   public class Time_Singleton : Singleton<Time_Singleton>
   {
      public List<TimedMonoBehaviour> timers = new List<TimedMonoBehaviour>();
      public void Pause() //Pause all TimedMonoBehaviours
      {
         foreach (TimedMonoBehaviour timedScript in timers)
         {
            timedScript.Pause();
         }
      }
      public void Unpause() //Unpause all TimedMonoBehaviours
      {
         foreach (TimedMonoBehaviour timedScript in timers)
         {
            timedScript.Unpause();
         }
      }
   }
}


Also straight forward,

but it mostly just seems like whatever TimedMonoBehaviour does is encapsulating all of the actual "work" here. Sure, we can call Pause() and Unpause() on the Singleton now, but there still does not seem to be a Time implementation here.

We're almost there.

This is just an example to get an idea of what we want to have in the end: a globally accessible script which can control groups of objects in an encapsulated manner. Let's look into TimedMonoBehaviour so that we can try to put it all together.

About that Time wrapper

using UnityEngine;
using System.Collections;
namespace ratdev
{
   public class TimedMonoBehaviour : MonoBehaviour
   {
      public bool paused = false;
      protected float deltaTime { get; private set; } // Can be used in sub-classes
      private float _lastUpdate = 0.0f;
      private void Start()
      {
         DoStart();
      }
      private void DoStart()
      {
         StartCoroutine(TimeLoop());
      }
      IEnumerator TimeLoop()
      {
         while (Application.isPlaying) // (looping)
         {
            deltaTime = Time.realtimeSinceStartup - _lastUpdate;
            _lastUpdate = Time.realtimeSinceStartup; // ! UnityEngine.Time wrapper
            if (!paused)
            {
               TimedUpdate();
            }
            yield return null;
         }
      }
      protected virtual void TimedUpdate() { } // Replace Update() in sub-classes
      public void Pause()
      {
         paused = true;
         OnPause();
      }
      protected virtual void OnPause() { } // Triggers when pause is called
      public void Unpause()
      {
         paused = false;
         OnUnpause();
      }
      protected virtual void OnUnpause() { } // Triggers when unpause is called
   }
}

TimedMonoBehaviour.cs

Now we're cookin' !

If I look closely I think that I can spot two consecutive lines of code which are actually doing something.

But seriously,

there is a bit more to unpack here. This is actually the UnityEngine.Time wrapper and it is a central piece to the overall logic which we are attempting to implement.

The TimedMonoBehaviour script has three purposes: the first is to calculate the time between Unity Update cycles and cache that float value until the next Update. This will be used later to calculate a total running time for game logic (over which we will have control; IE pause/unpause, scale, scrub, jump).

The second purpose of this script is to track if we are paused or not by exposing the two public methods Pause and Unpause (which update a local tracking bool and notify any inheriting classes of the state change using the virtual functions OnPause/OnUnpause). These two methods provide the interfacing which will allow us to use this wrapper for our application logic needs.

It's third purpose is to send a TimedUpdate() call to any inheriting classes on each Update() frame that we are not paused. For the purposes of this example we will only be using this method to update the elapsed game time. However, virtual functions are a useful convention to allow for separation of responsibility in our code, so it doesn't hurt.

If you are unclear about any of those 3 things, read the TimedMonoBehaviour class again and try to identify which sections of code handle each of these 3 purposes. After you are confident that you understand the inner implementations of this object, let's take at look at a class which will utilize this functionality, and also connect back to our Singleton in Unity so that our game can take advantage of the functionality that we are designing.

Let's move on to the final piece of the puzzle!

What did you just call me?

using System.Collections.Generic;
namespace ratdev
{
   public class TimedObjectManager : TimedMonoBehaviour
   {
      private float _elapsedTime = 0.0f;
      private List<iPauseable> _pausableScripts = new List<iPauseable>();
      // <summary>
      // Returns the current calculated total elapsed running time of this manager
      // </summary>
      public float getElapsedTime()
      {
         return _elapsedTime;
      }
      protected override void TimedUpdate()
      {
         //Increment the elapsed game time based on the TimedMonoBehaviour delta time
         _elapsedTime += deltaTime;
      }
      // methods for managed objects
      public float Register(iPauseable script)
      {
         _pausableScripts.Add(script);
      }
      public float UnRegister(iPauseable script)
      {
         _pausableScripts.Remove(script);
      }
      protected override void OnPause()
      {
         foreach (iPauseable script in _pausableScripts)
         {
            script.Pause();
         }
      }
      protected override void OnUnpause()
      {
         foreach (iPauseable script in _pausableScripts)
         {
            script.UnPause();
         }
      }
   }
}

TimedObjectManager.cs

First Singletons and now Managers, too? Is this serious? Well, sometimes you should just call a pig a pig. This object will implement the functionality of the TimedMonoBehaviour and also manage any timed objects in the scene, so we call it a TimedObjectManager. If it makes sense to you and it works for your codebase, you should just use it and move on. You can always find and replace later.

Another pretty easy one here,

only two distinct parts. This script will keep a running total of the elapsed unpaused time as sent from it's base class TimedMonoBehaviour, and it will pass Pause/UnPause messages to any scripts which implement iPausable (and Register). Similar to the original pseudo code example with objects registering with the Time_Singleton, we will now instead have groups of "pausable" objects which are managed by managers (which themselves will be accessible through the singleton).

(I will point out that I have supplied a summary for the getElapsedTime() method, just as an example for a commonly used exposed interface. I have left out others here and above for succinctness, but really at the very least the Pause/Unpause/Register methods should also have summaries.)

We're... there?

Sure are! We can now declare (as in a named instance of a type) what we needed originally to fulfill our desired application logic: two different instances of the TimedObjectManager script, each to handle the timing and behavior of two distinctly defined groups of objects. And this extrapolated game functionality will be available to the rest of our code without needing any direct references to any of the actual timed objects themselves.

But, there are still no objects?

namespace ratdev
{
   public interface iPauseable
   {
      void Pause();
      void UnPause();
   }
}

iPausable.cs

using UnityEngine;
namespace ratdev
{
   public class TimeManaged_ParticleEffect : MonoBehaviour, iPauseable
   {
      ... // _myParticleEffect ref would be here
      public void OnEnable()
      {
         Time_Singleton.instance.GameScene_TimeManager.Register((iPauseable)this);
      }
      public void OnDisable()
      {
         Time_Singleton.instance.GameScene_TimeManager.Unregister((iPauseable)this);
      }
      public void Pause()
      {
         _myParticleEffect.Stop();
      }
      public void UnPause()
      {
         _myParticleEffect.Resume();
      }
   }
}

generic implementation example

For sake of completeness, there is the iPausable interface, as well as an example for an actor script which will register with the TimedObjectManager and respond to Pause/Unpause calls. For other use cases (physics/translations), it should be as simple as using ~Manager.getElapsedTime() (or expose and using the delta) instead of using Time.* in your scripts. This information should allow you to design and template out your own object behavior for tweens / alpha fades / animations / and so on, as your game idea demands.

Finally,

The individual Managers can now handle pausing/unpausing any of their own registered objects as needed, and we can access the Managers themselves through the Singleton when we want to initiate a global Game or UI pause, in order to satisfy our video game design requirements. We might as well take out those original Pause and Unpause methods and clean up our Time_Singleton.

We've now got our final Time wrapper implementation, all ready to be set up in the Unity Inspector:

namespace ratdev
{
   public class Time_Singleton : Singleton<Time_Singleton>
   {
      public TimedObjectManager GameScene_TimeManager;
      public TimedObjectManager UI_TimeManager;
   }
}

Time_Singleton.cs

But wait . . .

. . . that's it? Just an empty class with two variables? Yup! That is all that we need to accomplish the design goals of this object: it is wrapping UnityEngine.Time (two instances), adding some extra functionality (Pause/Unpause), and abstracting it away from the rest of the codebase. To utilize this functionality later, there will be no need to know the specific implementation details; you will just need to have an understanding of the high level concepts as they relate directly to their analogs in the application logic. ("Game gets paused, UI can keep running.")

You should now be able to

recreate the GIF at the top of this page, using just the 5 named code files here, and one additional line of code not shown on this page. The falling cube has a script attached with one single line of code in the Mono.FixedUpdate() method. This line of code references the TimeManager's elapsed time and calculates a new position for the cube. If you cannot already mentally imagine this line of code and how it ties into the rest of this information, I encourage you to try it for yourself. Since you've made it this far, I know that you can do it. Recreate the files shown here and reread the notes for each as you do. Try to really understand the purpose of each code block, even sections that at first appear to be 'too simple' or to 'not do anything'.

If you already can

That's great! Perhaps you can challenge yourself to come up with a way to extend on this functionality. Write a simple script using your prefered input method (OnGUI, keybind) to call the ~Manager base Pause()/Unpause() methods, and then add an actor script to have the cube respond to these calls. Make it change itself to a red material when paused, and then back again? Make some rain and lightning particle effects that can ominously slow down and then freeze when your antagonist appears, and then instantly resume when they leave. Try to come up with some other interesting uses!

So,

If this object design pattern is so simple and fulfills such a common need, why doesn't Unity already provide some built-in global Time wrappers to use?

Well,

that's actually the point of creating a game, isn't it? To define the behavior of the system(s) that you will need to accomplish the inputs and outputs that you desire. Unity doesn't know if your game design will require 2 separate instances of Time, or 17; nor is that the purpose of the engine. Unity doesn't know if you need the ability to Pause or Unpause your game, or if you would like to count the number of times that you paused some number of cubes to award an achievement on the 1000th, and so on. Unity provides you with a framework which you can use to define a program structure of your own. (Although, even they can get caught out; having to retroactively provide Time.unscaledTime in their native wrapper because of too many devs abusing timeScale to implement display functions like that slowing rain effect.)

So while it might look like 'not much is there', we have actually reduced the exposed components down to only the ones which we need to fulfill the application logic required for our game. Maintaining this level of abstraction can be difficult when the same problem could have been solved locally with a single float, but using clean design patterns consistently will allow us to scale up our codebase while preserving the ease of 're-learning' sections of code when it is revisited for fixes or additions.

To conclude,

Try to identify the exact application logic needs of your video game, and then work to structure their implementation into a few exacting objects encapsulating all of their own required local functionality. This approach of maintaining local encapsulation of logic and external abstraction of intent will help to improve your code clarity with reusable 'large scope' objects (a Unity core module wrapper), as well as small (a particle prefab that can pause it's effects).

Next time:

Now that we have an understanding of the Singleton design pattern, and a wrapper for UnityEngine.Time, we're only a few steps away from using C# delegates to implement the Command pattern in Unity! I'm excited to write more, I hope that you will give me some feedback (and maybe some encouragement!) on reddit! Thanks for making it this far, I truly hope that you have found something useful.

"