Instantiating prefabs from an array of GameObjects
Hi Everyone,
I'm new on here and also quite new to c# and Unity. Please be gentle with me if I'm asking a dumb question.
I was hoping for some help on something that is doing my head in. I am instantiating 2000 prefabs and have them rotate and move around the screen randomly. I have actually got the code working fine if I declare the prefab as global and drop the actual prefab onto the navigator window.
However this means it is always the same prefab floating about, but I want to put some different ones up into the air. I have created an array of prefabs and I am trying to assign one of the 22 different elements, randomly, to the instantiation. This is where the code falls down.
It is the line:
GameObject fragment = (GameObject) Instantiate(fragmentloadarray[iValue], position, Quaternion.Euler
that is causing the error: "NullReferenceException: Object reference to set to an instance of an object"
void Start()
{
fragmentloadarray = new GameObject[22];
fragmentloadarray = Resources.LoadAll("Fragments") as GameObject[];
fragmentarray = new GameObject[numberOfFragments];
for (int count = 0; count < numberOfFragments; count++)
{
Vector3 position = new Vector3(Random.Range(-sizeofarea, sizeofarea), Random.Range((-sizeofarea + 1.9f) / 1.4f, (sizeofarea + 1.9f) / 1.4f), Random.Range(-sizeofarea + 0.8f, sizeofarea + 0.8f));
iValue = Mathf.RoundToInt(Random.Range(0.0f, 21.0f));
GameObject fragment = (GameObject) Instantiate(fragmentloadarray[iValue], position, Quaternion.Euler
(
Random.Range(0.0f, 360.0f),
Random.Range(0.0f, 360.0f),
Random.Range(0.0f, 360.0f)
));
fragmentarray[count] = fragment;
fragment.GetComponent<Rigidbody>().AddTorque(transform.up * Random.Range(-spinrate, spinrate));
fragment.GetComponent<Rigidbody>().AddTorque(transform.right * Random.Range(-spinrate, spinrate));
float scalefactortoapply = Random.Range(0.05f, 0.65f);
Vector3 scale = fragment.transform.localScale;
scale.Set(scalefactortoapply, scalefactortoapply, 0.1f);
fragment.transform.localScale = scale;
fragment.GetComponent<Rigidbody>().AddForce
(
Random.Range(-fragmentspeed, fragmentspeed),
Random.Range(-fragmentspeed, fragmentspeed),
Random.Range(-fragmentspeed, fragmentspeed)
);
}
}
I have been stuck on this for a few days now and would really appreciate some advice from the pro's
Thanks in advance
Ian
I'd also be happy to take any advice on best coding practices, I'm not saying my code is particularly elegant.
Oh and if it helps, my variable declarations are:
//public GameObject prefab;
int numberOfFragments = 2000;
float spinrate = 0.00006f;
float fragmentspeed = 2.5f;
float sizeofarea = 2f;
int iValue;
public GameObject[] fragmentarray;
public GameObject[] fragmentloadarray;
I'm not the most experienced person around, but this seems like it should be working. $$anonymous$$y first suggestion would be to check that your fragmentloadarray actually has something in it.
Try: Debug.Log(fragmentloadarray [0]);
Do you get back the right gameObject? If you get the right object back, I'll have to test it out a little more thoroughly to see what's going on. I just did a quick test and everything worked.
Also, if you're not getting the right prefab, try replacing your line 5 with this ins$$anonymous$$d:
fragmentloadarray = Resources.LoadAll("Fragments", typeof(GameObject)) as GameObject[];
Answer by Danish-gaur · Jul 23, 2016 at 09:52 AM
Hello, The null reference exception is coming because "fragmentloadarray" is empty. The array is empty coz its unable to load from resource. try to assign the array in inspector manually is possible.
Thanks
Answer by IronarmGames_LLC · Jul 23, 2016 at 12:06 PM
@uglymug ,below is a simple way you can manage something like this while keeping the legwork simple and rave via code instead of assigning an array yourself.
This code will automatically look for the file path you specify in the inspector and populate its list with available types.
using UnityEngine;
using System.Collections.Generic;
public class ArrayInstantiator : MonoBehaviour {
[Header("Values")]
public string resourcePath;
public Transform spawnPosition;
public int desiredCount;
public float delayInterval;
[Header("Debug")]
public int currentCount;
public List<GameObject> availableObjects = new List<GameObject>();
void Start() {
availableObjects.AddRange (Resources.LoadAll <GameObject> (resourcePath));
InvokeRepeating ("SpawnObject", delayInterval, delayInterval);
}
void SpawnObject() {
if (currentCount < desiredCount) {
int spawnIdx = Random.Range (0, availableObjects.Count);
GameObject inst = (GameObject)Instantiate (availableObjects[spawnIdx], spawnPosition.position, Quaternion.Euler (Vector3.zero));
// Since we declared a local var of "inst" we can now have a reference
// to this instantiated object and access its properties
} else {
CancelInvoke ("SpawnObject");
}
}
}
Note, the variables listed under the Debug
header will be assigned via script so be sure to not set these values to anything yourself.
Heres some screenshots of the project folder and inspector to give you an idea of the setup:
You'll see that in my Resources
folder i created a SubFolder
called "TestObjects"
and put my items i want spawned in there, then in the spcripts inspector i assigned the Resource Path to "TestObjects/"
as that follows the standard file-folder structure.
Now, you can assign all your logic to the temp variable "inst"
inside of the SpawnObject
function.
Let me know if you need any help with this.
Answer by uglymug · Jul 23, 2016 at 07:58 PM
Hi Guys,
thank you so much to everyone for the responses. The generosity in these forums always inspires me.
thanks especially @IronarmGames_LLC that's an elegant solution. I did find an alternative bit of code (also using lists) that did work (after another 2 days of keyboard bashing). I've included it below in case anyone can make use of it but the solution you provided seems more elegant.
{
// Fragment behaviour
int numberOfFragments = 1600;
float spinrate = 0.000003f;
float fragmentspeed = 0.8f;
float sizeofarea = 2f;
void Start() {
// Get all Fragment prefabs (x22) from Fragments folder and create a list
var loadedObjects = Resources.LoadAll("Fragments");
List<GameObject> gameObjects = new List<GameObject>();
foreach (var loadedObject in loadedObjects)
{
gameObjects.Add(loadedObject as GameObject);
}
for (int count = 0; count < numberOfFragments; count++)
{
// Generate random start positions, also calculates the bounds that the prefabs start within
float actualxaxis = Random.Range(-xaxis/2, xaxis/2);
float actualyaxis = Random.Range((-yaxis/2) + yoffset, (yaxis/2) + yoffset);
float actualzaxis = Random.Range(-zaxis/2, zaxis/2);
Vector3 position = new Vector3(actualxaxis, actualyaxis, actualzaxis);
int pickfragment = Random.Range(0, 21);
GameObject fragment = (GameObject)Instantiate(gameObjects[pickfragment], position, Quaternion.Euler
(
Random.Range(0.0f, 360.0f),
Random.Range(0.0f, 360.0f), // generate random orientations
Random.Range(0.0f, 360.0f)
));
I've stripped out some lines that were not pertinent to the original question, hope it still makes sense.
thanks again everyone
Ian