Should I pool a non-monobehaviour class?
I have an infinite generated level. The level is made up of "tiles". The tiles are a non-mono class which holds a reference to a GameObject. Should I pool this class?
Tile Class
using UnityEngine;
public class Tile { // Meta public GameObject parent; // Parent Object public GameObject obj; // Actual tileObject public GameObject text; // Perfect text effect
// Must be reset on pool
public Tile prevTile;
public Tile nextTile;
// Input & direction
// Must be reset to false if pooled
public Inputs input;
public D direction;
public Rigidbody rb;
public Renderer rend;
public MeshRenderer flashRenderer;
public SpriteRenderer inputRenderer;
public Transform inputTransform;
public ParticleSystem inputEffect;
public ParticleSystem perfectEffect;
// Must be reset on pool
public Sprite inputSprite;
public BoxCollider collider;
// Flags
// Must all be reset to false if pooled
public bool dead = false;
public bool current = false;
public bool change = false;
public bool jump = false;
public bool bounce = false;
public bool corner = false;
public bool isFinish = false;
public bool shaking = false;
public bool rippling = false;
public bool flashing = false;
// Animation
// Must be reset on pool
public int inputSpriteIndex;
public float time = 0f;
public Vector3 startShakePos;
public int shakeRot = 1;
public float currentShake;
public float textScaleTime = 0.5f;
public Tile (GameObject p)
{
parent = p;
collider = p.GetComponent<BoxCollider> ();
collider.isTrigger = false;
obj = p.transform.GetChild (0).gameObject;
text = p.transform.GetChild (4).gameObject;
text.SetActive (false);
rb = parent.GetComponent<Rigidbody> ();
rend = obj.GetComponent <Renderer> ();
inputTransform = parent.transform.GetChild (1);
inputRenderer = parent.transform.GetChild (1).GetComponent<SpriteRenderer> ();
inputEffect = parent.transform.GetChild (2).gameObject.GetComponent<ParticleSystem> ();
perfectEffect = parent.transform.GetChild (3).gameObject.GetComponent <ParticleSystem> ();
inputTransform.localScale = new Vector3 (0.3f , 0.3f , 0.3f);
}
}
Initialization
public Tile GetProceduralTile ()
{
// Get prev tile and save its pos and rotation
prevTile = m.tiles [m.tiles.Count - 1].parent.transform;
position = prevTile.position;
rotation = prevTile.eulerAngles;
// For shake tiles so they always shake horizontally
if (rotation != lastRotation)
tileShakeRot *= -1;
lastRotation = rotation;
// Plan the tile
planner.PlanTile ();
// Get the tile object from pool
tileObj = Object.Instantiate (refs.tilePrefabs [(int) planner.tileType]);
// Instantiate a new Tile class
tile = new Tile (tileObj); // TODO (maybe) pool the Tile class too
tile.rb.isKinematic = true;
tile.collider.isTrigger = false;
// Set the pos , rot , scale of the parent
tileObj.transform.position = position;
tileObj.transform.eulerAngles = rotation;
tileObj.transform.localScale = new Vector3 (tileSizeXZ , tileSizeY , tileSizeXZ);
// Offset and rotate the parent
tileObj.transform.Translate (offsets [planner.offset]);
tileObj.transform.Rotate (rotations [(int) planner.tileType] , Space.World);
// Add the Tile class to the parent object's hitboxcomponent
tileObj.GetComponent<TileHitBox> ().tile = tile;
// Set the parent tag
tileObj.tag = normalTag;
// Set tile flags
tile.jump = planner.jump;
tile.change = planner.change;
tile.bounce = planner.bounce;
tile.corner = planner.corner;
// Set tile direction and input and sprite
tile.input = planner.input;
tile.shakeRot = tileShakeRot;
// Return the Tile
return tile;
}
Depends. Can you provide some more info on the conditions where you'd create/delete an object?
Also does it only contain a gameobject reference or do you also instantiate it?
I updated my question with my code.
I often pool non-monobehaviour objects, mostly collections that have a temporary use
But in my case, I have to reset alot of the variables if I pool them. I am wondering if this is more costly than just instantiating a new class with all the data already set to default?
I don't think that setting a couple of variables will have a great impact. Garbage collector in Unity is getting better, but until now, the GC hiccups were a major issue, so I was avoiding any unnecessary allocation. You will have to benchmark it yourself I guess. But as you said, even when the class is instantiated, it's default constructor set every variable to default.
If every new level creates a list of 300 of these things, you may as well create the list once and re-use it for each level. That's pretty much all pooling is. If you create and destroy a dozen each frame, you may as well put old ones on the UnusedTile list and grab new ones from it.
Object pooling was invented for normal classes, long before Unity. It's not like you can only pool gameObjects, or it only saves time for them.
I only instantiate one every 0.5-1 second.
Answer by Captain_Pineapple · Sep 09, 2019 at 09:51 AM
Looking at your code there is basically just one line that is bothersome: tileObj = Object.Instantiate (refs.tilePrefabs [(int) planner.tileType]);
all the rest can be done a few hundered times every frame without any further notice in performance. But instantiation is costly. So your question should be: if you pool, can you get around the instantiation part? if yes -> use pooling. If no -> not sure if there will be any positive effect besides perhaps the GC benefits resulting from pooling. But since you also have to search for inactive pooled objects each time you want to activate one you have to add some performance cost right there. So in that case you'd simply have to try and test it.