I have currently made an object pooler for my endless runner mobile game that creates my platform generation by choosing a random deactivated object (which is spawned at the beginning) and places it infront of the new one, this works perfectly.
I am now trying to figure out if it would be suitable to create an empty with loads of different forms of coins (sizes, height ect) and activate a random child within the empty for the coin generation (there would never be more than 1-2 coin prefabs on the screen at once)
My main concern is would doing this cause any performace issues as i’m planning on having around 50 variations and was wondering if it’s wise to have them all in the scene at once even though they’re deactivated?
If it’s just sizes and heights, why dont you just keep your correct setup and make a function that changes the size and height of the coin before you place it in the scene?
If you’re doing object pooling, you can just resize the object as needed before placing it in the scene. I would think this would be better than instantiating and destroying several coins.
Both of those are the same pic, but I get what you mean now.
You could still do object pooling, but it depends on need. Instantiating is generally fine especially if you are just looking to do a few things at a time. But even if you have a pool of 50 items that are off, you should be fine as well.
Ah sweet, would you say that it’s a requirement to object pool for mobile devices? I have been listening to talks/presentations at GDC ect and it seems that it’s the best, but i also read somewhere that ‘planning optimization first’ could be a negative?
Requirement, probably not. But it generally is encouraged to try to use object pooling if you can. It doesn’t mean it’s right for every situation. I know those with more testing and knowledge can probably give you a better idea of the difference performance wise.
I’d be curious what argument there is against optimization first. I do think it’s important to keep it in mind when designing your game systems.
I think a lot of people’s argument against optimization at first is that it takes time away from development and has you stuck on something that honestly might not truly matter compared to other optimizations that you could do. Also it could “complicate” the system more than needed when sometimes it really isn’t needed because it isn’t causing a problem in the beginning.
Like possibly in the case of his problem, he said there is only 1-2 coins on the screen at a time. That isn’t many and instantiating them each time rather than pooling likely wouldn’t be that big of a deal instead of worrying about physic/mesh optimizations which are typically the source of slowness.
The thing is you are worrying about optimizing now but you haven’t done any actually testing to see if a generic and basic way would cause any problems. You should see if the simple way will cause problems/slowdowns and if it does, that’s when you should look at optimizations. An exception could be looping through a list like 10 times, that’s obviously “slow” and probably should be looked at.
My personal opinion is just do it the easiest and simplest way at first unless you absolutely know there will be problems in the future as with any kind of design there is always tons of refactoring because things change and better ideas hit you all the time.
I have lots of pools, and generally they’re around 8-32 objects each. To get one from the pool I scan the pool array for inactive ones then return the first inactive one. To put something back in the pool, I merely deactivate it.
Certainly, there’s more efficient pools, but I’ve found this method to be absolutely no difference in performance since I’m just avoiding allocations, and checking 32 bools is hardly computationally expensive. To optimise further I’d just keep a counter and roll around so that it requires generally less loops for when it’s heavily used or fragmented.
TLDR - pooling does not need to be hyper efficient to be just as effective.
Ah, I thought id do pooling first as i’ve never done it before it was challenging to get what I wanted working,
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PlatformGeneration : MonoBehaviour {
public GameObject[] platforms;
// public List<GameObject> platformList = new List<GameObject>();
public GameObject player;
public Transform generationPoint;
public float minX = 7.0f;
public float maxX = 10.0f;
private float randDist;
public PlatformRecycle platformRecycle;
public float width;
GameObject lastobj;
public platformPools[] PlatformPools;
[HideInInspector]
public bool jumpPlatformSpawned;
public GameObject[] jumpPlatforms;
public int amountOfJumpPlatformsToPool;
[System.Serializable]
public class platformPools
{
public string platFormType;
public GameObject[] platforms;
public int amountToPool;
}
// Use this for initialization
void Start () {
generationPoint = GameObject.Find("GenerationPoint").transform;
GameObject go = platformRecycle.grabRecycledObject();
platformRecycle = GetComponent<PlatformRecycle>();
go.transform.position = new Vector2(transform.position.x, transform.position.y);
lastobj = go;
platformRecycle.GrabLastObject(lastobj);
go.SetActive(true);
}
// Update is called once per frame
void Update ()
{
Spawn();
}
public void Spawn()
{
if (player.GetComponent<PlayerControl>().playerStates == PlayerControl.playerState.Running)
{
if (transform.position.x <= generationPoint.position.x && !jumpPlatformSpawned)
{
randDist = Random.Range(minX, maxX);
GameObject newplatform = platformRecycle.grabRecycledObject();
transform.position = new Vector2(transform.position.x + (newplatform.GetComponent<BoxCollider2D>().size.x / 2) + randDist, transform.position.y);
newplatform.transform.position = transform.position;
newplatform.transform.rotation = transform.rotation;
checkCoinsActive(newplatform);
newplatform.SetActive(true);
lastobj = newplatform;
platformRecycle.GrabLastObject(lastobj);
transform.position = new Vector2(transform.position.x + (newplatform.GetComponent<BoxCollider2D>().size.x / 2), transform.position.y);
if (newplatform.GetComponent<Jumpplatform>() != null)
jumpPlatformSpawned = true;
}
else if (transform.position.x <= generationPoint.position.x && jumpPlatformSpawned)
{
randDist = Random.Range(1f, 15.0f);
GameObject newplatform = platformRecycle.grabRecycledObject();
transform.position = new Vector2(transform.position.x + (newplatform.GetComponent<BoxCollider2D>().size.x / 2) + randDist, transform.position.y);
newplatform.transform.position = transform.position;
newplatform.transform.rotation = transform.rotation;
checkCoinsActive(newplatform);
newplatform.SetActive(true);
lastobj = newplatform;
platformRecycle.GrabLastObject(lastobj);
transform.position = new Vector2(transform.position.x + (newplatform.GetComponent<BoxCollider2D>().size.x / 2), transform.position.y);
jumpPlatformSpawned = false;
}
}
}
public void checkCoinsActive(GameObject obj)
{
// makes sure all coins are active upon activation
Transform[] coins = obj.transform.gameObject.GetComponentInChildren<Transform>().gameObject.GetComponentsInChildren<Transform>(true);
foreach (Transform go in coins)
{
go.transform.gameObject.SetActive(true);
}
}
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PlatformRecycle : MonoBehaviour {
public GameObject player;
public GameObject pooledObject;
public int amountToPool;
PlatformGeneration platformGeneration;
public List<GameObject> newlist;
public List<GameObject> newJumplist;
// Use this for initialization
public List<GameObject> pooledPlatforms;
public List<GameObject> pooledJumpPlatforms;
void Start ()
{
player = GameObject.FindGameObjectWithTag("Player");
platformGeneration = gameObject.GetComponent<PlatformGeneration>();
pooledPlatforms = new List<GameObject>();
for (int i = 0; i < platformGeneration.PlatformPools.Length; i++)
{
for (int j = 0; j < platformGeneration.PlatformPools[i].amountToPool; j++)
{
GameObject obj = (GameObject)Instantiate(platformGeneration.PlatformPools[i].platforms[Random.Range(0, platformGeneration.PlatformPools[i].platforms.Length)]);
obj.SetActive(false);
pooledPlatforms.Add(obj);
}
}
for (int i = 0; i < pooledPlatforms.Count; i++)
{
if (!pooledPlatforms[i].activeInHierarchy)
{
newlist.Add(pooledPlatforms[i]);
}
}
for (int i = 0; i < platformGeneration.jumpPlatforms.Length; i++)
{
{
for (int j = 0; j < platformGeneration.amountOfJumpPlatformsToPool; j++)
{
GameObject obj = (GameObject)Instantiate(platformGeneration.jumpPlatforms[Random.Range(0, platformGeneration.jumpPlatforms.Length)]);
obj.SetActive(false);
pooledJumpPlatforms.Add(obj);
}
}
}
for (int i = 0; i < pooledJumpPlatforms.Count; i++)
{
if (!pooledJumpPlatforms[i].activeInHierarchy)
{
newJumplist.Add(pooledJumpPlatforms[i]);
}
}
}
public GameObject grabRecycledObject()
{
newlist.Clear();
newJumplist.Clear();
for (int i = 0; i < pooledPlatforms.Count; i++)
{
if (!pooledPlatforms[i].activeInHierarchy)
{
newlist.Add(pooledPlatforms[i]);
}
}
for (int i = 0; i < pooledJumpPlatforms.Count; i++)
{
if (!pooledJumpPlatforms[i].activeInHierarchy)
{
newJumplist.Add(pooledJumpPlatforms[i]);
}
}
if (newlist.Count > 0 || newJumplist.Count > 0)
{
GameObject go;
if (!platformGeneration.jumpPlatformSpawned)
{
go = newlist[Random.Range(0, newlist.Count)];
return go;
}
else if (platformGeneration.jumpPlatformSpawned)
{
go = newJumplist[Random.Range(0, newJumplist.Count)];
if (go.activeInHierarchy)
{
newJumplist.Remove(go);
go = newJumplist[Random.Range(0, newJumplist.Count)];
}
return go;
}
// return go;
}
GameObject obj = (GameObject)Instantiate(pooledObject);
obj.SetActive(false);
pooledPlatforms.Add(obj);
return obj;
}
public GameObject GrabLastObject(GameObject go)
{
return go;
}
}
Ive created it so I can assign the objects in a list, which allows me to make sure the right platforms, e.g “Straight platform Variations” and “Jump platform variations” each have its own array so it’s easier to find and change prefabs, on Start, i will instantiate the amount of platforms I have chosen, while also picking random platforms from each array.
I have tested fps on my adroid device, and there is no drops at all at the moment for just the platform generation, and i’m getting 4-6 drawcalls/setpass calls at any given time, is this good?
Excuse me, i want to make a game with a platformer endless runner style.
i do have to administrate what appears on screen and get processed in the mobile phone at time; so COINS, OBSTACLES (ROCKS AND OTHER STUFF), ENEMIES, VEHICLES, BOOST ITEMS, each item should be in a different object pool or all in one? thanks
Next time it might be better to create your own thread for a related, but new question, especially when it’s old… but anyways.
If you’re re-using a lot of objects like that in your endless runner, I think it would be easier to pool them separately, so that you can retrieve what you need when you need it, by checking the appropriate (pool).