Be gentle as I have started learning Unity 2 weeks ago but…I don’t understand why most Object Pooling code is based off of MonoBehavior and not ScriptableObject.
You can implement what is needed within the ScriptableObject.Awake() method, call Instantiate and Destroy as well as avoid the overhead of the MonoDevelop.Update().
Do you know how else you can get rid of the overhead of Update()? By removing the function from the script. Like mentioned at the blog post below when the MonoBehaviour for a given type is first called the “magic” methods are added to lists to be called. If they don’t exist, they don’t get added, and thus they don’t add overhead.
I usually use ScriptableObject ot make saome sort of “configuration file” that lives in project assets and can be referenced by anyone. I don’t see much of the point in creating it within the scene.
MonoBehavior represents component attached to a game object within the scnee, so a reasonable idea is to implement behaviors that are available in the scene through monobehaviors.
Now, the thing here is, I’m not sure why would your object pool even need an Update() in the first place. Object pools usually have methods for creating pooled objects and returning those to the pool, but they absolutely do not need an “Update” method.
To avoid overhead of MonoDevelop.Update() (PLease profile before optimziing to avoid premature optimization) you simply can use different function instead of update(). A script that does not have Update() method, does not have Update() method overhead. A script that does not have Updaate(), Start(), OnEnable() and OnDisable() should be incredibly cheap to run, with overhead being minimal or non-existent.
With all that in mind, I definitely do not see the point in using ScriptableObject for object pooll.
If you don’t plan on attaching it to a GameObject as a component, utilizing any of the Order Of Events sequencing or taking advantage of the component-based nature of Unity, then no.
Avoiding MonoBehavior doesn’t really give you any advantages, and can actually add some problems if you at some point decide that you really wanna reference object pool from somewhere.
MonoBehavior also gives you ability to investigate contents of the component in Inspector while the game is running. I can’t think of any real reason not to use MonoBehavior for pools, honestly.
I do this frequently if I want to host an implementation across multiple monobehaviours without dealing with 15 components per transform. If there is tight coupling between elements, I think it often makes sense to do this and results are easy to work with.
Honestly though, worrying about monobehaviour overhead is totally inappropriate if you’re learning basic intro stuff. It won’t make any difference in the vast majority of use cases.
If for example, you’re doing any sort of string manipulation - you’re looking at order magnitude more overhead.
Most of the time, there are other fish to fry. If you’re still coming to grips with the component based design and the different tools like MonoBehaviour vs S.O. then you most likely not in the right place to predict performance costs or bottlenecks.
I think it’s more a matter of choosing a proper life cycle of the objects you want to pool, than that of avoiding overhead.
And if most of such implementations are based on MonoBehaviour rather than ScriptableObject, I suppose it’s because pools are often used as a temporary cache for frequently instantiated objects, rather than as a permanent storage for such objects that should persist between reloads.
Object pools are typically designed in a manner that it stores unused objects in a stateless status, so that any required dependencies or states are restored when an object is checked out to a client. So, in such a design there’s no states to preserve for unused objects, which means there’s little reason to use ScriptableObject, because its primary purpose is to store such states.
And I agree with other people that premature optimization is something we should avoid at all cost, and I feel it lucky that Unity provides a builtin profiler, so we don’t have to rely on our ‘gut feeling’ when we decide what design approach to take for performance.