- Home /
How do I Instantiate a prefab in a randomly generated location specific to tile type (2D Procedural game)
Hey all, newer to Unity and I've been messing around with the board creator script found here and attempting to combine it with the enemy spawn portion of the 2D rogue like tutorial here however I cannot get the "new List" function to work properly. This portion of code to my understanding is supposed to create a list of possible vectors based on the x and y of columns and rows generated and then on my enemy instantiate portion of script it will call random values from the list of possible vectors and assign them to the newly created prefab transforms.
I am trying to make it so that it will spawn a random number of enemies on the board only in the spaces occupied by the tiles assigned to the room array.
Eg)
private Room[] rooms;
public GameObject[] enemyTiles;
number of enemies = random.range (0, rooms.length);
GameObject tileChoice = enemyTile[Random.Range (0, enemyTile.Length)];
Instantiate(tileChoice, "random position in a randomly generated room", Quaternion.identity);
This is the section of code that should be creating the list of Vectors:
public List gridPositions = new List ();
void InitialiseList ()
{
//Clear our list gridPositions.
gridPositions.Clear ();
//Loop through x axis (columns).
for(int x = 1; x < columns-1; x++)
{
//Within each column, loop through y axis (rows).
for(int y = 1; y < rows-1; y++)
{
//At each index add a new Vector3 to our list with the x and y coordinates of that position.
gridPositions.Add (new Vector3(x, y, 0f));
}
}
}
which returns the error: Using the generic type 'System.Collections.Generic.List' requires 1 type arguments
Alternatively if anyone knows how I could randomly generate an enemy during the room and corridor portion of the board creator rather than instantiating them after the rooms have all been created that works as well if not better.
Any help is appreciated :)
Here is the full board creator code for the sake of testing. (note this calls from some of the scripts and assets in the above linked pages and will not properly function without them)
using System.Collections;
using UnityEngine;
using Random = UnityEngine.Random;
using System;
using System.Collections.Generic;
public class BoardCreator : MonoBehaviour
{
// The type of tile that will be laid in a specific position.
public enum TileType
{
Wall, Floor,
}
public int columns = 100; // The number of columns on the board (how wide it will be).
public int rows = 100; // The number of rows on the board (how tall it will be).
public IntRange numRooms = new IntRange (15, 20); // The range of the number of rooms there can be.
public IntRange roomWidth = new IntRange (3, 10); // The range of widths rooms can have.
public IntRange roomHeight = new IntRange (3, 10); // The range of heights rooms can have.
public IntRange corridorLength = new IntRange (6, 10); // The range of lengths corridors between rooms can have.
public GameObject[] floorTiles; // An array of floor tile prefabs.
public GameObject[] wallTiles; // An array of wall tile prefabs.
public GameObject[] outerWallTiles; // An array of outer wall tile prefabs.
public GameObject[] enemyTiles;
public GameObject player;
private int playerstartX;
private int playerstartY;
public int enemyCount = 3;
private TileType[][] tiles; // A jagged array of tile types representing the board, like a grid.
private Room[] rooms; // All the rooms that are created for this board.
private Corridor[] corridors; // All the corridors that connect the rooms.
private GameObject boardHolder; // GameObject that acts as a container for all other tiles.
private void Start ()
{
// Create the board holder.
boardHolder = new GameObject("BoardHolder");
SetupTilesArray ();
CreateRoomsAndCorridors ();
SetTilesValuesForRooms ();
SetTilesValuesForCorridors ();
InstantiateTiles ();
InstantiateOuterWalls ();
InstantiatePlayer ();
LayoutObjectAtRandom (enemyTiles, enemyCount, enemyCount);
}
void SetupTilesArray ()
{
// Set the tiles jagged array to the correct width.
tiles = new TileType[columns][];
// Go through all the tile arrays...
for (int i = 0; i < tiles.Length; i++)
{
// ... and set each tile array is the correct height.
tiles[i] = new TileType[rows];
}
}
void CreateRoomsAndCorridors ()
{
// Create the rooms array with a random size.
rooms = new Room[numRooms.Random];
// There should be one less corridor than there is rooms.
corridors = new Corridor[rooms.Length - 1];
// Create the first room and corridor.
rooms[0] = new Room ();
corridors[0] = new Corridor ();
// Setup the first room, there is no previous corridor so we do not use one.
rooms[0].SetupRoom(roomWidth, roomHeight, columns, rows);
// Setup the first corridor using the first room.
corridors[0].SetupCorridor(rooms[0], corridorLength, roomWidth, roomHeight, columns, rows, true);
for (int i = 1; i < rooms.Length; i++)
{
// Create a room.
rooms[i] = new Room ();
// Setup the room based on the previous corridor.
rooms[i].SetupRoom (roomWidth, roomHeight, columns, rows, corridors[i - 1]);
// If we haven't reached the end of the corridors array...
if (i < corridors.Length)
{
// ... create a corridor.
corridors[i] = new Corridor ();
// Setup the corridor based on the room that was just created.
corridors[i].SetupCorridor(rooms[i], corridorLength, roomWidth, roomHeight, columns, rows, false);
}
//if (i == rooms.Length *.5f)
//{
// Vector3 playerPos = new Vector3 (rooms[i].xPos, rooms[i].yPos, 0);
// Instantiate(player, playerPos, Quaternion.identity);
//}
}
}
void SetTilesValuesForRooms ()
{
// Go through all the rooms...
for (int i = 0; i < rooms.Length; i++)
{
Room currentRoom = rooms[i];
// ... and for each room go through it's width.
for (int j = 0; j < currentRoom.roomWidth; j++)
{
int xCoord = currentRoom.xPos + j;
// For each horizontal tile, go up vertically through the room's height.
for (int k = 0; k < currentRoom.roomHeight; k++)
{
int yCoord = currentRoom.yPos + k;
// The coordinates in the jagged array are based on the room's position and it's width and height.
tiles[xCoord][yCoord] = TileType.Floor;
}
}
}
}
void SetTilesValuesForCorridors ()
{
// Go through every corridor...
for (int i = 0; i < corridors.Length; i++)
{
Corridor currentCorridor = corridors[i];
// and go through it's length.
for (int j = 0; j < currentCorridor.corridorLength; j++)
{
// Start the coordinates at the start of the corridor.
int xCoord = currentCorridor.startXPos;
int yCoord = currentCorridor.startYPos;
// Depending on the direction, add or subtract from the appropriate
// coordinate based on how far through the length the loop is.
switch (currentCorridor.direction)
{
case Direction.North:
yCoord += j;
break;
case Direction.East:
xCoord += j;
break;
case Direction.South:
yCoord -= j;
break;
case Direction.West:
xCoord -= j;
break;
}
// Set the tile at these coordinates to Floor.
tiles[xCoord][yCoord] = TileType.Floor;
}
}
}
void InstantiateTiles ()
{
// Go through all the tiles in the jagged array...
for (int i = 0; i < tiles.Length; i++)
{
for (int j = 0; j < tiles[i].Length; j++)
{
// ... and instantiate a floor tile for it.
InstantiateFromArray (floorTiles, i, j);
// If the tile type is Wall...
if (tiles[i][j] == TileType.Wall)
{
// ... instantiate a wall over the top.
InstantiateFromArray (wallTiles, i, j);
}
}
}
}
void InstantiateOuterWalls ()
{
// The outer walls are one unit left, right, up and down from the board.
float leftEdgeX = -1f;
float rightEdgeX = columns + 0f;
float bottomEdgeY = -1f;
float topEdgeY = rows + 0f;
// Instantiate both vertical walls (one on each side).
InstantiateVerticalOuterWall (leftEdgeX, bottomEdgeY, topEdgeY);
InstantiateVerticalOuterWall(rightEdgeX, bottomEdgeY, topEdgeY);
// Instantiate both horizontal walls, these are one in left and right from the outer walls.
InstantiateHorizontalOuterWall(leftEdgeX + 1f, rightEdgeX - 1f, bottomEdgeY);
InstantiateHorizontalOuterWall(leftEdgeX + 1f, rightEdgeX - 1f, topEdgeY);
}
void InstantiateVerticalOuterWall (float xCoord, float startingY, float endingY)
{
// Start the loop at the starting value for Y.
float currentY = startingY;
// While the value for Y is less than the end value...
while (currentY <= endingY)
{
// ... instantiate an outer wall tile at the x coordinate and the current y coordinate.
InstantiateFromArray(outerWallTiles, xCoord, currentY);
currentY++;
}
}
void InstantiateHorizontalOuterWall (float startingX, float endingX, float yCoord)
{
// Start the loop at the starting value for X.
float currentX = startingX;
// While the value for X is less than the end value...
while (currentX <= endingX)
{
// ... instantiate an outer wall tile at the y coordinate and the current x coordinate.
InstantiateFromArray (outerWallTiles, currentX, yCoord);
currentX++;
}
}
void InstantiateFromArray (GameObject[] prefabs, float xCoord, float yCoord)
{
// Create a random index for the array.
int randomIndex = Random.Range(0, prefabs.Length);
// The position to be instantiated at is based on the coordinates.
Vector3 position = new Vector3(xCoord, yCoord, 0f);
// Create an instance of the prefab from the random index of the array.
GameObject tileInstance = Instantiate(prefabs[randomIndex], position, Quaternion.identity) as GameObject;
// Set the tile's parent to the board holder.
tileInstance.transform.parent = boardHolder.transform;
}
void InstantiatePlayer ()
{
playerstartX = Mathf.RoundToInt (columns / 2f);
playerstartY = Mathf.RoundToInt (rows / 2f);
Vector3 playerstart = new Vector3 (playerstartX, playerstartY, 0f);
GameObject player1 = Instantiate(player, playerstart, Quaternion.identity) as GameObject;
}
public List gridPositions = new List ();
void InitialiseList ()
{
//Clear our list gridPositions.
gridPositions.Clear ();
//Loop through x axis (columns).
for(int x = 1; x < columns-1; x++)
{
//Within each column, loop through y axis (rows).
for(int y = 1; y < rows-1; y++)
{
//At each index add a new Vector3 to our list with the x and y coordinates of that position.
gridPositions.Add (new Vector3(x, y, 0f));
}
}
}
Vector3 RandomPosition ()
{
//Declare an integer randomIndex, set it's value to a random number between 0 and the count of items in our List gridPositions.
int randomIndex = Random.Range (0, gridPositions.Count);
//Declare a variable of type Vector3 called randomPosition, set it's value to the entry at randomIndex from our List gridPositions.
Vector3 randomPosition = gridPositions[randomIndex];
//Remove the entry at randomIndex from the list so that it can't be re-used.
gridPositions.RemoveAt (randomIndex);
//Return the randomly selected Vector3 position.
return randomPosition;
}
void LayoutObjectAtRandom (GameObject[] tileArray, int minimum, int maximum)
{
//Choose a random number of objects to instantiate within the minimum and maximum limits
int objectCount = Random.Range (minimum, maximum+1);
//Instantiate objects until the randomly chosen limit objectCount is reached
for(int i = 0; i < objectCount; i++)
{
//Choose a position for randomPosition by getting a random position from our list of available Vector3s stored in gridPosition
Vector3 randomPosition = RandomPosition();
//Choose a random tile from tileArray and assign it to tileChoice
GameObject tileChoice = tileArray[Random.Range (0, tileArray.Length)];
//Instantiate tileChoice at the position returned by RandomPosition with no change in rotation
Instantiate(tileChoice, randomPosition, Quaternion.identity);
}
}
//LayoutObjectAtRandom (enemyTiles, enemyCount, enemyCount);
}
Answer by KittenSnipes · May 06, 2018 at 10:12 AM
@unity_BGmJcdED5Dqjfw This should be correct. You never told the list what objects it would be holding and I took an educated guess that it would hold objects of type Vector3. Now it should work. If there are any problems feel free to comment and I will help the best I can. Cheers! :D
public List<Vector3> gridPositions = new List<Vector3>();
void InitialiseList ()
{
//Clear our list gridPositions.
gridPositions.Clear ();
//Loop through x axis (columns).
for(int x = 1; x < columns-1; x++)
{
//Within each column, loop through y axis (rows).
for(int y = 1; y < rows-1; y++)
{
//At each index add a new Vector3 to our list with the x and y coordinates of that position.
gridPositions.Add (new Vector3(x, y, 0f));
}
}
}
I had tried this but turns out I forgot to call InitialiseList in my start function so it was there but never being implemented. Adding the Vector3 type and actually initializing the list on start worked perfect. The only thing now is to figure out how to make it only spawn on tiles tagged "floor" as currently they spawn in the wall tiles as well.
Figured out how to deter$$anonymous$$e the center of each room created and spawn an enemy in those rooms from the array of enemy tiles so now the number of enemies will be equal to the number of rooms and only spawn on the map where the rooms are located :)
void EnemySpawner ()
{
for (int i = 1; i < rooms.Length; i++)
{
Vector2 room$$anonymous$$iddle = new Vector2 (rooms[i].roomWidth / 2f, rooms[i].roomHeight / 2f);
Vector2 roomPosition = new Vector2 (rooms [i].xPos, rooms [i].yPos);
InstantiateFromArray (enemyTiles, (room$$anonymous$$iddle.x + roomPosition.x), (room$$anonymous$$iddle.y + roomPosition.y));
}
}
Thanks again for the help :)
(Side note after figuring out a better way to instantiate the enemies I don't even need the List function but I'll be using the list method for other implementations so it was still helpful)
Im glad I could help and if any more help is needed feel free to comment again :D
Your answer
Follow this Question
Related Questions
Choose random prefabs from list? 1 Answer
Random select from array and spawn 1 Answer
How do I Photon Instantiate a scene object randomly from a list of objects? 0 Answers
script not working pls solve it 1 Answer
Random 2D tile engine question 0 Answers