I’ve got this ObjectPooler code from someone on these boards from months ago. Its very good. I want to use it but there’s just one problem, and at this point I’m very tired unable to think.
The problem is the pooler operates on GameObjects. That’s all fine and dandy but when I need a script from an object in real time it is very slow. In the past I used GetComponent<>() and thats what caused the problem but I’d like to cache the scripts to reduce time overhead.
I haven’t found a way to implement this. I’ve tried a number of techniques. Like creating a C-struct like class to hold MonoBehaviours, and the Script types (as form System.Type) with them and providing accessor functions and properties. Yet I don’t believe this is any more efficient and using Arrays introduces problems with array bounds and resizing (what do I do when I add or subtract a script from my game object?).
I did try using generic Data structures. Like I said I’m a little bit too tired to do any in depth thinking.
So essentially I’d like to create an object pooler which will cache the scripts attached to each object and make them easily and efficiently accessible.
Yes, I’m kind of asking a “Give me the codezzz” type question, but perhaps even just some pointers would help. I know my way around C# enough to figure this on any other day tbh.
I don’t think its necessary to post the code I already have (I kind of deleted most of it trying to implement this caching feature).
I’ll describe it here:
I have a Stack to contain GameObjects. Two functions: GetObject() and ReturnObject() do all the work. If an object is needed you call GetObject(), which returns one from the top of the stack via Pop() or creates a new one if the stack is empty.
Return object places the object back on the stack.
Come to think of it I’ve been using it wrongly this entire time. I forgot to put the objects back!!
Just store a stack of components instead of GameObjects? A component has a reference to the GameObject it’s on, so you can get that if necessary. When creating new ones, you’d need to do GetComponent, but only once per object you create.
Something like:
public class Pool<T> {
private System.Collections.Generic.Stack<T> _storage;
private System.Func<T> _fnCreate;
private System.Action<T> _fnStore;
private System.Action<T> _fnRetrieve;
public Pool(System.Func<T> create, System.Action<T> store, System.Action<T> ret) {
_fnCreate = create;
_fnStore = store;
_fnRetrieve = ret;
_storage = new System.Collections.Generic.Stack<T>();
}
public T GetObject() {
if (_storage.Count > 0) {
T obj = _storage.Pop();
_fnRetrieve(obj);
return obj;
}
return _fnCreate();
}
public void StoreObject(T obj) {
_fnStore(obj);
_storage.Push(obj);
}
}
public class UnityPool<T> : Pool<T> where T : Component {
public UnityPool(GameObject prefab): base(
() => (MonoBehaviour.Instantiate(prefab) as GameObject).GetComponent<T>(),
obj => obj.gameObject.SetActive(false),
obj => obj.gameObject.SetActive(true)
) {}
}
That won’t work because Unity3d does not support generic classes. Does anybody know of a way to cache the script on the stack?? I’ve tried a bunch of things.
I’ve tried creating a container class with Component, or Generic types. Parallel stacks for the scripts and their associated gameobjects.
I think the biggest problem here is because Unity does not support Generic scripts I can’t have a generic objectpooler. It can hold gameobjects and I can call getComponent or SendMessage but they are game breaking and slow!
Man. I thought I could just create a script for my bullet, attach it, and then just get it when it collides with something.
Turns out that is much more difficult than I thought. Shame.
Unity does support generic classes. It doesn’t support putting them on a GameObject as a component, but you can use them within components just fine:
public class PoolExample : MonoBehaviour {
public GameObject prefab;
private UnityPool<Foo> _pool;
private void Awake () {
_pool = new UnityPool<Foo>(prefab);
}
// Do stuff with _pool
}
GetComponent shouldn’t be game-breakingly slow, especially if you’re caching the results ahead of time or on the first use. It’s actually very efficient.
Now you are just whining. Unity deals with generics perfectly well. The only point you will have trouble with is the serialisation, but that can be dealt with using inheritance.
You have a couple of options.
Take any pool and make it a generic.
Take any pool system and replace every GameObject with Component, or the type of Component you are interested in.
You can attach generics to a GameObject directly, like so.
public class MyGeneric<T> : MonoBehaviouir
{
public T randomObject;
}
// Attach this version to a GameObject
public class MyGenericFloat : MyGeneric<Float>
{
}
I’m not whining, lol. I legitimately thought Unity could not deal with Generics because of the message which appears in the console when you try to use generics with AddComponent().
But I was pretty frustrated!
@Dameon that’s exactly what I was trying to do. Cache the scripts that I need so I’m not doing GetComponent() every update (or more).
I hate to ask for more help but could you BoredMormon just comment that code so I have a better understanding of the code?? Inheritance is something I’ve never used or really understood.
As for the sleep issue; it wasn’t just that I hadn’t slept last week; its that it was a holiday and I was at home. That place has a funny effect on me. I usually stay up really late and sleep in when not working so my sleep schedule gets thrown off. Plus the house is tiny and I didn’t get much exercise to waken the mind
Its an interesting paradox in programming. The things that make so much human sense are the most difficult things to do. Like adding “just one more feature” that makes perfect sense. You may have to rewrite everything just for it
EDIT: Oh I just commented the code myself >_< I understand it pretty well now. The only problem is I don’t want to write an objectPooler for every possible class I want to pool. Hmm… will pontificate upon this.
EDIT INFINITUM: Ended up using MonoBehaviour as Eric5h5 suggested for another problem earlier. Why? Because screw it I’m sick of dealing with this problem. Its so stupid! Game framerate visibly stops for a fraction of a second as objects are created filling up the stack to maximum capacity but after its full things go rather smoothly. I don’t know why I bothered with any of it. It would probably be more time efficient just to spawn as many objects as needed and use those instead of this stack nonsense. It may not save memory but it sure as hell wouldn’t kill my framerate.
I think you really need to revisit your basics of programming. An object pool is a pretty standard game programming concept, and you really shouldn’t be having this much trouble with it. Inheritance and polymorphism are very basic programming concepts that anybody working in C# should understand very early on.
You also seem to be putting much more effort into the concept of caching components than it should need. It should be fairly straightforward, and should have nothing to do with your pooling strategy. The idea is just that if you’re going to be using a Component fairly often, you use GetComponent once and assign the result to a field. This is two examples in one, featuring an example of getting a component in Awake, caching the results in a class field and using them in Update. The other example fires up a coroutine in Start and caches the results of a GetComponent call locally, so they’re discarded after the coroutine finishes.
private Collider2D _collider;
private void Awake()
{
_collider = gameObject.GetComponent<Collider2D>();
}
private void Update()
{
Debug.Log(_collider.bounds);
}
private void Start()
{
StartCoroutine(ColliderRoutine());
}
private IEnumerator ColliderRoutine()
{
WaitForSeconds waitObject = new WaitForSeconds(1);
transformObject = GetComponent<Transform>();
while (transformObject.position.x > 0)
{
// This line is just for demo purposes. This will not work in a build because you can't modify structs like this.
transformObject.position.x -= 1;
yield return waitObject;
}
}