An update that behaves like a singleton in a monobehavior

I’m constantly coming up with scenarios where I need to collect complex data once per frame which many instances then go on to use.

I tend to:

  1. use update to grab this data with a boolean to determine whether it has already been done, which is then reset on the lateupdate - this costs the extra function call, which may not have huge impact on the actual performance, but does make things messy.

or

  1. have a class that I know will have only instance, such as the main character class do it for them, this creates interdependancy which is a pain if you want to grab assets from one game to another, or indeed follow the code six months later.

Is there a way of setting a function within an instantiatable monobehavior which knows that it is done once, and once alone per frame? like this…

public class SomethingReallyProlificLikeBeesGrassOrParticles:MonoBehavior{
  static void UpdateOnce(){
    //do something really hairy that you'd be mad to do any more than once per frame if any instance is there
    }
    
    void Update(){
    //do the simple stuff as many times as I have instantiated this class (think in terms of thousands)
    }

}

I’d probably have a manager class (call it GrassManager for example), which has a list of all the actual blades of grass.

On each update within GrassManager, do what you need to do and then call an update routine on each blade of grass by iterating through your list of active grass blades. Make sure you create your own update routine (public void UpdateGrass()) within the GrassBlade class as you’ll have more control over when it is called, as well as it being faster than using the Unity Update() method, which relies on Reflection.

When actually processing the UpdateGrass function, you can either pass in the parameters you need or just reference the GrassManager. There are a couple of ways to do this. You could either setup the GrassManager as a singleton or for each blade of grass you can store a reference to the GrassManager either as your setting up, or pass it during the update function.

The Singleton method is probably the one I would use, and the basis would be something like the below (note this is just written of the top of my head, expect syntax errors etc!)

class GrassManager : Monobehaviour
{
private static GrassManager instance = null;
private List<GrassBlade> GrassBlades;

public static GrassManager GetInstance()
{
return instance;
}

void Awake()
{
if(instance == null)
{
instance = this;
DontDestroyOnLoad(this.gameObject);
}
}
}

Elsewhere within your game, you can now access the GrassManager with GrassManager.GetInstance()

It’s not possible without an extra class. All callbacks from Unity are directed to an instance. Unity doesn’t call static methods of classes. So if your “Grass” class should manage itself you would have to pick one of them to do the management stuff as well. While this is possible, it’s a horrible approach. A manager should always be seperate from the things it manages.

If Unity would have implemented something like EditorApplication.update for the runtime it would be possible without an extra class. In the editor you could do:

public class Grass : ScriptableObject
{
    static List<Grass> m_List = new List<Grass>();
    static bool m_Initialized = false;
    static void Initialize()
    {
        if (!m_Initialized)
        {
            EditorApplication.update += OnUpdate;
            m_Initialized = true;
        }
    }
    static OnUpdate()
    {
        // do something
    }
    // other instance stuff here
    void OnEnable()
    {
        Initialize();
        m_List.Add(this); // add itself to the list.
    }
}

Unity doesn’t have a static update delegate to which you can subscribe at runtime. However you could use a seperate helper class like my CoroutineHelper to achieve the same thing. Instead of the m_Initialited boolean you could simply store the “Run” instance to know it’s already running:

public class Grass : ScriptableObject
{
    static List<Grass> m_List = new List<Grass>();
    static Run m_UpdateJob = null;
    static void Initialize()
    {
        if (m_UpdateJob == null)
            m_UpdateJob = Run.EachFrame(OnUpdate);
    }
    // [ ... ]
}

This will ensure that OnUpdate is called once every frame as soon as the first “Grass” instance has been created. This does also work at runtime. The CoroutineHelper class is a selfmanaged singleton. It automatically creates an instance if one is needed. All coroutines which are started by the static methods the Run class provides will run on that singleton instance.

Unity is completely event driven. You don’t have control of the programs main loop. That’s part of the engines core. So you can’t have your code execute somehow every frame without having an instance which receives an event from the engine.

Another approach for a selfmanaging class would be this:

public class Grass : MonoBehaviour
{
    int m_LastFrame = -1;
    void Update()
    {
        if (Time.frameCount > m_LastFrame)
        {
            m_LastFrame = Time.frameCount;
            // do something that should only happens once per frame here
        }
        // other update things should be done here.
        // this part is executed for every instance of "Grass".
    }
}

While this is possible, it adds a bit overhead to each Grass instance. If your class actually doesn’t need an Update itself it’s just wasted performance to have an Update method in every instance.

With this approach the first Grass instance which get his Update called will do the “extra work”. Once it’s done it updates the int variable so all other instances will ignore that part until the next frame.