using UnityEngine;
using System.Collections.Generic;
public class ObjectPool
{
private GameObject prefab;
private List<GameObject> pool;
public ObjectPool(GameObject prefab, int initialSize)
{
this.prefab = prefab;
this.pool = new List<GameObject>();
for (int i = 0; i < initialSize; i++)
{
AllocateInstance();
}
}
public GameObject GetInstance()
{
if (pool.Count == 0)
{
AllocateInstance();
}
int lastIndex = pool.Count - 1;
GameObject instance = pool[lastIndex];
pool.RemoveAt(lastIndex);
instance.SetActive(true);
return instance;
}
public void ReturnInstance(GameObject instance)
{
instance.SetActive(false);
pool.Add(instance);
}
protected virtual GameObject AllocateInstance()
{
GameObject instance = (GameObject)GameObject.Instantiate(prefab);
instance.SetActive(false);
pool.Add(instance);
return instance;
}
}
And i’m using it with this script to instantiate objects. It should put the objects in random positions around the terrain area. But instead all the objects are in the same position at x = 0 , y = 20 , z = 0 Not random at all.
using System;
using UnityEngine;
using Random = UnityEngine.Random;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class InstantiateObjects : MonoBehaviour
{
public GameObject Spaceship;
public int spaceshipsStartingHeight = 20;
[HideInInspector]
public GameObject[] spaceships;
// for tracking properties change
private Vector3 _extents;
private int _spaceshipCount;
private float _spaceshipSize;
private List<int> randomNumbers = new List<int>();
private ObjectPool bulletPool;
/// <summary>
/// How far to place spheres randomly.
/// </summary>
public Vector3 Extents;
/// <summary>
/// How many spheres wanted.
/// </summary>
public int SpaceShipCount;
public float SpaceShipSize;
// Use this for initialization
void Start()
{
rndNumbers();
Clone();
spaceships = GameObject.FindGameObjectsWithTag("SpaceShip");
}
private void OnValidate()
{
// prevent wrong values to be entered
Extents = new Vector3(Mathf.Max(0.0f, Extents.x), Mathf.Max(0.0f, Extents.y), Mathf.Max(0.0f, Extents.z));
SpaceShipCount = Mathf.Max(0, SpaceShipCount);
SpaceShipSize = Mathf.Max(0.0f, SpaceShipSize);
}
private void Reset()
{
Extents = new Vector3(250.0f, 20.0f, 250.0f);
SpaceShipCount = 100;
SpaceShipSize = 20.0f;
}
// Update is called once per frame
void Update()
{
}
private void Clone()
{
if (Extents == _extents && SpaceShipCount == _spaceshipCount && Mathf.Approximately(SpaceShipSize, _spaceshipSize))
return;
// cleanup
var ShipsToDestroy = GameObject.FindGameObjectsWithTag("SpaceShip");
foreach (var t in ShipsToDestroy)
{
if (Application.isEditor)
{
DestroyImmediate(t);
}
else
{
Destroy(t);
}
}
var withTag = GameObject.FindWithTag("Terrain");
if (withTag == null)
throw new InvalidOperationException("Terrain not found");
bulletPool = new ObjectPool(Spaceship, SpaceShipCount);
for (var i = 0; i < SpaceShipCount; i++)
{
GameObject o = bulletPool.GetInstance();
o.tag = "SpaceShip";
o.transform.SetParent(base.gameObject.transform);
o.transform.localScale = new Vector3(SpaceShipSize, SpaceShipSize, SpaceShipSize);
// get random position
var x = Random.Range(-Extents.x, Extents.x);
var y = Extents.y; // sphere altitude relative to terrain below
var z = Random.Range(-Extents.z, Extents.z);
// now send a ray down terrain to adjust Y according terrain below
var height = 10000.0f; // should be higher than highest terrain altitude
var origin = new Vector3(x, height, z);
var ray = new Ray(origin, Vector3.down);
RaycastHit hit;
var maxDistance = 20000.0f;
var nameToLayer = LayerMask.NameToLayer("Terrain");
var layerMask = 1 << nameToLayer;
if (Physics.Raycast(ray, out hit, maxDistance, layerMask))
{
var distance = hit.distance;
y = height - distance + y; // adjust
}
else
{
Debug.LogWarning("Terrain not hit, using default height !");
}
//o.transform.Rotate(0.0f,randomNumbers[i],0.0f);
// place !
o.transform.position = new Vector3(x, y + spaceshipsStartingHeight, z);
}
_extents = Extents;
_spaceshipCount = SpaceShipCount;
_spaceshipSize = SpaceShipSize;
}
public void rndNumbers()
{
}
}
Call Reset() in the Start method of InstantiateObjects, before Clone().
Also class fields should start with a lowercase, methods and classes are uppercase. eg: RndNumbers() not rndNumbers(), extents not Extents. Not required, just C# convention and avoids confusion.
I haven’t really thoroughly looked at your code, but one thing I’ll point out is Start and Awake are only called once per instance of an object. When you do pooling, you’re enabling/disabling an object (at least, that’s the pooling I’m familiar with), and when you enable, it doesn’t cause your Start/Awake to get called again.
I use Fast Pool for pooling, and I put stuff in OnEnable for initializing stuff i grab out of the pool.
So in short, if your Start function is where you randomize the position, try in OnEnable instead perhaps.
First if it’s in the Start and if i also call Reset() first it will work.
If i move it to the OnEnable like you said then i had to call Reset() in the OnEnable too but i’m also getting exception:
Destroying GameObjects immediately is not permitted during physics trigger/contact, animation event callbacks or OnValidate. You must use Destroy instead.
UnityEngine.Object:smile:estroyImmediate(Object)
InstantiateObjects:Clone() (at Assets/MyScripts/InstantiateObjects.cs:87)
InstantiateObjects:OnValidate() (at Assets/MyScripts/InstantiateObjects.cs:50)
Line 50 is in the OnEnable:
Clone();
And line 87 is:
DestroyImmediate(t);
And last thing how can i use the Update() to change the number of cloned objects in real time.
I mean when i change in the Inspector the value of public int SpaceShipCount; i want it to destroy all objects and create new ones with the new value. So i tried inside the Update to do:
void Update()
{
if (_spaceshipCount != SpaceShipCount)
{
for (var i = 0; i < _spaceshipCount; i++)
{
ReleaseBullet(o);
}
_spaceshipCount = SpaceShipCount;
Reset();
Clone();
}
}
So I’m trying to follow along with your code on mobile, but it’s been tough to follow along…
Am I right in that at the beginning of your clone function you are DESTROYING all your Spaceship objects, and then later on you are going and enabling spaceships from the pool? If so, I’m not sure why you are destroying spaceships. You should actually be releasing them to the pool. It looks like that’s what your ReturnInstance function in your pooler class is for. I could be misunderstanding your code, but I think that’s what you’re doing? If I understand correctly, when doing it this way, you basically aren’t pooling.
If I’m understanding right, then you’ve got a couple issues… you are possibly “creating” your objects from the pool correctly, but not “removing” them correctly. And then, secondly, the Start/Awake thing I mentioned.
Let me know if I understood the part in my 2nd para correctly, and well, if so… then see if you can tackle releasing the instances to the pool properly.
FYI, I apologize if I misunderstood what you were doing there.
You right about the part in your 2nd para.
I got mixed/messed the pooling with my old script.
What i want to do is first to create X number of objects random around the terrain.
Then in the Update when changing the SpaceShipCount in the Inspector destroy(Release with the pooling) and re clone the new number of objects. If there are 100 objects and then while the game is running i change the SpaceShipCount value to 1 then destroy all the objects and create 1 new.
But i don’t want to make it like gun bullets but more like destroying them all at once and create the new once using the pooling.
OK, so now that I understand what you’re doing a little better, a couple things…
I’m not sure where your InstantiateObjects script is attached, but I assume this is some other spawn manager object of some sort, and not a spaceship (I didn’t understand that at first)… so OnEnable on this object/script is not the right place. Is it on some separate unrelated object? If so, that definitely is another reason Start doesn’t work for randomizing stuff. It’s hard for me to say by just looking at code, but it looks to me like you just have this thing destroy all spaceships and then add from the pool once, and then nothing happens (because of it being done in start on whatever object this is happening in).
Really I’d have some manager object that handles your spawning / despawning code (the pulling from and releasing to pool). The spaceships themselves, however, I’d suggest having a script on them that has OnEnable trigger where you set their position randomly. Or, if that doesn’t work, you could do it in the spawn manager object script too (InstantiateObjects), just make sure this is happening in something that happens more than once (ex. Update(), with some sort of criteria to ensure this happens only when you need it to)… because Start will only get called once!
I hope that gives you somewhere to go from here…
So let’s summarize that a little nicer for you…
Have your InstantiateObjects script on some empty game object, maybe called SpawnManager.
Move your check to release everything to the pool, and “recreate” from the pool to some place that happens on some sort of frequent basis… ex., Update()
Consider where you want to put your position randomizing. You can leave it in the InstantiateObjects script (again in Update or InvokeRepeating function or something repeatedly called). That’s fine. I like to have that stuff in a script on the actual objects themselves (OnEnable in this case), but it’s definitely not necessary.
In your case, the reason why it isn’t random, I think is because the destroy/recreate process only happens once… on start… then never again. But that’s just what I think from eyeballing your code.
One more thing… I also see in Start you’re dumping all your spaceships into an array of gameobjects called spaceships. But I don’t see where you ever use this (do you?). Finding objects even by tag might not be the best on performance… so if you don’t use that array, dump it. There’s probably more efficient ways of looking for spaceships to “destroy” (release to the pool) too, but that’s a problem for another time probably.
Now i removed also all the // cleanup part. This way it’s now creating the objects random using the pooling.
The script InstantiateObjects is already attached to a empty GameObject and i dragged in the inspector to the public GameObject Spaceship; a Sphere. So it’s cloning spheres now.
The Spaceship is just names i gave it to this script before just didn’t change the names yet.
Now i’m not sure if i understood how to do the Update part with the releasing and re cloning.
OK, so that’s good that you’ve got it cleaned up a bit. Now, I assume Clone can go in your update since you’ve got it only executing the code if the number of spaceships changes.
For your clone, you might have done this already, but just loop through your “spaceships” and do ReturnInstance for each of them, and then rebuild them from the pool like you were already… and after pulling from the pool, move the object randomly. I can’t really verify your randomization code works, but I assume it does (not seeing the log entry warning you set up?). If things still aren’t working, we might need an updated version of your code posted here.
Off to bed for the night… will check back tomorrow… good luck!