- Home /
Optimizing a massive array of GameObjects pls help
Hi! So this is the first time I've ever really started a unity project with the intent to finish it, and so what my plan is is to make a game similar to Factorio. I have a 10,000 by 10,000 array to hold values for the map (It's an array of instances of a class called Tile, with IDs and other info) This works great, and I've got some nice ore generation going. It's pretty fast, too. However, the problem is when I go to instantiate images where the Tiles are. There can be many millions of tiles generated in the world, and so iterating through a List of the coordinates of every tile on startup is impractical. At this point, I don't know how Unity would handle that many GameObjects in a scene, even if they're not technically rendered. I was thinking of instantiating new GameObjects when the camera moves, and destroying the GameObjects when they are out of view, but this seems to cause gameplay lag. Can anyone help me with a solution?
If I understood correctly, you are instantiating the Image class in the startup, if so, I'd suggest to already do this in the editor and then just set the correct sprite during the initialization.
And also, why not just enable/disable ins$$anonymous$$d of creating/destroying?
And maybe, just initialize your tiles as the camera gets close ins$$anonymous$$d of everything at once.
What I'm currently doing is instantiating a prefab of a Sprite over and over for every filled Tile in the word. For example, if the world were 100x100, and it had a little 3x3 square of stone in it, in the Tile[,], most of the values would be null, except for a few with a Tile ID that corresponds to stone. When generating these values, I store the coordinates of the Tiles in a List, then make a foreach loop that goes through every position of every tile, reads the ID, and instantiates a prefab of the correct sprite. This is probably a really bad way of doing it, but I'm fairly new to this so it's the only way I could come up with
I would do it this way: (but I think it’ll only work for maps of fixed size)
- Setup your sprites right in the editor and reference them in your tile matrix
- Add some fields in your main script so you can have the equivalent sprite for every tile ID
- Use this method:
- When this method happens (make sure to use a Boolean here so you only do this once per game), check the tile ID and apply the correct sprite by searching the script fields you added in step two.
You’ll need a new script in each Sprite for the OnBecameVisible method to work
About step two, you can also use a ScriptableObject
For big maps like this the more you instantiate the more memory you are going to use even if the objects are inactive. An advanced strategy could be using a pool of each tile and moving them into place as they become visible and actually holding all their info internally in variables, so they are just visual GameObjects.
In my opinion, the easiest way would be to ise a 2d array for your map that corresponds to coordinated in space so that you can access a part of the array using the players position and using a distance modifier to walk up and down the 2d array from the players position in it. Enabling/disabling or whatever you need to do as you walk the array. This way if your array is 10k x 10k and your player is accessing section 3544x6942 and your distance is 50, you are only iterating 2500 objects. Something like this.
public void UpdatePosition(Vector3 position, int distance)
{
int x = (int)position.x;
int y = (int)position.y;
for (int _x = x; _x < x + distance; _x++)
for (int _y = y; _y < y + distance; _y++)
objectGrid(_x,_y).DoSomething();
for (int _x = x-1; _x < x - distance; _x--)
for (int _y = y-1; _y < y- distance; _y--)
objectGrid(_x,_y).DoSomething();
}
Don't use seperate GameObjects to display each tile. Use a number of meshes that you generate via code, with one quad per tile where you assign the correct UVs for a tilemap.
Answer by metalted · Apr 04, 2019 at 05:30 AM
So yeah handling 100.000.000 objects can be a bit demanding. In this case I would setup a couple systems to not handle everything all the time.
Because you don't want to loop through an enormous array, you need to implement something like chunks. The chunks minecraft uses are a great example. When the player is a certain distance away, load the chunk. Or when you preload a lot of stuff before the game, set the chunk active. This will greatly reduce the complexity because you are handling 10.000 chunks instead of 100M objects. Those can be stored in a 100x100 array. That's a bit more manageable already.
So to access a tile now, you need to write a function that converts the id of the clicked tile to a chunk/tile id. So something like: clicked ID (155, 122). Mathf.FloorToInt(155 / width) = 15. 155%width = 5. So you know its the 15th chunk to the right and the 5th tile in the chunk. And ofcourse do the same for the y coordinate.
In case you have a lot of different tile images, you might want to think about categorizing the tiles. So lets say you have 1000 different tiles, you don't want to loop through each time to find a picture for your tile. If you group them to idk roads, trees, buildings and add that identity to your tile, you can get that array to 50 elements, saving some processing time. Because you are dealing with so many object, every improvement has a great impact. Just an idea, maybe using an object is a good way to go. You can access information using the key:value pair. So you don't have to loop but just use object.objectName or object['objectName']. For example: When you store all your images in an object and you scan a tile and get the type (bigTree e.g.) you just get the image by typing myImageObject['bigTree']. Never tried it but might be a good way to go. I apologize in advance for how this message looks, but for some reason I cant add linebreaks or codeblocks or any styling for that matter, so this is it. Hope this gives you some ideas.