How to find the active random object in an object pool array
Hi there guys.
I have a bit of a problem with a script I use to generate an endless levels. The original script uses instantiation to add the new level as the player moves in the Y direction (up). But the object being instantiated does too much heavy lifting, and I get fragmentation when a new part of the level or a new "room" is instantiated, which is why I would like to pool the prefab objects and just set them active as needed.
This is my original level generation script:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class RoomGenScript : MonoBehaviour {
public GameObject [] availableRooms;
public List<GameObject> currentRooms;
private float screenHeightInPoints;
// Use this for initialization
void Start () {
float width = 1f * Camera.main.orthographicSize;
screenHeightInPoints = width*Camera.main.orthographicSize / 64f;
}
// Update is called once per frame
void Update () {
}
void FixedUpdate()
{
GenerateRoomIfRequired ();
}
void AddRoom(float farthestRoomEndY)
{
int randomRoomIndex = Random.Range (0, availableRooms.Length);
GameObject room = (GameObject)Instantiate (availableRooms [randomRoomIndex]);
float roomHeight = room.transform.FindChild ("floor").localScale.y;
float roomCenter = farthestRoomEndY + roomHeight * -0.678f;
room.transform.position = new Vector3 ( 0, roomCenter, 0);
currentRooms.Add (room);
}
void GenerateRoomIfRequired()
{
List<GameObject> roomsToRemove = new List<GameObject> ();
bool addRooms = true;
float playerY = transform.position.y;
float removeRoomY = playerY - screenHeightInPoints;
float addRoomY = playerY + screenHeightInPoints;
float farthestRoomEndY = 0;
foreach (var room in currentRooms) {
float roomHeight = room.transform.FindChild ("floor").localScale.y;
float roomStartY = room.transform.position.y - (roomHeight * 0.1f);
float roomEndY = roomStartY + roomHeight;
if (roomStartY > addRoomY)
addRooms = false;
if (roomEndY < removeRoomY * 1f)
roomsToRemove.Add (room);
farthestRoomEndY = Mathf.Max (farthestRoomEndY, roomEndY);
}
foreach (var room in roomsToRemove) {
currentRooms.Remove (room);
Destroy (room);
}
if (addRooms)
AddRoom (farthestRoomEndY);
}
}
So I've tried my hand at pooling the objects and then just setting them active, but I'm missing something and just not sure exactly how to go about it. In my modified script, I've instantiated the objects in the start menu and then gotten rid of the instantiation in my "AddRoom" function, and set a random object from the array to active. Now Im not sure how to add the random active "room" to my "currentRooms" List in the "AddRooms" function.
This is the modified script:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class RoomGenScript : MonoBehaviour {
public GameObject [] availableRooms = new GameObject[4];
public List<GameObject> currentRooms;
private float screenHeightInPoints;
public GameObject room1;
public GameObject room2;
public GameObject room3;
public GameObject room4;
// Use this for initialization
public void Start () {
float width = 1f * Camera.main.orthographicSize;
screenHeightInPoints = width*Camera.main.orthographicSize / 64f;
availableRooms [0] = (GameObject)Instantiate (room1);
availableRooms [1] = (GameObject)Instantiate (room2);
availableRooms [2] = (GameObject)Instantiate (room3);
availableRooms [3] = (GameObject)Instantiate (room4);
room1.SetActive (false);
room2.SetActive (false);
room3.SetActive (false);
room4.SetActive (false);
}
// Update is called once per frame
void Update () {
}
void FixedUpdate()
{
GenerateRoomIfRequired ();
}
void AddRoom(float farthestRoomEndY)
{
int randomRoomIndex = Random.Range (0, availableRooms.Length);
availableRooms [availableRooms.Length].SetActive (true);
float roomHeight = transform.FindChild ("floor").localScale.y;
float roomCenter = farthestRoomEndY + roomHeight * -0.678f;
transform.position = new Vector3 ( 0, roomCenter, 0);
currentRooms.Add (//What do I add in here?);
}
void GenerateRoomIfRequired()
{
List<GameObject> roomsToRemove = new List<GameObject> ();
bool addRooms = true;
float playerY = transform.position.y;
float removeRoomY = playerY - screenHeightInPoints;
float addRoomY = playerY + screenHeightInPoints;
float farthestRoomEndY = 0;
foreach (var room in currentRooms) {
float roomHeight = room.transform.FindChild ("floor").localScale.y;
float roomStartY = room.transform.position.y - (roomHeight * 0.1f);
float roomEndY = roomStartY + roomHeight;
if (roomStartY > addRoomY)
addRooms = false;
if (roomEndY < removeRoomY * 1f)
roomsToRemove.Add (room);
farthestRoomEndY = Mathf.Max (farthestRoomEndY, roomEndY);
}
foreach (var room in roomsToRemove) {
currentRooms.Remove (room);
Destroy (room);
}
if (addRooms)
AddRoom (farthestRoomEndY);
}
}
I feel like I'm looking at this the wrong way. Any guidance would be greatly appreciated. Thank you in advance.
There is so much optimization you could do it hurts my eyes :) Calling transform.FindChild is not recommended to do inside a loop. Also move the roomsToRemove list creation outside the loop. You could just create it on start and then clear it at the end of the loop as there is no need to create a new list every loop.
Haha thanks! Always helps to have a pair of more fresh and experienced eyes contribute. Which other way could I use to get the size of that game object besides transform.FindChild or transform.Find
Answer by Anton-Korhonen · Nov 01, 2016 at 04:09 PM
Just access the gameobject from the availableRooms list with the randomRoomIndex you generated:
currentRooms.Add(availableRooms[randomRoomIndex]);
Did you mean to set the room with the index randomRoomIndex active? Like this:
availableRooms[randomRoomIndex].SetActive (true);
Hi StickTick, thanks for the reply.
Yes I did mean to set the room that way, lol I overlooked that, thanks. Your solution has helped, but what I need to add to "currentRooms" is the room that is currently active in the scene, wouldn't currentRooms.Add(availableRooms[randomRoomIndex]); just add any random room from "availableRooms", even though they are not active in the scene?
Yes it does. I thought that's what you wanted at first, but now if i understand correctly you want the room with the farthestRoomEndY position?
Sorry for not clearing that up, yes that's what I want. The lowest room in Y direction.
I overlooked your comment. currentRooms.Add(availableRooms[randomRoomIndex]) adds the same room to the array that you set active. randomRoomIndex is just an integer and doesn't call Random.Range again.
@Joorst That means that the script reaches the AddRoom function without going through if(farthestRoomEndY < roomEndY). I think that not expected behaviour? At that point farthestRoomEndY hasn't been modified and it equals zero.
You talked about "the lowest room in Y direction". Does this mean that farthestRoomEndY should be the room with the lowest Y value? If that's the case, then you should reverse the if(farthestRoomEndY < roomEndY) statement. The word "farthest" makes it a little bit unclear, because farthest could be farthest up or down.
Can't seem to get it to work, the error stays. The most clear definition of what i would like is:
In the original script, the GameObject room was instantiated (line 30 of the original script), once that gameobject gets instantiated it would be active in the scene and would be added to the List "currentRooms".
What I need is just to replace the instantiate with setactive. Which ever object is active in the scene (room1, room2, room3 or room4), needs to then be added to "currentRooms". Ive tried currentRooms.Add (availableRooms[randomRoomIndex].activeInHierarchy)
, but that didn't work.
This is the thing that i don't understand. If you're setting the gameobject availableRooms[randomRoomIndex] active, then don't you want the exact same gameobject added to the list?
These lines of code choose a random room from the availableRooms array and set it active:
int randomRoomIndex = Random.Range (0, availableRooms.Length);
availableRooms [randomRoomIndex].SetActive (true);
This line (in my original answer) adds the exact same room to the currentRooms list that we just set active
currentRooms.Add(availableRooms[randomRoomIndex]);
It does exactly the same thing as your original AddRoom code. You can ditch the larger modified code i gave you with the GameObject Room argument in the AddRoom function. I thought you needed a reference to a room but you only need those lines of code in my original answer.
randomRoomIndex doesn't get a new value when you refer it at the end of the AddRoom function. I think it's clear:
1. You choose a random room
2. You set that room active
3. You add that room to the currentRooms list
The code above does just that.