Reputation: 57
Hi I am working on game that uses random terrain and I want to spawn objects onto that terrain. To do this, I have created what I have called the Surface Populator Script.
This is the script:
public SurfaceSpawnerData spawnerData;
private float randomX;
private float randomZ;
private Renderer r;
void Start()
{
r = GetComponent<Renderer>();
for (int i = 0; i < spawnerData.spawnableObjects.Length; i++)
{
spawnerData.spawnableObjects[i].currentObjects = 0;
}
spawnerData.SpawnedObjects.Clear();
SpawnObjects();
}
void Update()
{
}
void SpawnObjects()
{
RaycastHit hit;
for (int i = 0; i < spawnerData.spawnableObjects.Length; i++)
{
int currentObjects = spawnerData.spawnableObjects[i].currentObjects;
int numOfObjects = spawnerData.spawnableObjects[i].numberOfObjects;
if (currentObjects != numOfObjects)
{
if (Physics.Raycast(new Vector3(randomX, r.bounds.max.y + 5f, randomZ), -Vector3.up, out hit))
{
randomX = Random.Range(r.bounds.min.x, r.bounds.max.x);
randomZ = Random.Range(r.bounds.min.z, r.bounds.max.z);
if (hit.point.y >= spawnerData.spawnableObjects[i].spawnerStartHeight && hit.point.y <= spawnerData.spawnableObjects[i].spawnerEndHeight)
{
spawnerData.SpawnedObjects.Add(Instantiate(spawnerData.spawnableObjects[i].spawnablePrefab, hit.point, Quaternion.identity));
spawnerData.spawnableObjects[i].currentObjects += 1;
}
}
}
}
}
The script also gains its data from a scriptable object:
[CreateAssetMenu]
public class SurfaceSpawnerData : ScriptableObject
{
public SpawnableObjects[] spawnableObjects;
public List<GameObject> SpawnedObjects;
[System.Serializable]
public class SpawnableObjects
{
public GameObject spawnablePrefab;
public float spawnerStartHeight = 2f;
public float spawnerEndHeight;
public int currentObjects;
public int numberOfObjects;
}
}
This script currently works perfectly fine when placed inside the update method, however I do not want to do this due to its affect on performance. Therefore I am wondering if there is a way to stop the Unity start method from exiting until my SpawnObjects() function has stopped running. If this is not possible if you have any other ideas on how I run this only once without using the update function let me know.
I am relatively new to c# as a language and I'm sorry if there is an easy fix that I have missed. Any help would be appreciated. Thanks.
Upvotes: 1
Views: 58
Reputation: 90779
Since SpawnObjects
is a synchronus method Start
will not return until SpawnObjects
finished anyway.
As far as I understand your issue is rather that anything from Physics
is not available during initialization (Awake
, OnEnable
, Start
) but only within or after the Physics block (see ExecutionOrder) so e.g. in a method like FixedUpdate
or Update
.
So to answer your question: You could use a Coroutine and WaitForFixedUpdate
in order to make your Instantiation:
void Start()
{
r = GetComponent<Renderer>();
for (int i = 0; i < spawnerData.spawnableObjects.Length; i++)
{
spawnerData.spawnableObjects[i].currentObjects = 0;
}
spawnerData.SpawnedObjects.Clear();
StartCoroutine(DoInstantiate());
}
private IEnumerator DoInstantiate()
{
// wait until Physics are initialized
yield return new WaitForFixedUpdate();
SpawnObjects();
}
or as you can see in ScriptReference/Coroutine you can make this shorter by directly making the Start
a routine e.g. like
IEnumerator Start()
{
r = GetComponent<Renderer>();
for (int i = 0; i < spawnerData.spawnableObjects.Length; i++)
{
spawnerData.spawnableObjects[i].currentObjects = 0;
}
spawnerData.SpawnedObjects.Clear();
// wait until Physics are initialized
yield return new WaitForFixedUpdate();
SpawnObjects();
}
Upvotes: 2