- Home /
Instantiating a random prefab.
So I once asked about this code before with random materials but now I need this for random prefabs and i'm not sure how to achieve it. (It won't work currently as I changed the prefab variable to a list to store the objects in the inspector)
using UnityEngine;
using System.Collections.Generic;
public class SkylineManager : MonoBehaviour {
public List<Transform> prefab;
public int numberOfObjects;
public float recycleOffset;
public Vector3 minSize, maxSize, minGap, maxGap;
public float minY, maxY;
private Vector3 nextPosition;
private Queue<Transform> objectQueue;
void Start () {
objectQueue = new Queue<Transform>(numberOfObjects);
for(int i = 0; i < numberOfObjects; i++){
objectQueue.Enqueue((Transform)Instantiate(prefab));
}
nextPosition = transform.localPosition;
for(int i = 0; i < numberOfObjects; i++){
Recycle();
}
}
void Update () {
if(objectQueue.Peek().localPosition.x + recycleOffset < Player.distanceTraveled){
Recycle();
}
}
private void Recycle () {
Vector3 scale = new Vector3(
Random.Range(minSize.x, maxSize.x),
Random.Range(minSize.y, maxSize.y),
Random.Range(minSize.z, maxSize.z));
Vector3 position = nextPosition;
position.x += scale.x * 0.5f;
position.y += scale.y * 0.5f;
Transform o = objectQueue.Dequeue();
o.localScale = scale;
o.localPosition = position;
objectQueue.Enqueue(o);
nextPosition += new Vector3(
Random.Range(minGap.x, maxGap.x) + scale.x,
Random.Range(minGap.y, maxGap.y),
Random.Range(minGap.z, maxGap.z));
if(nextPosition.y < minY){
nextPosition.y = minY + maxGap.y;
}
else if(nextPosition.y > maxY){
nextPosition.y = maxY - maxGap.y;
}
}
}
Answer by sacredgeometry · Mar 03, 2013 at 09:10 PM
RIght to explain the code, first start a new scene and do this.
Create a new scene
Add the script to a game object with a visible object like a cube or sphere
populate the prefabs array/List on the object with some cubes of different variations(i tested with cubs with different materials)
set the cubeSpawnPoint transform to an empty game object and stick it close to the player.
run it
Basically what is happening is that its instantiating an object and picking a random object from the prefabs list, its then parenting it to an empty game object so that al the blocks are organised in your hierarchy, then it is moving the blocks to the position of the cumbeSpawnPoint Transform and translating them based of the last cube position an the current cubes size, so in theory this will work with any shape size cubes as long as they are being stacked in the z axis. (i may have over looked something but I will do more testing later to check that this works)
Looping Functionality
I have started to build in looping functionality for you so but got distracted last night, it just involves a switch and an enum (for ease of use). Basically when the enum is on a certain value it stops adding to the blocksInLevel list and receiving a random index of the prefab list for instantiation. It then could either use the current in game blocks and set their position so that you only ever have a certain amount of blocks in game or it can iterate through the blocksInLevel list and instantiate new blocks based on the current sequence.
Pretty simple stuff.
Pastebin link, might be easier to copy and paste from
I have tried to document the script and make it as self documenting as possible, if there are any questions let me know.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class LevelBlockSpawner : MonoBehaviour {
public List<GameObject> prefabs = new List<GameObject>(); // This contains the different type of blocks
private List<GameObject> blocksInLevel = new List<GameObject>(); // This contains a reference to current configuration of the block in game
private GameObject BlockStorage; // This GameObject is used to organise all the instantiated cubes
private Vector3 lastBlockPosition = new Vector3(0,0,0); //
public Transform cubeSpawnPoint;
private int initialBlockAmount = 12;
private int blocksAhead = 8;
private int blockCreateAmount = 3;
private int blockDeleteCounter = 0;
public enum Mode
{
continuous,
looping
}
public Mode BlockMode = Mode.continuous;
// INIT
// -------------------------------------------------------------------------------------------------------------------------------------------
void Start () {
BlockStorage = new GameObject("Block Storage"); // Instantiates the block storage object
// Error handeling for if thhe cubeSpawnPoint transform was not chosen (avoids null reference)
if(cubeSpawnPoint == null){
cubeSpawnPoint = this.transform;
}
// Instantiates n amount of blocks (based on the initialBlockAmoun variable) just to populate the level at the start of game
for (int i = 0; i < initialBlockAmount; i++) {
CreateBlock();
}
}
// -------------------------------------------------------------------------------------------------------------------------------------------
void Update () {
/* This if statement checks to see if the players position ( specifically, this game object) has got the size of the cube * blockAhead
* distance away form the last block. Essentially blocksAhead variable tests to see if the player is that many blocks away from hitting the end
* It then creates an amount of blocks based on blockCreateAmount variable ... so 3 which seems to be enough at most speeds.
*/
if(this.transform.position.z >= lastBlockPosition.z - (blocksInLevel[blocksInLevel.Count-1].transform.lossyScale.z * blocksAhead)){
for (int i = 0; i < blockCreateAmount; i++) {
CreateBlock();
Destroy(blocksInLevel[blockDeleteCounter]);
blockDeleteCounter++;
}
}
}
/*
* This cretes a single block and adds it to the blocksInLevel List <GameObject> : This is done to keep an eye on the order and allow you to remove
* Blocks based on their order or loop blocks or slice parts out of the list for isolated selection.
*/
void CreateBlock (){
try {
GameObject tmpCube = (GameObject)Instantiate(prefabs[ Random.Range(0, prefabs.Count - 1) ] );
tmpCube.transform.parent = BlockStorage.transform;
blocksInLevel.Add(tmpCube);
tmpCube.transform.position = cubeSpawnPoint.position;
tmpCube.transform.Translate(0.0f ,0.0f , lastBlockPosition.z + tmpCube.transform.localScale.z);
lastBlockPosition = tmpCube.transform.position;
} catch{
print("There is an issue with the prefabs array, make sure it has at is populated and there are no empty slots.");
}
}
// THIS IS FOR DEMONSTRATION YOU MAY REMOVE THIS
// -------------------------------------------------------------------------------------------------------------------------------------------
void FixedUpdate()
{
float speed = 0.05f;
rigidbody.AddForce(transform.forward * speed, ForceMode.VelocityChange);
}
// -------------------------------------------------------------------------------------------------------------------------------------------
}
I really don't mean to sound sarcastic or ungrateful for your answer but if I knew how to do that I would have done it, could you show me how, the notes on such things seem to be a bit sparse for code like this.
I assumed you knew considering theres already a list...i will amend my answer
$$anonymous$$ost of the code I have I understand, but in this and one other case it's more of an assemblage of copied code from various unity answers pages.
alternativly because you already have the transforms you can do the same instantiate but change the prefabList to the "prefab" list you have already but use prefab.gameObject to grab the gameObject.
Answer by grossindel · Jan 27, 2014 at 09:48 PM
Your code returned an error. ArgumentOutOfRangeException: Argument is out of range. Parameter name: index
This section is for answers and my code is a roughly coded example. Check that you have gameObjects in your prefab array
Your answer
Follow this Question
Related Questions
How to access property of a prefab before Instantiating. 1 Answer
Make sure objects are not instantiated on each other 2 Answers
C# Spawn Random Object, at Random 2D Location 2 Answers
Drop item from airplane 1 Answer
Instantiate issues 1 Answer