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
2) 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)
}
}
Answer by Bunny83 · Mar 01, 2016 at 01:46 PM
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.
Answer by phil_me_up · Mar 01, 2016 at 12:24 PM
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()
$$anonymous$$any thanks, but that's what I do already. I'm hoping someone knows how get the grass script (although I'm not actually thinking grass here) to manage itself on a once per frame basis, without needing a babysitting class. I'd thought Invoke() at the start, but am not sure whether you could detect that this has happened, and thus know to do it only once. Another thought was patching your calcs onto another function, (Perhaps naughtily an internal one) with UnityEngine.That'sthefunctionIwasLookingFor+=mycheekyBahaviour();, but I'm too new to patching functions like this right now ;).
Hmmm, I'm a little confused by what you're trying to do..
Update will occur once per frame. Calling that in a manager class will allow you yo update the grass manager once per frame. Calling an update on each blade from this update will then update each blade of grass, once per frame. All the common data can be calculated before calling update on each blade of grass, and then each blade of grass can access this data through the singleton pattern.
If you are dealing with loads of objects then you really want to keep things as simple as possible. Having delegates, actions, listeners etc are perfectly appropriate in some cases, but overkill in others. Obviously I don't completely know what your trying to achieve, but having the manager class as I described above is probably the best solution in most cases. Each blade can access the manager class without needing to pass around references / copy data etc making it more efficient than passing parameters in functions or storing copies / references in each blade.
This situation isn't really babysitting. In fact it's almost less like babysitting than just having them all as separate game objects. By having a manager you have a greater understanding of the objects in your game, greater control over the update / initialisation routines etc and less reliance upon Reflection, which is slow.
Sorry if that's no help!
Hi phil, wanted to explain ,I upvoted your answer but didn't choose it because whilst it works and I know it works, it's not what I was looking for.
A single call to any class (not instance) per frame would be so handy, and negate the need for manager classes in many cases. Ironically, According to @Bunny83 the answer is not really so I'll likely remain doing things as described. thanks again.