- Home /
How To Disable Gameobjects When Not In View, Then Enable When In View
I am working on a 2D game that generates a world and then generate foliage on top of the world that can be broken. Since the world is 1000 by 1000 tiles there is a lot of foliage to be spawned. It slows down the frame rate by a lot even with the SpriteRender turned off. If there a performance efficient way to load nearby foliage while disabling ones out of view then your answer will be much appreciated. I have already tried placing each gameobject in a list then checking the distance but that's a lot of objects to process in a foreach loop.
Answer by davidcox70 · Aug 05, 2020 at 08:12 PM
I would probably make my tiles in a 2 dimensional array, which then makes it easy to reference them. I would also keep track of the tiles I had made visible by putting them in a list. Something like:
public class makeSomeTiles : MonoBehaviour
{
private GameObject[,] tiles;
public GameObject tileObject; //drag a base tile object into here in the editor
public int numTilesWide = 100; //the width of world in tiles
public int numTilesHigh = 100; // the height of our world in tiles.
private List<GameObject> activeTiles;
void Start()
{
// create an array big enough to take your entire scene
tiles = new GameObject[numTilesWide, numTilesHigh];
// hide the source original tile
tileObject.SetActive(false);
// we'll use this list later to hold activated tiles
activeTiles = new List<GameObject>();
// run the make tiles routine
makeTiles(numTilesWide, numTilesHigh);
// just activate the ones we want - say 5 x 5 around the tile located at 60,30
Vector2 playerTile = new Vector2(60, 30);
// and do this whenever we detect the player is on a new tile.
activateArea(playerTile, 2);
}
void makeTiles(int width, int height)
{
// count through the rows and columns
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// create copies of the base tile template
tiles[x, y] = GameObject.Instantiate(tileObject);
// name the tile, just because :-)
tiles[x, y].name = "Tile #" + ((y * width) + x).ToString();
//position the tile
tiles[x, y].transform.position = new Vector2(x, y);
}
}
}
void activateArea(Vector2 centerTile, int tileRange)
{
//clear the old active tiles (if any)
foreach (GameObject tileRef in activeTiles)
{
tileRef.SetActive(false);
}
//clear the active tiles list
activeTiles.Clear();
// activate the tiles around the center tile
for (int x = -tileRange; x <= tileRange; x++)
{
for (int y = -tileRange; y <= tileRange; y++)
{
// offset to center tile
Vector2 tileToActivate = centerTile + new Vector2(x, y);
// limit the output, because we'll get an error if we are at the edge of the world
int xCoordinate = (int)Mathf.Clamp(tileToActivate.x, 0, numTilesWide - 1);
int yCoordinate = (int)Mathf.Clamp(tileToActivate.y, 0, numTilesHigh - 1);
// make the tile visible
tiles[xCoordinate, yCoordinate].SetActive(true);
// add the tile to a list which we can use to hide the tile later.
activeTiles.Add(tiles[xCoordinate, yCoordinate]);
}
}
}
}
This script makes all the tiles at the start. You might prefer to make them as the user approaches. It depends how long you want to wait for startup.
On the world size that i wanted 1000 by 1000(1,000,000 tiles) it was running at 3 frames but with your amazing system that have everything i needed it was running at 200 frames. Thank you for helping me make my game run faster
Answer by Ymrasu · Aug 05, 2020 at 05:38 AM
MonoBehaviour objects have some messages that you could use: OnBecameVisible() and OnBecameInvisible(). So you can add something like this to a script:
void OnBecameVisible()
{
enabled = true;
}
void OnBecameInvisible()
{
enabled = false;
}
The problem is that it counts the editor Scene camera too. So it could throw you off while testing.
Each plant doesn't have a mono behavior script as that would be a lot of mono behaviors. I looked in the profiler and can see that rendering the sprites is causing the frame drops
It would just be one small script that you could attach as a component to anything you want to enable and disable itself when in and out of view. I think it would help more than it would hurt performance.
I ran a test by generate a 200 by 200 tile world with the same seed. The first test had the mono behavior on each plant and was averaging around 370fps. The second test took off the mono behavior on each plant and was averaging around 430fps. Also the frames are high right now only because the world is really small.
Your answer
Follow this Question
Related Questions
slow rendering and frozen frames in unity 2019.4.16f 3 Answers
Do non-rendered gameobjects use memory? 1 Answer
Is there a way to disable the IK Manager on playtime without impairing the animation? 0 Answers
gfx.waitforpresent took up to 40ms even without any gameobject running 0 Answers
Android & IOS Performance Difference 1 Answer