How to create Generics Pooling System for components/scripts?
up vote
8
down vote
favorite
My awareness of Generics is that they can help me streamline my pooling, but can't figure out how.
My pooling system is minimalistic, but messy. And now getting unwieldy and messy, and MESSY. It doesn't scale nicely...
My FXDistribrutor.cs class is a component attached to an object in the initial scene, designed to permanently exist throughout all scenes of the game. It has a static reference to itself, so that I can call into it from anywhere easily. More on that contrivance at the end. I'm not even sure if that's the 'right' way to do this. But it works nicely.
FXDistributor has a public slot for each type of FX Unit it is able to distribute, and an array for the pool of this type of FX, and an index for the array and a size of the pool.
Here are two examples:
public BumperFX BmprFX;
BumperFX _poolOfBumperFX;
int _indexBumperFX, _poolSize = 10;
public LandingFX LndngFX;
LandingFX _poolOfLndngFX;
int _indexLndngFX, _poolSizeLndngFX = 5;
In the Unity Start call, I fill the pools of each FX Unit:
void Start(){
_poolOfBumperFX = new BumperFX[_poolSize];
for (var i = 0; i < _poolSize; i++) {
_poolOfBumperFX[i] = Instantiate(BmprFX, transform );
}
_poolOfLndngFX = new LandingFX[_poolSizeLndngFX];
for ( var i = 0; i < _poolSizeLndngFX; i++ ) {
_poolOfLndngFX[i] = Instantiate( LndngFX, transform );
}
}
And in the body of the class I have a bunch of methods for each FX type, to provide them to wherever they're needed:
public LandingFX GimmeLandingFX ( ){
if ( _indexLndngFX == _poolSizeLndngFX ) _indexLndngFX = 0;
var lndngFX = _poolOfLndngFX[_indexLndngFX];
_indexLndngFX++; return lndngFX;
}
public BumperFX GimmeBumperFX ( ) {
if ( _indexBumperFX == _poolSize ) _indexBumperFX = 0;
var bumperFX = _poolOfBumperFX[_indexBumperFX];
_indexBumperFX++; return bumperFX;
}
So when I want one of these FX, and to use it, I call like this, from anywhere, into the static reference:
FXDistributor.sRef.GimmeLandingFX( ).Bounce(
bounce.point,
bounce.tangentImpulse,
bounce.normalImpulse
);
How do I streamline this approach with Generics so I can easily and less messily do this sort of thing for a couple of dozen types of FX Units?
c# unity3d generics pooling
|
show 6 more comments
up vote
8
down vote
favorite
My awareness of Generics is that they can help me streamline my pooling, but can't figure out how.
My pooling system is minimalistic, but messy. And now getting unwieldy and messy, and MESSY. It doesn't scale nicely...
My FXDistribrutor.cs class is a component attached to an object in the initial scene, designed to permanently exist throughout all scenes of the game. It has a static reference to itself, so that I can call into it from anywhere easily. More on that contrivance at the end. I'm not even sure if that's the 'right' way to do this. But it works nicely.
FXDistributor has a public slot for each type of FX Unit it is able to distribute, and an array for the pool of this type of FX, and an index for the array and a size of the pool.
Here are two examples:
public BumperFX BmprFX;
BumperFX _poolOfBumperFX;
int _indexBumperFX, _poolSize = 10;
public LandingFX LndngFX;
LandingFX _poolOfLndngFX;
int _indexLndngFX, _poolSizeLndngFX = 5;
In the Unity Start call, I fill the pools of each FX Unit:
void Start(){
_poolOfBumperFX = new BumperFX[_poolSize];
for (var i = 0; i < _poolSize; i++) {
_poolOfBumperFX[i] = Instantiate(BmprFX, transform );
}
_poolOfLndngFX = new LandingFX[_poolSizeLndngFX];
for ( var i = 0; i < _poolSizeLndngFX; i++ ) {
_poolOfLndngFX[i] = Instantiate( LndngFX, transform );
}
}
And in the body of the class I have a bunch of methods for each FX type, to provide them to wherever they're needed:
public LandingFX GimmeLandingFX ( ){
if ( _indexLndngFX == _poolSizeLndngFX ) _indexLndngFX = 0;
var lndngFX = _poolOfLndngFX[_indexLndngFX];
_indexLndngFX++; return lndngFX;
}
public BumperFX GimmeBumperFX ( ) {
if ( _indexBumperFX == _poolSize ) _indexBumperFX = 0;
var bumperFX = _poolOfBumperFX[_indexBumperFX];
_indexBumperFX++; return bumperFX;
}
So when I want one of these FX, and to use it, I call like this, from anywhere, into the static reference:
FXDistributor.sRef.GimmeLandingFX( ).Bounce(
bounce.point,
bounce.tangentImpulse,
bounce.normalImpulse
);
How do I streamline this approach with Generics so I can easily and less messily do this sort of thing for a couple of dozen types of FX Units?
c# unity3d generics pooling
Do BumperFX and LandingFX have a common parent class?
– derHugo
Nov 9 at 23:35
Not really. They're both MonoBehaviours, which is a Unity base class sort of thing, but not in the normal sense of common parent class. And I'm not really familiar with hierarchies in C#, so have my entire project as flat. I kind of think this is something I need to learn to overcome, to use generics. I'm coming from Swift.
– Confused
Nov 10 at 4:07
Is the generic pool supposed to be used for Components/Scripts or GameObjects prefabs?
– Programmer
Nov 11 at 21:18
Hey @Programmer, you legend! The pools are of GameObjects, that often have many sub-objects. The parent usually has the activation components and controller component for the FX that the whole little tree of objects is.
– Confused
Nov 11 at 23:33
1
I wasn't notified on your comment. What I meant is that when you have two objects, for example BulletObject, GrenadeObjects, you can create make the pool take GameObject as param and create pool of BulletObject and GrenadeObjects with two different pool instances. Do you understand me? Should I put code to show you?
– Programmer
Nov 14 at 19:41
|
show 6 more comments
up vote
8
down vote
favorite
up vote
8
down vote
favorite
My awareness of Generics is that they can help me streamline my pooling, but can't figure out how.
My pooling system is minimalistic, but messy. And now getting unwieldy and messy, and MESSY. It doesn't scale nicely...
My FXDistribrutor.cs class is a component attached to an object in the initial scene, designed to permanently exist throughout all scenes of the game. It has a static reference to itself, so that I can call into it from anywhere easily. More on that contrivance at the end. I'm not even sure if that's the 'right' way to do this. But it works nicely.
FXDistributor has a public slot for each type of FX Unit it is able to distribute, and an array for the pool of this type of FX, and an index for the array and a size of the pool.
Here are two examples:
public BumperFX BmprFX;
BumperFX _poolOfBumperFX;
int _indexBumperFX, _poolSize = 10;
public LandingFX LndngFX;
LandingFX _poolOfLndngFX;
int _indexLndngFX, _poolSizeLndngFX = 5;
In the Unity Start call, I fill the pools of each FX Unit:
void Start(){
_poolOfBumperFX = new BumperFX[_poolSize];
for (var i = 0; i < _poolSize; i++) {
_poolOfBumperFX[i] = Instantiate(BmprFX, transform );
}
_poolOfLndngFX = new LandingFX[_poolSizeLndngFX];
for ( var i = 0; i < _poolSizeLndngFX; i++ ) {
_poolOfLndngFX[i] = Instantiate( LndngFX, transform );
}
}
And in the body of the class I have a bunch of methods for each FX type, to provide them to wherever they're needed:
public LandingFX GimmeLandingFX ( ){
if ( _indexLndngFX == _poolSizeLndngFX ) _indexLndngFX = 0;
var lndngFX = _poolOfLndngFX[_indexLndngFX];
_indexLndngFX++; return lndngFX;
}
public BumperFX GimmeBumperFX ( ) {
if ( _indexBumperFX == _poolSize ) _indexBumperFX = 0;
var bumperFX = _poolOfBumperFX[_indexBumperFX];
_indexBumperFX++; return bumperFX;
}
So when I want one of these FX, and to use it, I call like this, from anywhere, into the static reference:
FXDistributor.sRef.GimmeLandingFX( ).Bounce(
bounce.point,
bounce.tangentImpulse,
bounce.normalImpulse
);
How do I streamline this approach with Generics so I can easily and less messily do this sort of thing for a couple of dozen types of FX Units?
c# unity3d generics pooling
My awareness of Generics is that they can help me streamline my pooling, but can't figure out how.
My pooling system is minimalistic, but messy. And now getting unwieldy and messy, and MESSY. It doesn't scale nicely...
My FXDistribrutor.cs class is a component attached to an object in the initial scene, designed to permanently exist throughout all scenes of the game. It has a static reference to itself, so that I can call into it from anywhere easily. More on that contrivance at the end. I'm not even sure if that's the 'right' way to do this. But it works nicely.
FXDistributor has a public slot for each type of FX Unit it is able to distribute, and an array for the pool of this type of FX, and an index for the array and a size of the pool.
Here are two examples:
public BumperFX BmprFX;
BumperFX _poolOfBumperFX;
int _indexBumperFX, _poolSize = 10;
public LandingFX LndngFX;
LandingFX _poolOfLndngFX;
int _indexLndngFX, _poolSizeLndngFX = 5;
In the Unity Start call, I fill the pools of each FX Unit:
void Start(){
_poolOfBumperFX = new BumperFX[_poolSize];
for (var i = 0; i < _poolSize; i++) {
_poolOfBumperFX[i] = Instantiate(BmprFX, transform );
}
_poolOfLndngFX = new LandingFX[_poolSizeLndngFX];
for ( var i = 0; i < _poolSizeLndngFX; i++ ) {
_poolOfLndngFX[i] = Instantiate( LndngFX, transform );
}
}
And in the body of the class I have a bunch of methods for each FX type, to provide them to wherever they're needed:
public LandingFX GimmeLandingFX ( ){
if ( _indexLndngFX == _poolSizeLndngFX ) _indexLndngFX = 0;
var lndngFX = _poolOfLndngFX[_indexLndngFX];
_indexLndngFX++; return lndngFX;
}
public BumperFX GimmeBumperFX ( ) {
if ( _indexBumperFX == _poolSize ) _indexBumperFX = 0;
var bumperFX = _poolOfBumperFX[_indexBumperFX];
_indexBumperFX++; return bumperFX;
}
So when I want one of these FX, and to use it, I call like this, from anywhere, into the static reference:
FXDistributor.sRef.GimmeLandingFX( ).Bounce(
bounce.point,
bounce.tangentImpulse,
bounce.normalImpulse
);
How do I streamline this approach with Generics so I can easily and less messily do this sort of thing for a couple of dozen types of FX Units?
c# unity3d generics pooling
c# unity3d generics pooling
edited Nov 17 at 1:59
Programmer
73.9k1077138
73.9k1077138
asked Nov 8 at 22:17
Confused
3,13541751
3,13541751
Do BumperFX and LandingFX have a common parent class?
– derHugo
Nov 9 at 23:35
Not really. They're both MonoBehaviours, which is a Unity base class sort of thing, but not in the normal sense of common parent class. And I'm not really familiar with hierarchies in C#, so have my entire project as flat. I kind of think this is something I need to learn to overcome, to use generics. I'm coming from Swift.
– Confused
Nov 10 at 4:07
Is the generic pool supposed to be used for Components/Scripts or GameObjects prefabs?
– Programmer
Nov 11 at 21:18
Hey @Programmer, you legend! The pools are of GameObjects, that often have many sub-objects. The parent usually has the activation components and controller component for the FX that the whole little tree of objects is.
– Confused
Nov 11 at 23:33
1
I wasn't notified on your comment. What I meant is that when you have two objects, for example BulletObject, GrenadeObjects, you can create make the pool take GameObject as param and create pool of BulletObject and GrenadeObjects with two different pool instances. Do you understand me? Should I put code to show you?
– Programmer
Nov 14 at 19:41
|
show 6 more comments
Do BumperFX and LandingFX have a common parent class?
– derHugo
Nov 9 at 23:35
Not really. They're both MonoBehaviours, which is a Unity base class sort of thing, but not in the normal sense of common parent class. And I'm not really familiar with hierarchies in C#, so have my entire project as flat. I kind of think this is something I need to learn to overcome, to use generics. I'm coming from Swift.
– Confused
Nov 10 at 4:07
Is the generic pool supposed to be used for Components/Scripts or GameObjects prefabs?
– Programmer
Nov 11 at 21:18
Hey @Programmer, you legend! The pools are of GameObjects, that often have many sub-objects. The parent usually has the activation components and controller component for the FX that the whole little tree of objects is.
– Confused
Nov 11 at 23:33
1
I wasn't notified on your comment. What I meant is that when you have two objects, for example BulletObject, GrenadeObjects, you can create make the pool take GameObject as param and create pool of BulletObject and GrenadeObjects with two different pool instances. Do you understand me? Should I put code to show you?
– Programmer
Nov 14 at 19:41
Do BumperFX and LandingFX have a common parent class?
– derHugo
Nov 9 at 23:35
Do BumperFX and LandingFX have a common parent class?
– derHugo
Nov 9 at 23:35
Not really. They're both MonoBehaviours, which is a Unity base class sort of thing, but not in the normal sense of common parent class. And I'm not really familiar with hierarchies in C#, so have my entire project as flat. I kind of think this is something I need to learn to overcome, to use generics. I'm coming from Swift.
– Confused
Nov 10 at 4:07
Not really. They're both MonoBehaviours, which is a Unity base class sort of thing, but not in the normal sense of common parent class. And I'm not really familiar with hierarchies in C#, so have my entire project as flat. I kind of think this is something I need to learn to overcome, to use generics. I'm coming from Swift.
– Confused
Nov 10 at 4:07
Is the generic pool supposed to be used for Components/Scripts or GameObjects prefabs?
– Programmer
Nov 11 at 21:18
Is the generic pool supposed to be used for Components/Scripts or GameObjects prefabs?
– Programmer
Nov 11 at 21:18
Hey @Programmer, you legend! The pools are of GameObjects, that often have many sub-objects. The parent usually has the activation components and controller component for the FX that the whole little tree of objects is.
– Confused
Nov 11 at 23:33
Hey @Programmer, you legend! The pools are of GameObjects, that often have many sub-objects. The parent usually has the activation components and controller component for the FX that the whole little tree of objects is.
– Confused
Nov 11 at 23:33
1
1
I wasn't notified on your comment. What I meant is that when you have two objects, for example BulletObject, GrenadeObjects, you can create make the pool take GameObject as param and create pool of BulletObject and GrenadeObjects with two different pool instances. Do you understand me? Should I put code to show you?
– Programmer
Nov 14 at 19:41
I wasn't notified on your comment. What I meant is that when you have two objects, for example BulletObject, GrenadeObjects, you can create make the pool take GameObject as param and create pool of BulletObject and GrenadeObjects with two different pool instances. Do you understand me? Should I put code to show you?
– Programmer
Nov 14 at 19:41
|
show 6 more comments
2 Answers
2
active
oldest
votes
up vote
4
down vote
accepted
In Unity, the Instantiate() and Destroy() functions are used to create copy of objects especially prefabs and destroy them. When it comes to pooling, the pool object is usually represented in the pool as a Type of GameObject. When you need to access a component from the pool you first retrieve the pool GameObject then use the GetComponent function to retrieve the component from a GameObject.
Reading your question and comments carefully, you want to avoid the GetComponent section and represent just the components not the GameObject so that you can also access the components directly.
If this is what you want then this is where Unity's Component is required. See below for steps required to do this.
Note that when I say component/script, I am referring to your scripts that derive from MonoBehaviour that can be attached to GameObjects or built-in Unity components such as Rigidbody and BoxCollider.
1. Store the components/scripts to a List of Component.
List<Component> components;
2. Store the List of Components in a Dictionary with Type as the key and List<Component> as the value. This makes it easier and faster to group and find components by Type.
Dictionary<Type, List<Component>> poolTypeDict;
3. The rest is really easy. Make the function that adds or retrieves the pool items from and to the Dictionary to be generic then use Convert.ChangeType to cast between the generic type to Component type or from generic to what ever type that is requested to be returned.
4. When you need to add item to the Dictionary, check if the Type exist yet, if it does, retrieve the existing key, create and add new Component to it with the Instantiate function then save it to the Dictionary.
If the Type doesn't exist yet, no need to retrieve any data from the Dictionary. Simply create new one and add it to the Dictionary with its Type.
Once you add item to the pool de-activate the GameObject with component.gameObject.SetActive(false)
5. When you need to retrieve an item from the pool, check if the Type exist as key then retrieve the value which is List of Component. Loop over the components and return any component that has a de-activated GameObject. You can check that by checking if component.gameObject.activeInHierarchy is false.
Once you retrieve item from the pool activate the GameObject with component.gameObject.SetActive(true)
If no component is found, you can decide to either return null or instantiate new component.
6. To recycle the item back to the pool when you're done using it, you don't call the Destroy function. Simply de-activate the GameObject with component.gameObject.SetActive(false)*. This will make the component able to be found next time you search for available components in the Dictionary and List.
Below is an example of minimum generic pool system for scripts and components:
public class ComponentPool
{
//Determines if pool should expand when no pool is available or just return null
public bool autoExpand = true;
//Links the type of the componet with the component
Dictionary<Type, List<Component>> poolTypeDict = new Dictionary<Type, List<Component>>();
public ComponentPool() { }
//Adds Prefab component to the ComponentPool
public void AddPrefab<T>(T prefabReference, int count = 1)
{
_AddComponentType<T>(prefabReference, count);
}
private Component _AddComponentType<T>(T prefabReference, int count = 1)
{
Type compType = typeof(T);
if (count <= 0)
{
Debug.LogError("Count cannot be <= 0");
return null;
}
//Check if the component type already exist in the Dictionary
List<Component> comp;
if (poolTypeDict.TryGetValue(compType, out comp))
{
if (comp == null)
comp = new List<Component>();
//Create the type of component x times
for (int i = 0; i < count; i++)
{
//Instantiate new component and UPDATE the List of components
Component original = (Component)Convert.ChangeType(prefabReference, typeof(T));
Component instance = Instantiate(original);
//De-activate each one until when needed
instance.gameObject.SetActive(false);
comp.Add(instance);
}
}
else
{
//Create the type of component x times
comp = new List<Component>();
for (int i = 0; i < count; i++)
{
//Instantiate new component and UPDATE the List of components
Component original = (Component)Convert.ChangeType(prefabReference, typeof(T));
Component instance = Instantiate(original);
//De-activate each one until when needed
instance.gameObject.SetActive(false);
comp.Add(instance);
}
}
//UPDATE the Dictionary with the new List of components
poolTypeDict[compType] = comp;
/*Return last data added to the List
Needed in the GetAvailableObject function when there is no Component
avaiable to return. New one is then created and returned
*/
return comp[comp.Count - 1];
}
//Get available component in the ComponentPool
public T GetAvailableObject<T>(T prefabReference)
{
Type compType = typeof(T);
//Get all component with the requested type from the Dictionary
List<Component> comp;
if (poolTypeDict.TryGetValue(compType, out comp))
{
//Get de-activated GameObject in the loop
for (int i = 0; i < comp.Count; i++)
{
if (!comp[i].gameObject.activeInHierarchy)
{
//Activate the GameObject then return it
comp[i].gameObject.SetActive(true);
return (T)Convert.ChangeType(comp[i], typeof(T));
}
}
}
//No available object in the pool. Expand array if enabled or return null
if (autoExpand)
{
//Create new component, activate the GameObject and return it
Component instance = _AddComponentType<T>(prefabReference, 1);
instance.gameObject.SetActive(true);
return (T)Convert.ChangeType(instance, typeof(T));
}
return default(T);
}
}
public static class ExtensionMethod
{
public static void RecyclePool(this Component component)
{
//Reset position and then de-activate the GameObject of the component
GameObject obj = component.gameObject;
obj.transform.position = Vector3.zero;
obj.transform.rotation = Quaternion.identity;
component.gameObject.SetActive(false);
}
}
USAGE:
It can take a any prefab component script. Prefabs are used for this since pooled objects are usually prefabs instantiated and waiting to be used.
Example prefab scripts (LandingFX, BumperFX) :
public class LandingFX : MonoBehaviour { ... }
and
public class BumperFX : MonoBehaviour { ... }
Two variables to hold the Prefabs references. You can either use public variables and assign them from the Editor or load them with the Resources API.
public LandingFX landingFxPrefab;
public BumperFX bumperFxPrefab;
Create new Component Pool and disable auto-resize
ComponentPool cmpPool = new ComponentPool();
cmpPool.autoExpand = false;
Create 2 pools for LandingFX and BumperFX components. It can take any component
//AddPrefab 2 objects type of LandingFX
cmpPool.AddPrefab(landingFxPrefab, 2);
//AddPrefab 2 objects type of BumperFX
cmpPool.AddPrefab(bumperFxPrefab, 2);
When you need a LandingFX from the pool, you can retrieve them as below:
LandingFX lndngFX1 = cmpPool.GetAvailableObject(landingFxPrefab);
LandingFX lndngFX2 = cmpPool.GetAvailableObject(landingFxPrefab);
When you need a BumperFX from the pool, you can retrieve them as below:
BumperFX bmpFX1 = cmpPool.GetAvailableObject(bumperFxPrefab);
BumperFX bmpFX2 = cmpPool.GetAvailableObject(bumperFxPrefab);
When you're done using the retrieved component, recycle them back to the pool instead of destroying them:
lndngFX1.RecyclePool();
lndngFX2.RecyclePool();
bmpFX1.RecyclePool();
bmpFX2.RecyclePool();
Perhaps unrelated question. Having gotten the component, and using that as the thing to pool and access, is it a big climb up the hierarchy to access the parent transform to position each pooled object when it's needed?
– Confused
Nov 18 at 23:42
Sorry, I don't understand the comment. Can you rephrase it?
– Programmer
Nov 18 at 23:49
Sure. @Programmer. Every one of these pooled objects I need to position to where they're going to be effective effects. So I need to grab the parent transform, somewhere, and tell it where to go. Is there much overhead in doing that? Or is it just normal "transform.position = goHereDoYourStuff;"?
– Confused
Nov 19 at 0:31
I really don't know about it's performance hit on doing that but it would make sense to change transform.position than the parent because the changing the parent pos will unnecessary change/update all of the child object transform too.
– Programmer
Nov 19 at 0:35
1
I was overthinking the transform problem. Was super simple. The component has access to its host's transform... DOH! THANK YOU!!!!
– Confused
2 days ago
add a comment |
up vote
3
down vote
I'm not that pleased with the solution, but combining a nice object pool with a use of simple Dictionary<K, V> yields the following:
// pool of single object type, uses new for instantiation
public class ObjectPool<T> where T : new()
{
// this will hold all the instances, notice that it's up to caller to make sure
// the pool size is big enough not to reuse an object that's still in use
private readonly T _pool = new T[_maxObjects];
private int _current = 0;
public ObjectPool()
{
// performs initialization, one may consider doing lazy initialization afterwards
for (int i = 0; i < _maxObjects; ++i)
_pool[i] = new T();
}
private const int _maxObjects = 100; // Set this to whatever
public T Get()
{
return _pool[_current++ % _maxObjects];
}
}
// pool of generic pools
public class PoolPool
{
// this holds a reference to pools of known (previously used) object pools
// I'm dissatisfied with an use of object here, but that's a way around the generics :/
private readonly Dictionary<Type, object> _pool = new Dictionary<Type, object>();
public T Get<T>() where T : new()
{
// is the pool already instantiated?
if (_pool.TryGetValue(typeof(T), out var o))
{
// if yes, reuse it (we know o should be of type ObjectPool<T>,
// where T matches the current generic argument
return ((ObjectPool<T>)o).Get();
}
// First time we see T, create new pool and store it in lookup dictionary
// for later use
ObjectPool<T> pool = new ObjectPool<T>();
_pool.Add(typeof(T), pool);
return pool.Get();
}
}
Now, you can simply do the following:
pool.Get<A>().SayHello();
pool.Get<B>().Bark();
This however still leaves a room for improvements as it instantiates classes with new rather than your factory method, as well as does not provide a way to customize pool size in a generic way.
Sorry for my ignorance (it knows no bounds), what's the problem with the instantiation of classes with new?, and where and how am I using a factory method? I don't even really know how I came up with my way. I understand arrays, and chose to use them. If a pooled object is in use, I simply deactivate it, thereby resetting it, and use it. Kind of like how polyphony on a good keyboard steals the note played earliest when it runs out of polyphony.
– Confused
Nov 9 at 0:22
There's nothing with usingnew, the thing is thatnew()requires that typeTprovides public parameterless constructor. I assumed you're using factory method because of call to_poolOfLndngFX[i] = Instantiate( LndngFX, transform );, where I assumeInstantiateis some wrapper aroundActivator.CreateInstance.
– orhtej2
Nov 9 at 10:44
2
argh. I think I see. No. Instantiate is a Unity system thingy. It makes copies of things, so... yes. Probably is a factory function.
– Confused
Nov 9 at 13:25
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
4
down vote
accepted
In Unity, the Instantiate() and Destroy() functions are used to create copy of objects especially prefabs and destroy them. When it comes to pooling, the pool object is usually represented in the pool as a Type of GameObject. When you need to access a component from the pool you first retrieve the pool GameObject then use the GetComponent function to retrieve the component from a GameObject.
Reading your question and comments carefully, you want to avoid the GetComponent section and represent just the components not the GameObject so that you can also access the components directly.
If this is what you want then this is where Unity's Component is required. See below for steps required to do this.
Note that when I say component/script, I am referring to your scripts that derive from MonoBehaviour that can be attached to GameObjects or built-in Unity components such as Rigidbody and BoxCollider.
1. Store the components/scripts to a List of Component.
List<Component> components;
2. Store the List of Components in a Dictionary with Type as the key and List<Component> as the value. This makes it easier and faster to group and find components by Type.
Dictionary<Type, List<Component>> poolTypeDict;
3. The rest is really easy. Make the function that adds or retrieves the pool items from and to the Dictionary to be generic then use Convert.ChangeType to cast between the generic type to Component type or from generic to what ever type that is requested to be returned.
4. When you need to add item to the Dictionary, check if the Type exist yet, if it does, retrieve the existing key, create and add new Component to it with the Instantiate function then save it to the Dictionary.
If the Type doesn't exist yet, no need to retrieve any data from the Dictionary. Simply create new one and add it to the Dictionary with its Type.
Once you add item to the pool de-activate the GameObject with component.gameObject.SetActive(false)
5. When you need to retrieve an item from the pool, check if the Type exist as key then retrieve the value which is List of Component. Loop over the components and return any component that has a de-activated GameObject. You can check that by checking if component.gameObject.activeInHierarchy is false.
Once you retrieve item from the pool activate the GameObject with component.gameObject.SetActive(true)
If no component is found, you can decide to either return null or instantiate new component.
6. To recycle the item back to the pool when you're done using it, you don't call the Destroy function. Simply de-activate the GameObject with component.gameObject.SetActive(false)*. This will make the component able to be found next time you search for available components in the Dictionary and List.
Below is an example of minimum generic pool system for scripts and components:
public class ComponentPool
{
//Determines if pool should expand when no pool is available or just return null
public bool autoExpand = true;
//Links the type of the componet with the component
Dictionary<Type, List<Component>> poolTypeDict = new Dictionary<Type, List<Component>>();
public ComponentPool() { }
//Adds Prefab component to the ComponentPool
public void AddPrefab<T>(T prefabReference, int count = 1)
{
_AddComponentType<T>(prefabReference, count);
}
private Component _AddComponentType<T>(T prefabReference, int count = 1)
{
Type compType = typeof(T);
if (count <= 0)
{
Debug.LogError("Count cannot be <= 0");
return null;
}
//Check if the component type already exist in the Dictionary
List<Component> comp;
if (poolTypeDict.TryGetValue(compType, out comp))
{
if (comp == null)
comp = new List<Component>();
//Create the type of component x times
for (int i = 0; i < count; i++)
{
//Instantiate new component and UPDATE the List of components
Component original = (Component)Convert.ChangeType(prefabReference, typeof(T));
Component instance = Instantiate(original);
//De-activate each one until when needed
instance.gameObject.SetActive(false);
comp.Add(instance);
}
}
else
{
//Create the type of component x times
comp = new List<Component>();
for (int i = 0; i < count; i++)
{
//Instantiate new component and UPDATE the List of components
Component original = (Component)Convert.ChangeType(prefabReference, typeof(T));
Component instance = Instantiate(original);
//De-activate each one until when needed
instance.gameObject.SetActive(false);
comp.Add(instance);
}
}
//UPDATE the Dictionary with the new List of components
poolTypeDict[compType] = comp;
/*Return last data added to the List
Needed in the GetAvailableObject function when there is no Component
avaiable to return. New one is then created and returned
*/
return comp[comp.Count - 1];
}
//Get available component in the ComponentPool
public T GetAvailableObject<T>(T prefabReference)
{
Type compType = typeof(T);
//Get all component with the requested type from the Dictionary
List<Component> comp;
if (poolTypeDict.TryGetValue(compType, out comp))
{
//Get de-activated GameObject in the loop
for (int i = 0; i < comp.Count; i++)
{
if (!comp[i].gameObject.activeInHierarchy)
{
//Activate the GameObject then return it
comp[i].gameObject.SetActive(true);
return (T)Convert.ChangeType(comp[i], typeof(T));
}
}
}
//No available object in the pool. Expand array if enabled or return null
if (autoExpand)
{
//Create new component, activate the GameObject and return it
Component instance = _AddComponentType<T>(prefabReference, 1);
instance.gameObject.SetActive(true);
return (T)Convert.ChangeType(instance, typeof(T));
}
return default(T);
}
}
public static class ExtensionMethod
{
public static void RecyclePool(this Component component)
{
//Reset position and then de-activate the GameObject of the component
GameObject obj = component.gameObject;
obj.transform.position = Vector3.zero;
obj.transform.rotation = Quaternion.identity;
component.gameObject.SetActive(false);
}
}
USAGE:
It can take a any prefab component script. Prefabs are used for this since pooled objects are usually prefabs instantiated and waiting to be used.
Example prefab scripts (LandingFX, BumperFX) :
public class LandingFX : MonoBehaviour { ... }
and
public class BumperFX : MonoBehaviour { ... }
Two variables to hold the Prefabs references. You can either use public variables and assign them from the Editor or load them with the Resources API.
public LandingFX landingFxPrefab;
public BumperFX bumperFxPrefab;
Create new Component Pool and disable auto-resize
ComponentPool cmpPool = new ComponentPool();
cmpPool.autoExpand = false;
Create 2 pools for LandingFX and BumperFX components. It can take any component
//AddPrefab 2 objects type of LandingFX
cmpPool.AddPrefab(landingFxPrefab, 2);
//AddPrefab 2 objects type of BumperFX
cmpPool.AddPrefab(bumperFxPrefab, 2);
When you need a LandingFX from the pool, you can retrieve them as below:
LandingFX lndngFX1 = cmpPool.GetAvailableObject(landingFxPrefab);
LandingFX lndngFX2 = cmpPool.GetAvailableObject(landingFxPrefab);
When you need a BumperFX from the pool, you can retrieve them as below:
BumperFX bmpFX1 = cmpPool.GetAvailableObject(bumperFxPrefab);
BumperFX bmpFX2 = cmpPool.GetAvailableObject(bumperFxPrefab);
When you're done using the retrieved component, recycle them back to the pool instead of destroying them:
lndngFX1.RecyclePool();
lndngFX2.RecyclePool();
bmpFX1.RecyclePool();
bmpFX2.RecyclePool();
Perhaps unrelated question. Having gotten the component, and using that as the thing to pool and access, is it a big climb up the hierarchy to access the parent transform to position each pooled object when it's needed?
– Confused
Nov 18 at 23:42
Sorry, I don't understand the comment. Can you rephrase it?
– Programmer
Nov 18 at 23:49
Sure. @Programmer. Every one of these pooled objects I need to position to where they're going to be effective effects. So I need to grab the parent transform, somewhere, and tell it where to go. Is there much overhead in doing that? Or is it just normal "transform.position = goHereDoYourStuff;"?
– Confused
Nov 19 at 0:31
I really don't know about it's performance hit on doing that but it would make sense to change transform.position than the parent because the changing the parent pos will unnecessary change/update all of the child object transform too.
– Programmer
Nov 19 at 0:35
1
I was overthinking the transform problem. Was super simple. The component has access to its host's transform... DOH! THANK YOU!!!!
– Confused
2 days ago
add a comment |
up vote
4
down vote
accepted
In Unity, the Instantiate() and Destroy() functions are used to create copy of objects especially prefabs and destroy them. When it comes to pooling, the pool object is usually represented in the pool as a Type of GameObject. When you need to access a component from the pool you first retrieve the pool GameObject then use the GetComponent function to retrieve the component from a GameObject.
Reading your question and comments carefully, you want to avoid the GetComponent section and represent just the components not the GameObject so that you can also access the components directly.
If this is what you want then this is where Unity's Component is required. See below for steps required to do this.
Note that when I say component/script, I am referring to your scripts that derive from MonoBehaviour that can be attached to GameObjects or built-in Unity components such as Rigidbody and BoxCollider.
1. Store the components/scripts to a List of Component.
List<Component> components;
2. Store the List of Components in a Dictionary with Type as the key and List<Component> as the value. This makes it easier and faster to group and find components by Type.
Dictionary<Type, List<Component>> poolTypeDict;
3. The rest is really easy. Make the function that adds or retrieves the pool items from and to the Dictionary to be generic then use Convert.ChangeType to cast between the generic type to Component type or from generic to what ever type that is requested to be returned.
4. When you need to add item to the Dictionary, check if the Type exist yet, if it does, retrieve the existing key, create and add new Component to it with the Instantiate function then save it to the Dictionary.
If the Type doesn't exist yet, no need to retrieve any data from the Dictionary. Simply create new one and add it to the Dictionary with its Type.
Once you add item to the pool de-activate the GameObject with component.gameObject.SetActive(false)
5. When you need to retrieve an item from the pool, check if the Type exist as key then retrieve the value which is List of Component. Loop over the components and return any component that has a de-activated GameObject. You can check that by checking if component.gameObject.activeInHierarchy is false.
Once you retrieve item from the pool activate the GameObject with component.gameObject.SetActive(true)
If no component is found, you can decide to either return null or instantiate new component.
6. To recycle the item back to the pool when you're done using it, you don't call the Destroy function. Simply de-activate the GameObject with component.gameObject.SetActive(false)*. This will make the component able to be found next time you search for available components in the Dictionary and List.
Below is an example of minimum generic pool system for scripts and components:
public class ComponentPool
{
//Determines if pool should expand when no pool is available or just return null
public bool autoExpand = true;
//Links the type of the componet with the component
Dictionary<Type, List<Component>> poolTypeDict = new Dictionary<Type, List<Component>>();
public ComponentPool() { }
//Adds Prefab component to the ComponentPool
public void AddPrefab<T>(T prefabReference, int count = 1)
{
_AddComponentType<T>(prefabReference, count);
}
private Component _AddComponentType<T>(T prefabReference, int count = 1)
{
Type compType = typeof(T);
if (count <= 0)
{
Debug.LogError("Count cannot be <= 0");
return null;
}
//Check if the component type already exist in the Dictionary
List<Component> comp;
if (poolTypeDict.TryGetValue(compType, out comp))
{
if (comp == null)
comp = new List<Component>();
//Create the type of component x times
for (int i = 0; i < count; i++)
{
//Instantiate new component and UPDATE the List of components
Component original = (Component)Convert.ChangeType(prefabReference, typeof(T));
Component instance = Instantiate(original);
//De-activate each one until when needed
instance.gameObject.SetActive(false);
comp.Add(instance);
}
}
else
{
//Create the type of component x times
comp = new List<Component>();
for (int i = 0; i < count; i++)
{
//Instantiate new component and UPDATE the List of components
Component original = (Component)Convert.ChangeType(prefabReference, typeof(T));
Component instance = Instantiate(original);
//De-activate each one until when needed
instance.gameObject.SetActive(false);
comp.Add(instance);
}
}
//UPDATE the Dictionary with the new List of components
poolTypeDict[compType] = comp;
/*Return last data added to the List
Needed in the GetAvailableObject function when there is no Component
avaiable to return. New one is then created and returned
*/
return comp[comp.Count - 1];
}
//Get available component in the ComponentPool
public T GetAvailableObject<T>(T prefabReference)
{
Type compType = typeof(T);
//Get all component with the requested type from the Dictionary
List<Component> comp;
if (poolTypeDict.TryGetValue(compType, out comp))
{
//Get de-activated GameObject in the loop
for (int i = 0; i < comp.Count; i++)
{
if (!comp[i].gameObject.activeInHierarchy)
{
//Activate the GameObject then return it
comp[i].gameObject.SetActive(true);
return (T)Convert.ChangeType(comp[i], typeof(T));
}
}
}
//No available object in the pool. Expand array if enabled or return null
if (autoExpand)
{
//Create new component, activate the GameObject and return it
Component instance = _AddComponentType<T>(prefabReference, 1);
instance.gameObject.SetActive(true);
return (T)Convert.ChangeType(instance, typeof(T));
}
return default(T);
}
}
public static class ExtensionMethod
{
public static void RecyclePool(this Component component)
{
//Reset position and then de-activate the GameObject of the component
GameObject obj = component.gameObject;
obj.transform.position = Vector3.zero;
obj.transform.rotation = Quaternion.identity;
component.gameObject.SetActive(false);
}
}
USAGE:
It can take a any prefab component script. Prefabs are used for this since pooled objects are usually prefabs instantiated and waiting to be used.
Example prefab scripts (LandingFX, BumperFX) :
public class LandingFX : MonoBehaviour { ... }
and
public class BumperFX : MonoBehaviour { ... }
Two variables to hold the Prefabs references. You can either use public variables and assign them from the Editor or load them with the Resources API.
public LandingFX landingFxPrefab;
public BumperFX bumperFxPrefab;
Create new Component Pool and disable auto-resize
ComponentPool cmpPool = new ComponentPool();
cmpPool.autoExpand = false;
Create 2 pools for LandingFX and BumperFX components. It can take any component
//AddPrefab 2 objects type of LandingFX
cmpPool.AddPrefab(landingFxPrefab, 2);
//AddPrefab 2 objects type of BumperFX
cmpPool.AddPrefab(bumperFxPrefab, 2);
When you need a LandingFX from the pool, you can retrieve them as below:
LandingFX lndngFX1 = cmpPool.GetAvailableObject(landingFxPrefab);
LandingFX lndngFX2 = cmpPool.GetAvailableObject(landingFxPrefab);
When you need a BumperFX from the pool, you can retrieve them as below:
BumperFX bmpFX1 = cmpPool.GetAvailableObject(bumperFxPrefab);
BumperFX bmpFX2 = cmpPool.GetAvailableObject(bumperFxPrefab);
When you're done using the retrieved component, recycle them back to the pool instead of destroying them:
lndngFX1.RecyclePool();
lndngFX2.RecyclePool();
bmpFX1.RecyclePool();
bmpFX2.RecyclePool();
Perhaps unrelated question. Having gotten the component, and using that as the thing to pool and access, is it a big climb up the hierarchy to access the parent transform to position each pooled object when it's needed?
– Confused
Nov 18 at 23:42
Sorry, I don't understand the comment. Can you rephrase it?
– Programmer
Nov 18 at 23:49
Sure. @Programmer. Every one of these pooled objects I need to position to where they're going to be effective effects. So I need to grab the parent transform, somewhere, and tell it where to go. Is there much overhead in doing that? Or is it just normal "transform.position = goHereDoYourStuff;"?
– Confused
Nov 19 at 0:31
I really don't know about it's performance hit on doing that but it would make sense to change transform.position than the parent because the changing the parent pos will unnecessary change/update all of the child object transform too.
– Programmer
Nov 19 at 0:35
1
I was overthinking the transform problem. Was super simple. The component has access to its host's transform... DOH! THANK YOU!!!!
– Confused
2 days ago
add a comment |
up vote
4
down vote
accepted
up vote
4
down vote
accepted
In Unity, the Instantiate() and Destroy() functions are used to create copy of objects especially prefabs and destroy them. When it comes to pooling, the pool object is usually represented in the pool as a Type of GameObject. When you need to access a component from the pool you first retrieve the pool GameObject then use the GetComponent function to retrieve the component from a GameObject.
Reading your question and comments carefully, you want to avoid the GetComponent section and represent just the components not the GameObject so that you can also access the components directly.
If this is what you want then this is where Unity's Component is required. See below for steps required to do this.
Note that when I say component/script, I am referring to your scripts that derive from MonoBehaviour that can be attached to GameObjects or built-in Unity components such as Rigidbody and BoxCollider.
1. Store the components/scripts to a List of Component.
List<Component> components;
2. Store the List of Components in a Dictionary with Type as the key and List<Component> as the value. This makes it easier and faster to group and find components by Type.
Dictionary<Type, List<Component>> poolTypeDict;
3. The rest is really easy. Make the function that adds or retrieves the pool items from and to the Dictionary to be generic then use Convert.ChangeType to cast between the generic type to Component type or from generic to what ever type that is requested to be returned.
4. When you need to add item to the Dictionary, check if the Type exist yet, if it does, retrieve the existing key, create and add new Component to it with the Instantiate function then save it to the Dictionary.
If the Type doesn't exist yet, no need to retrieve any data from the Dictionary. Simply create new one and add it to the Dictionary with its Type.
Once you add item to the pool de-activate the GameObject with component.gameObject.SetActive(false)
5. When you need to retrieve an item from the pool, check if the Type exist as key then retrieve the value which is List of Component. Loop over the components and return any component that has a de-activated GameObject. You can check that by checking if component.gameObject.activeInHierarchy is false.
Once you retrieve item from the pool activate the GameObject with component.gameObject.SetActive(true)
If no component is found, you can decide to either return null or instantiate new component.
6. To recycle the item back to the pool when you're done using it, you don't call the Destroy function. Simply de-activate the GameObject with component.gameObject.SetActive(false)*. This will make the component able to be found next time you search for available components in the Dictionary and List.
Below is an example of minimum generic pool system for scripts and components:
public class ComponentPool
{
//Determines if pool should expand when no pool is available or just return null
public bool autoExpand = true;
//Links the type of the componet with the component
Dictionary<Type, List<Component>> poolTypeDict = new Dictionary<Type, List<Component>>();
public ComponentPool() { }
//Adds Prefab component to the ComponentPool
public void AddPrefab<T>(T prefabReference, int count = 1)
{
_AddComponentType<T>(prefabReference, count);
}
private Component _AddComponentType<T>(T prefabReference, int count = 1)
{
Type compType = typeof(T);
if (count <= 0)
{
Debug.LogError("Count cannot be <= 0");
return null;
}
//Check if the component type already exist in the Dictionary
List<Component> comp;
if (poolTypeDict.TryGetValue(compType, out comp))
{
if (comp == null)
comp = new List<Component>();
//Create the type of component x times
for (int i = 0; i < count; i++)
{
//Instantiate new component and UPDATE the List of components
Component original = (Component)Convert.ChangeType(prefabReference, typeof(T));
Component instance = Instantiate(original);
//De-activate each one until when needed
instance.gameObject.SetActive(false);
comp.Add(instance);
}
}
else
{
//Create the type of component x times
comp = new List<Component>();
for (int i = 0; i < count; i++)
{
//Instantiate new component and UPDATE the List of components
Component original = (Component)Convert.ChangeType(prefabReference, typeof(T));
Component instance = Instantiate(original);
//De-activate each one until when needed
instance.gameObject.SetActive(false);
comp.Add(instance);
}
}
//UPDATE the Dictionary with the new List of components
poolTypeDict[compType] = comp;
/*Return last data added to the List
Needed in the GetAvailableObject function when there is no Component
avaiable to return. New one is then created and returned
*/
return comp[comp.Count - 1];
}
//Get available component in the ComponentPool
public T GetAvailableObject<T>(T prefabReference)
{
Type compType = typeof(T);
//Get all component with the requested type from the Dictionary
List<Component> comp;
if (poolTypeDict.TryGetValue(compType, out comp))
{
//Get de-activated GameObject in the loop
for (int i = 0; i < comp.Count; i++)
{
if (!comp[i].gameObject.activeInHierarchy)
{
//Activate the GameObject then return it
comp[i].gameObject.SetActive(true);
return (T)Convert.ChangeType(comp[i], typeof(T));
}
}
}
//No available object in the pool. Expand array if enabled or return null
if (autoExpand)
{
//Create new component, activate the GameObject and return it
Component instance = _AddComponentType<T>(prefabReference, 1);
instance.gameObject.SetActive(true);
return (T)Convert.ChangeType(instance, typeof(T));
}
return default(T);
}
}
public static class ExtensionMethod
{
public static void RecyclePool(this Component component)
{
//Reset position and then de-activate the GameObject of the component
GameObject obj = component.gameObject;
obj.transform.position = Vector3.zero;
obj.transform.rotation = Quaternion.identity;
component.gameObject.SetActive(false);
}
}
USAGE:
It can take a any prefab component script. Prefabs are used for this since pooled objects are usually prefabs instantiated and waiting to be used.
Example prefab scripts (LandingFX, BumperFX) :
public class LandingFX : MonoBehaviour { ... }
and
public class BumperFX : MonoBehaviour { ... }
Two variables to hold the Prefabs references. You can either use public variables and assign them from the Editor or load them with the Resources API.
public LandingFX landingFxPrefab;
public BumperFX bumperFxPrefab;
Create new Component Pool and disable auto-resize
ComponentPool cmpPool = new ComponentPool();
cmpPool.autoExpand = false;
Create 2 pools for LandingFX and BumperFX components. It can take any component
//AddPrefab 2 objects type of LandingFX
cmpPool.AddPrefab(landingFxPrefab, 2);
//AddPrefab 2 objects type of BumperFX
cmpPool.AddPrefab(bumperFxPrefab, 2);
When you need a LandingFX from the pool, you can retrieve them as below:
LandingFX lndngFX1 = cmpPool.GetAvailableObject(landingFxPrefab);
LandingFX lndngFX2 = cmpPool.GetAvailableObject(landingFxPrefab);
When you need a BumperFX from the pool, you can retrieve them as below:
BumperFX bmpFX1 = cmpPool.GetAvailableObject(bumperFxPrefab);
BumperFX bmpFX2 = cmpPool.GetAvailableObject(bumperFxPrefab);
When you're done using the retrieved component, recycle them back to the pool instead of destroying them:
lndngFX1.RecyclePool();
lndngFX2.RecyclePool();
bmpFX1.RecyclePool();
bmpFX2.RecyclePool();
In Unity, the Instantiate() and Destroy() functions are used to create copy of objects especially prefabs and destroy them. When it comes to pooling, the pool object is usually represented in the pool as a Type of GameObject. When you need to access a component from the pool you first retrieve the pool GameObject then use the GetComponent function to retrieve the component from a GameObject.
Reading your question and comments carefully, you want to avoid the GetComponent section and represent just the components not the GameObject so that you can also access the components directly.
If this is what you want then this is where Unity's Component is required. See below for steps required to do this.
Note that when I say component/script, I am referring to your scripts that derive from MonoBehaviour that can be attached to GameObjects or built-in Unity components such as Rigidbody and BoxCollider.
1. Store the components/scripts to a List of Component.
List<Component> components;
2. Store the List of Components in a Dictionary with Type as the key and List<Component> as the value. This makes it easier and faster to group and find components by Type.
Dictionary<Type, List<Component>> poolTypeDict;
3. The rest is really easy. Make the function that adds or retrieves the pool items from and to the Dictionary to be generic then use Convert.ChangeType to cast between the generic type to Component type or from generic to what ever type that is requested to be returned.
4. When you need to add item to the Dictionary, check if the Type exist yet, if it does, retrieve the existing key, create and add new Component to it with the Instantiate function then save it to the Dictionary.
If the Type doesn't exist yet, no need to retrieve any data from the Dictionary. Simply create new one and add it to the Dictionary with its Type.
Once you add item to the pool de-activate the GameObject with component.gameObject.SetActive(false)
5. When you need to retrieve an item from the pool, check if the Type exist as key then retrieve the value which is List of Component. Loop over the components and return any component that has a de-activated GameObject. You can check that by checking if component.gameObject.activeInHierarchy is false.
Once you retrieve item from the pool activate the GameObject with component.gameObject.SetActive(true)
If no component is found, you can decide to either return null or instantiate new component.
6. To recycle the item back to the pool when you're done using it, you don't call the Destroy function. Simply de-activate the GameObject with component.gameObject.SetActive(false)*. This will make the component able to be found next time you search for available components in the Dictionary and List.
Below is an example of minimum generic pool system for scripts and components:
public class ComponentPool
{
//Determines if pool should expand when no pool is available or just return null
public bool autoExpand = true;
//Links the type of the componet with the component
Dictionary<Type, List<Component>> poolTypeDict = new Dictionary<Type, List<Component>>();
public ComponentPool() { }
//Adds Prefab component to the ComponentPool
public void AddPrefab<T>(T prefabReference, int count = 1)
{
_AddComponentType<T>(prefabReference, count);
}
private Component _AddComponentType<T>(T prefabReference, int count = 1)
{
Type compType = typeof(T);
if (count <= 0)
{
Debug.LogError("Count cannot be <= 0");
return null;
}
//Check if the component type already exist in the Dictionary
List<Component> comp;
if (poolTypeDict.TryGetValue(compType, out comp))
{
if (comp == null)
comp = new List<Component>();
//Create the type of component x times
for (int i = 0; i < count; i++)
{
//Instantiate new component and UPDATE the List of components
Component original = (Component)Convert.ChangeType(prefabReference, typeof(T));
Component instance = Instantiate(original);
//De-activate each one until when needed
instance.gameObject.SetActive(false);
comp.Add(instance);
}
}
else
{
//Create the type of component x times
comp = new List<Component>();
for (int i = 0; i < count; i++)
{
//Instantiate new component and UPDATE the List of components
Component original = (Component)Convert.ChangeType(prefabReference, typeof(T));
Component instance = Instantiate(original);
//De-activate each one until when needed
instance.gameObject.SetActive(false);
comp.Add(instance);
}
}
//UPDATE the Dictionary with the new List of components
poolTypeDict[compType] = comp;
/*Return last data added to the List
Needed in the GetAvailableObject function when there is no Component
avaiable to return. New one is then created and returned
*/
return comp[comp.Count - 1];
}
//Get available component in the ComponentPool
public T GetAvailableObject<T>(T prefabReference)
{
Type compType = typeof(T);
//Get all component with the requested type from the Dictionary
List<Component> comp;
if (poolTypeDict.TryGetValue(compType, out comp))
{
//Get de-activated GameObject in the loop
for (int i = 0; i < comp.Count; i++)
{
if (!comp[i].gameObject.activeInHierarchy)
{
//Activate the GameObject then return it
comp[i].gameObject.SetActive(true);
return (T)Convert.ChangeType(comp[i], typeof(T));
}
}
}
//No available object in the pool. Expand array if enabled or return null
if (autoExpand)
{
//Create new component, activate the GameObject and return it
Component instance = _AddComponentType<T>(prefabReference, 1);
instance.gameObject.SetActive(true);
return (T)Convert.ChangeType(instance, typeof(T));
}
return default(T);
}
}
public static class ExtensionMethod
{
public static void RecyclePool(this Component component)
{
//Reset position and then de-activate the GameObject of the component
GameObject obj = component.gameObject;
obj.transform.position = Vector3.zero;
obj.transform.rotation = Quaternion.identity;
component.gameObject.SetActive(false);
}
}
USAGE:
It can take a any prefab component script. Prefabs are used for this since pooled objects are usually prefabs instantiated and waiting to be used.
Example prefab scripts (LandingFX, BumperFX) :
public class LandingFX : MonoBehaviour { ... }
and
public class BumperFX : MonoBehaviour { ... }
Two variables to hold the Prefabs references. You can either use public variables and assign them from the Editor or load them with the Resources API.
public LandingFX landingFxPrefab;
public BumperFX bumperFxPrefab;
Create new Component Pool and disable auto-resize
ComponentPool cmpPool = new ComponentPool();
cmpPool.autoExpand = false;
Create 2 pools for LandingFX and BumperFX components. It can take any component
//AddPrefab 2 objects type of LandingFX
cmpPool.AddPrefab(landingFxPrefab, 2);
//AddPrefab 2 objects type of BumperFX
cmpPool.AddPrefab(bumperFxPrefab, 2);
When you need a LandingFX from the pool, you can retrieve them as below:
LandingFX lndngFX1 = cmpPool.GetAvailableObject(landingFxPrefab);
LandingFX lndngFX2 = cmpPool.GetAvailableObject(landingFxPrefab);
When you need a BumperFX from the pool, you can retrieve them as below:
BumperFX bmpFX1 = cmpPool.GetAvailableObject(bumperFxPrefab);
BumperFX bmpFX2 = cmpPool.GetAvailableObject(bumperFxPrefab);
When you're done using the retrieved component, recycle them back to the pool instead of destroying them:
lndngFX1.RecyclePool();
lndngFX2.RecyclePool();
bmpFX1.RecyclePool();
bmpFX2.RecyclePool();
answered Nov 17 at 1:57
Programmer
73.9k1077138
73.9k1077138
Perhaps unrelated question. Having gotten the component, and using that as the thing to pool and access, is it a big climb up the hierarchy to access the parent transform to position each pooled object when it's needed?
– Confused
Nov 18 at 23:42
Sorry, I don't understand the comment. Can you rephrase it?
– Programmer
Nov 18 at 23:49
Sure. @Programmer. Every one of these pooled objects I need to position to where they're going to be effective effects. So I need to grab the parent transform, somewhere, and tell it where to go. Is there much overhead in doing that? Or is it just normal "transform.position = goHereDoYourStuff;"?
– Confused
Nov 19 at 0:31
I really don't know about it's performance hit on doing that but it would make sense to change transform.position than the parent because the changing the parent pos will unnecessary change/update all of the child object transform too.
– Programmer
Nov 19 at 0:35
1
I was overthinking the transform problem. Was super simple. The component has access to its host's transform... DOH! THANK YOU!!!!
– Confused
2 days ago
add a comment |
Perhaps unrelated question. Having gotten the component, and using that as the thing to pool and access, is it a big climb up the hierarchy to access the parent transform to position each pooled object when it's needed?
– Confused
Nov 18 at 23:42
Sorry, I don't understand the comment. Can you rephrase it?
– Programmer
Nov 18 at 23:49
Sure. @Programmer. Every one of these pooled objects I need to position to where they're going to be effective effects. So I need to grab the parent transform, somewhere, and tell it where to go. Is there much overhead in doing that? Or is it just normal "transform.position = goHereDoYourStuff;"?
– Confused
Nov 19 at 0:31
I really don't know about it's performance hit on doing that but it would make sense to change transform.position than the parent because the changing the parent pos will unnecessary change/update all of the child object transform too.
– Programmer
Nov 19 at 0:35
1
I was overthinking the transform problem. Was super simple. The component has access to its host's transform... DOH! THANK YOU!!!!
– Confused
2 days ago
Perhaps unrelated question. Having gotten the component, and using that as the thing to pool and access, is it a big climb up the hierarchy to access the parent transform to position each pooled object when it's needed?
– Confused
Nov 18 at 23:42
Perhaps unrelated question. Having gotten the component, and using that as the thing to pool and access, is it a big climb up the hierarchy to access the parent transform to position each pooled object when it's needed?
– Confused
Nov 18 at 23:42
Sorry, I don't understand the comment. Can you rephrase it?
– Programmer
Nov 18 at 23:49
Sorry, I don't understand the comment. Can you rephrase it?
– Programmer
Nov 18 at 23:49
Sure. @Programmer. Every one of these pooled objects I need to position to where they're going to be effective effects. So I need to grab the parent transform, somewhere, and tell it where to go. Is there much overhead in doing that? Or is it just normal "transform.position = goHereDoYourStuff;"?
– Confused
Nov 19 at 0:31
Sure. @Programmer. Every one of these pooled objects I need to position to where they're going to be effective effects. So I need to grab the parent transform, somewhere, and tell it where to go. Is there much overhead in doing that? Or is it just normal "transform.position = goHereDoYourStuff;"?
– Confused
Nov 19 at 0:31
I really don't know about it's performance hit on doing that but it would make sense to change transform.position than the parent because the changing the parent pos will unnecessary change/update all of the child object transform too.
– Programmer
Nov 19 at 0:35
I really don't know about it's performance hit on doing that but it would make sense to change transform.position than the parent because the changing the parent pos will unnecessary change/update all of the child object transform too.
– Programmer
Nov 19 at 0:35
1
1
I was overthinking the transform problem. Was super simple. The component has access to its host's transform... DOH! THANK YOU!!!!
– Confused
2 days ago
I was overthinking the transform problem. Was super simple. The component has access to its host's transform... DOH! THANK YOU!!!!
– Confused
2 days ago
add a comment |
up vote
3
down vote
I'm not that pleased with the solution, but combining a nice object pool with a use of simple Dictionary<K, V> yields the following:
// pool of single object type, uses new for instantiation
public class ObjectPool<T> where T : new()
{
// this will hold all the instances, notice that it's up to caller to make sure
// the pool size is big enough not to reuse an object that's still in use
private readonly T _pool = new T[_maxObjects];
private int _current = 0;
public ObjectPool()
{
// performs initialization, one may consider doing lazy initialization afterwards
for (int i = 0; i < _maxObjects; ++i)
_pool[i] = new T();
}
private const int _maxObjects = 100; // Set this to whatever
public T Get()
{
return _pool[_current++ % _maxObjects];
}
}
// pool of generic pools
public class PoolPool
{
// this holds a reference to pools of known (previously used) object pools
// I'm dissatisfied with an use of object here, but that's a way around the generics :/
private readonly Dictionary<Type, object> _pool = new Dictionary<Type, object>();
public T Get<T>() where T : new()
{
// is the pool already instantiated?
if (_pool.TryGetValue(typeof(T), out var o))
{
// if yes, reuse it (we know o should be of type ObjectPool<T>,
// where T matches the current generic argument
return ((ObjectPool<T>)o).Get();
}
// First time we see T, create new pool and store it in lookup dictionary
// for later use
ObjectPool<T> pool = new ObjectPool<T>();
_pool.Add(typeof(T), pool);
return pool.Get();
}
}
Now, you can simply do the following:
pool.Get<A>().SayHello();
pool.Get<B>().Bark();
This however still leaves a room for improvements as it instantiates classes with new rather than your factory method, as well as does not provide a way to customize pool size in a generic way.
Sorry for my ignorance (it knows no bounds), what's the problem with the instantiation of classes with new?, and where and how am I using a factory method? I don't even really know how I came up with my way. I understand arrays, and chose to use them. If a pooled object is in use, I simply deactivate it, thereby resetting it, and use it. Kind of like how polyphony on a good keyboard steals the note played earliest when it runs out of polyphony.
– Confused
Nov 9 at 0:22
There's nothing with usingnew, the thing is thatnew()requires that typeTprovides public parameterless constructor. I assumed you're using factory method because of call to_poolOfLndngFX[i] = Instantiate( LndngFX, transform );, where I assumeInstantiateis some wrapper aroundActivator.CreateInstance.
– orhtej2
Nov 9 at 10:44
2
argh. I think I see. No. Instantiate is a Unity system thingy. It makes copies of things, so... yes. Probably is a factory function.
– Confused
Nov 9 at 13:25
add a comment |
up vote
3
down vote
I'm not that pleased with the solution, but combining a nice object pool with a use of simple Dictionary<K, V> yields the following:
// pool of single object type, uses new for instantiation
public class ObjectPool<T> where T : new()
{
// this will hold all the instances, notice that it's up to caller to make sure
// the pool size is big enough not to reuse an object that's still in use
private readonly T _pool = new T[_maxObjects];
private int _current = 0;
public ObjectPool()
{
// performs initialization, one may consider doing lazy initialization afterwards
for (int i = 0; i < _maxObjects; ++i)
_pool[i] = new T();
}
private const int _maxObjects = 100; // Set this to whatever
public T Get()
{
return _pool[_current++ % _maxObjects];
}
}
// pool of generic pools
public class PoolPool
{
// this holds a reference to pools of known (previously used) object pools
// I'm dissatisfied with an use of object here, but that's a way around the generics :/
private readonly Dictionary<Type, object> _pool = new Dictionary<Type, object>();
public T Get<T>() where T : new()
{
// is the pool already instantiated?
if (_pool.TryGetValue(typeof(T), out var o))
{
// if yes, reuse it (we know o should be of type ObjectPool<T>,
// where T matches the current generic argument
return ((ObjectPool<T>)o).Get();
}
// First time we see T, create new pool and store it in lookup dictionary
// for later use
ObjectPool<T> pool = new ObjectPool<T>();
_pool.Add(typeof(T), pool);
return pool.Get();
}
}
Now, you can simply do the following:
pool.Get<A>().SayHello();
pool.Get<B>().Bark();
This however still leaves a room for improvements as it instantiates classes with new rather than your factory method, as well as does not provide a way to customize pool size in a generic way.
Sorry for my ignorance (it knows no bounds), what's the problem with the instantiation of classes with new?, and where and how am I using a factory method? I don't even really know how I came up with my way. I understand arrays, and chose to use them. If a pooled object is in use, I simply deactivate it, thereby resetting it, and use it. Kind of like how polyphony on a good keyboard steals the note played earliest when it runs out of polyphony.
– Confused
Nov 9 at 0:22
There's nothing with usingnew, the thing is thatnew()requires that typeTprovides public parameterless constructor. I assumed you're using factory method because of call to_poolOfLndngFX[i] = Instantiate( LndngFX, transform );, where I assumeInstantiateis some wrapper aroundActivator.CreateInstance.
– orhtej2
Nov 9 at 10:44
2
argh. I think I see. No. Instantiate is a Unity system thingy. It makes copies of things, so... yes. Probably is a factory function.
– Confused
Nov 9 at 13:25
add a comment |
up vote
3
down vote
up vote
3
down vote
I'm not that pleased with the solution, but combining a nice object pool with a use of simple Dictionary<K, V> yields the following:
// pool of single object type, uses new for instantiation
public class ObjectPool<T> where T : new()
{
// this will hold all the instances, notice that it's up to caller to make sure
// the pool size is big enough not to reuse an object that's still in use
private readonly T _pool = new T[_maxObjects];
private int _current = 0;
public ObjectPool()
{
// performs initialization, one may consider doing lazy initialization afterwards
for (int i = 0; i < _maxObjects; ++i)
_pool[i] = new T();
}
private const int _maxObjects = 100; // Set this to whatever
public T Get()
{
return _pool[_current++ % _maxObjects];
}
}
// pool of generic pools
public class PoolPool
{
// this holds a reference to pools of known (previously used) object pools
// I'm dissatisfied with an use of object here, but that's a way around the generics :/
private readonly Dictionary<Type, object> _pool = new Dictionary<Type, object>();
public T Get<T>() where T : new()
{
// is the pool already instantiated?
if (_pool.TryGetValue(typeof(T), out var o))
{
// if yes, reuse it (we know o should be of type ObjectPool<T>,
// where T matches the current generic argument
return ((ObjectPool<T>)o).Get();
}
// First time we see T, create new pool and store it in lookup dictionary
// for later use
ObjectPool<T> pool = new ObjectPool<T>();
_pool.Add(typeof(T), pool);
return pool.Get();
}
}
Now, you can simply do the following:
pool.Get<A>().SayHello();
pool.Get<B>().Bark();
This however still leaves a room for improvements as it instantiates classes with new rather than your factory method, as well as does not provide a way to customize pool size in a generic way.
I'm not that pleased with the solution, but combining a nice object pool with a use of simple Dictionary<K, V> yields the following:
// pool of single object type, uses new for instantiation
public class ObjectPool<T> where T : new()
{
// this will hold all the instances, notice that it's up to caller to make sure
// the pool size is big enough not to reuse an object that's still in use
private readonly T _pool = new T[_maxObjects];
private int _current = 0;
public ObjectPool()
{
// performs initialization, one may consider doing lazy initialization afterwards
for (int i = 0; i < _maxObjects; ++i)
_pool[i] = new T();
}
private const int _maxObjects = 100; // Set this to whatever
public T Get()
{
return _pool[_current++ % _maxObjects];
}
}
// pool of generic pools
public class PoolPool
{
// this holds a reference to pools of known (previously used) object pools
// I'm dissatisfied with an use of object here, but that's a way around the generics :/
private readonly Dictionary<Type, object> _pool = new Dictionary<Type, object>();
public T Get<T>() where T : new()
{
// is the pool already instantiated?
if (_pool.TryGetValue(typeof(T), out var o))
{
// if yes, reuse it (we know o should be of type ObjectPool<T>,
// where T matches the current generic argument
return ((ObjectPool<T>)o).Get();
}
// First time we see T, create new pool and store it in lookup dictionary
// for later use
ObjectPool<T> pool = new ObjectPool<T>();
_pool.Add(typeof(T), pool);
return pool.Get();
}
}
Now, you can simply do the following:
pool.Get<A>().SayHello();
pool.Get<B>().Bark();
This however still leaves a room for improvements as it instantiates classes with new rather than your factory method, as well as does not provide a way to customize pool size in a generic way.
answered Nov 8 at 22:57
orhtej2
1,6171715
1,6171715
Sorry for my ignorance (it knows no bounds), what's the problem with the instantiation of classes with new?, and where and how am I using a factory method? I don't even really know how I came up with my way. I understand arrays, and chose to use them. If a pooled object is in use, I simply deactivate it, thereby resetting it, and use it. Kind of like how polyphony on a good keyboard steals the note played earliest when it runs out of polyphony.
– Confused
Nov 9 at 0:22
There's nothing with usingnew, the thing is thatnew()requires that typeTprovides public parameterless constructor. I assumed you're using factory method because of call to_poolOfLndngFX[i] = Instantiate( LndngFX, transform );, where I assumeInstantiateis some wrapper aroundActivator.CreateInstance.
– orhtej2
Nov 9 at 10:44
2
argh. I think I see. No. Instantiate is a Unity system thingy. It makes copies of things, so... yes. Probably is a factory function.
– Confused
Nov 9 at 13:25
add a comment |
Sorry for my ignorance (it knows no bounds), what's the problem with the instantiation of classes with new?, and where and how am I using a factory method? I don't even really know how I came up with my way. I understand arrays, and chose to use them. If a pooled object is in use, I simply deactivate it, thereby resetting it, and use it. Kind of like how polyphony on a good keyboard steals the note played earliest when it runs out of polyphony.
– Confused
Nov 9 at 0:22
There's nothing with usingnew, the thing is thatnew()requires that typeTprovides public parameterless constructor. I assumed you're using factory method because of call to_poolOfLndngFX[i] = Instantiate( LndngFX, transform );, where I assumeInstantiateis some wrapper aroundActivator.CreateInstance.
– orhtej2
Nov 9 at 10:44
2
argh. I think I see. No. Instantiate is a Unity system thingy. It makes copies of things, so... yes. Probably is a factory function.
– Confused
Nov 9 at 13:25
Sorry for my ignorance (it knows no bounds), what's the problem with the instantiation of classes with new?, and where and how am I using a factory method? I don't even really know how I came up with my way. I understand arrays, and chose to use them. If a pooled object is in use, I simply deactivate it, thereby resetting it, and use it. Kind of like how polyphony on a good keyboard steals the note played earliest when it runs out of polyphony.
– Confused
Nov 9 at 0:22
Sorry for my ignorance (it knows no bounds), what's the problem with the instantiation of classes with new?, and where and how am I using a factory method? I don't even really know how I came up with my way. I understand arrays, and chose to use them. If a pooled object is in use, I simply deactivate it, thereby resetting it, and use it. Kind of like how polyphony on a good keyboard steals the note played earliest when it runs out of polyphony.
– Confused
Nov 9 at 0:22
There's nothing with using
new, the thing is that new() requires that type T provides public parameterless constructor. I assumed you're using factory method because of call to _poolOfLndngFX[i] = Instantiate( LndngFX, transform );, where I assume Instantiate is some wrapper around Activator.CreateInstance.– orhtej2
Nov 9 at 10:44
There's nothing with using
new, the thing is that new() requires that type T provides public parameterless constructor. I assumed you're using factory method because of call to _poolOfLndngFX[i] = Instantiate( LndngFX, transform );, where I assume Instantiate is some wrapper around Activator.CreateInstance.– orhtej2
Nov 9 at 10:44
2
2
argh. I think I see. No. Instantiate is a Unity system thingy. It makes copies of things, so... yes. Probably is a factory function.
– Confused
Nov 9 at 13:25
argh. I think I see. No. Instantiate is a Unity system thingy. It makes copies of things, so... yes. Probably is a factory function.
– Confused
Nov 9 at 13:25
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53217000%2fhow-to-create-generics-pooling-system-for-components-scripts%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Do BumperFX and LandingFX have a common parent class?
– derHugo
Nov 9 at 23:35
Not really. They're both MonoBehaviours, which is a Unity base class sort of thing, but not in the normal sense of common parent class. And I'm not really familiar with hierarchies in C#, so have my entire project as flat. I kind of think this is something I need to learn to overcome, to use generics. I'm coming from Swift.
– Confused
Nov 10 at 4:07
Is the generic pool supposed to be used for Components/Scripts or GameObjects prefabs?
– Programmer
Nov 11 at 21:18
Hey @Programmer, you legend! The pools are of GameObjects, that often have many sub-objects. The parent usually has the activation components and controller component for the FX that the whole little tree of objects is.
– Confused
Nov 11 at 23:33
1
I wasn't notified on your comment. What I meant is that when you have two objects, for example BulletObject, GrenadeObjects, you can create make the pool take GameObject as param and create pool of BulletObject and GrenadeObjects with two different pool instances. Do you understand me? Should I put code to show you?
– Programmer
Nov 14 at 19:41