Random position of asteroids -- Random not randomizing?
I'm having an issue with generating asteroids in random positions, based on the random number generator provided in unity. The rest of the Start() function appears to be working properly, as 99 randomly sized asteroids are being created in the scene, but they are all stacked on top of each other, apparently in the same position. I've tried to run the code with and without the Random.InitState(System.Environment.TickCount) and get the same result either way.
Can anyone tell me what the problem is? Code (C#) is attached below.
Thanks!
using System.Collections; using System.Collections.Generic; using UnityEngine;
public class AsteroidGenerator : MonoBehaviour { //meshes and textures that are loaded at run time public Mesh asteroid1; public Mesh asteroid2; public Mesh asteroid3; public Material lightGrey; public Material darkGrey; public Material rockTexture;
// Start is called before the first frame update
void Start()
{
Random.InitState(System.Environment.TickCount);
//function creates 99 asteroids of random size
int i, counter = 0;
for(i = 0; i < 100; ++i)
{
//create a new game object, mesh, and material to attach to it
GameObject theAsteroid = new GameObject("Asteroid" + i);
Mesh asteroid;
Material thisMaterial;
//position where the game object will be placed (random)
Vector3 pos = generateRandomPos();
//depending on count, set mesh and material components
if (counter == 0)
{
thisMaterial = lightGrey;
asteroid = Instantiate(asteroid1, pos, Quaternion.identity);
++counter;
}
if(counter == 1)
{
thisMaterial = darkGrey;
asteroid = Instantiate(asteroid2, pos, Quaternion.identity);
++counter;
}
else
{
thisMaterial = rockTexture;
asteroid = Instantiate(asteroid3, pos, Quaternion.identity);
counter = 0;
}
//assign mesh and material components to gameobject
theAsteroid.AddComponent<MeshFilter>();
theAsteroid.AddComponent<MeshRenderer>();
theAsteroid.GetComponent<MeshRenderer>().material = thisMaterial;
theAsteroid.GetComponent<MeshFilter>().mesh = asteroid;
//set size of asteroid
theAsteroid.transform.localScale = new Vector3(Random.Range(5f, 20f), Random.Range(5f, 20f), Random.Range(5f, 20f));
//add rigidbody and mesh collider
theAsteroid.AddComponent<Rigidbody>();
Rigidbody rb = theAsteroid.GetComponent<Rigidbody>();
rb.useGravity = false;
MeshCollider coll = theAsteroid.GetComponent<MeshCollider>();
theAsteroid.transform.parent = this.transform;
}
}
//generates random position based on seed
Vector3 generateRandomPos()
{
//Random.InitState(time);
float x = Random.Range(-500, 500);
float y = Random.Range(-100, 100);
float z = Random.Range(-700, 700);
return new Vector3(x, y, z);
}
}
Answer by Bunny83 · Aug 28, 2019 at 04:00 AM
Youre code is very confusing and we don't see all details. However you don't seem to instantiate actual prefabs of asteroids but just Meshes. A Mesh does not have a position that could be set by Instantiate. Passing a position and rotation to instantiate only makes sense when you actually instantiate a gameobject / prefab.
What you do is you create a new gameobject at the start of your loop:
GameObject theAsteroid = new GameObject("Asteroid" + i);
this gameobject will be at "0,0,0". At no point in your code will you move this gameobject to a different position. You essentially build the entire gameobject from scratch. There's also no point in actually instantiating the Mesh. The mesh of a model can and should be used by several gameobject, otherwise you blow up your graphics memory for no reason.
Your different asteroid types should be prefabs and all the things you setup procedurally can be setup once in the editor when creating the prefabs. If you really do not want to use prefabs and create everything procedurally, you should do this instead:
void Start()
{
Random.InitState(System.Environment.TickCount);
for(int i = 0; i < 100; ++i)
{
//create a new game object, mesh, and material to attach to it
GameObject theAsteroid = new GameObject("Asteroid" + i);
Mesh asteroid;
Material thisMaterial;
//depending on count, set mesh and material components
int counter = i%3;
if (counter == 0)
{
thisMaterial = lightGrey;
asteroid = asteroid1;
}
else if(counter == 1)
{
thisMaterial = darkGrey;
asteroid = asteroid2;
}
else
{
thisMaterial = rockTexture;
asteroid = asteroid3;
}
//assign mesh and material components to gameobject
theAsteroid.AddComponent<MeshFilter>().sharedMesh = asteroid;
theAsteroid.AddComponent<MeshRenderer>().sharedMaterial = thisMaterial;
//set size of asteroid
theAsteroid.transform.localScale = new Vector3(Random.Range(5f, 20f), Random.Range(5f, 20f), Random.Range(5f, 20f));
// set random position
theAsteroid.transform.position = generateRandomPos();
//add rigidbody and mesh collider
theAsteroid.AddComponent<Rigidbody>().useGravity = false;
theAsteroid.AddComponent<MeshCollider>();
theAsteroid.transform.parent = this.transform;
}
}
Note instead of using variables asteroid1, asteroid2 and lightGrey; darkGrey, ... You probably want to use an array instead. That way you don't need this if-else chain since you can directly access the mesh / material you want. By using the array length the code will automatically adapt to the number of materials / meshes you assigned in the inspector.
With prefabs the code would be much simpler:
public Transform[] asteroidPrefabs;
void Start()
{
Random.InitState(System.Environment.TickCount);
for(int i = 0; i < 100; ++i)
{
int counter = i%asteroidPrefabs.Length;
Transform prefab = asteroidPrefabs[counter];
Transform theAsteroid = Instantiate(prefab, generateRandomPos(), Quaternion.identity)
//set size of asteroid
theAsteroid.localScale = new Vector3(Random.Range(5f, 20f), Random.Range(5f, 20f), Random.Range(5f, 20f));
theAsteroid.parent = transform;
}
}
All the components and setting would simply be setup in Unity. Create 3 prefabs, attach a rigidbody component, set useGravity to false, etc... All you have to do is assign your different prefabs to the "asteroidPrefabs" array.
Thank you so much! That solved my problem -- I didn't realize that creating the game object first would set its position. I am just starting out with coding in unity, and wasn't entirely familiar with the concept of a prefab, but that is SO much better. I will definitely use those in the future. Again, thank you for taking the time to help me solve this, I really appreciate it!