First off, I was not sure whether to put this question here or in the Game Design forum. But this is dealing with more of the technical side of a design so I’m posting it here
Summary
So essentially I am looking to create a system where the world is split up into cells. By cells I mean a square in the world that as the player approaches or retreats it decides if the non-static items (NPCs, Items, etc) need to be spawned. The purpose of this is cut on the memory needed while the player is in the open world.
In-Depth Concept
As the player wonders the world I would like the system to drop loaded cells (Destroy all nonstatic gameobjects for within those cells) when the player is X cells away and load the new cells (Instantiate and initialize objects in those cells) within X distance. The game this will be used in is a very large game so I am trying to avoid keeping everything in memory at once, and I’m also trying to have this run as the player plays so they do not notice the world around them spawning and despawning. With that being said, I know that Instantiate and Destroy are very costly functions so I would like to try to avoid these so there are not large lag spikes in the game as the system loads in the new objects and removes the old ones.
My thoughts were to have the cells be four Vector3 points that represents the four corners of the “cell square” (boundaries). As the game initially loads the cell system will spawn the cell the player is standing in, plus the cells on each side including the corners. As the player transitions from each cell, any cell >1 cells away will be despawn and any cell <2 cells away that is not already spawned will spawn in.
The cells with have a constant size, equal on both sides.
Despawning will take all objects within those X and Z coordinates and save their data to a file then destroy the GameObjects. Spawn will load the new cells file and Instantiate the objects and load their data into them accordingly.
Meat of the subject
Is this an efficient way of achieving this system on a large scale without causing large lag spikes for the player? If not, is there a better way to do this? What are your ideas, thoughts and comments?
P.S.
I am not asking anyone to code this for me, I’m simply inquiring about the best and most efficient way to design this system.
Well, in most systems, you exchange one evil for the other. In this case, by reducing memory footprint you’ll increase runtime cost to instantiate and destroy objects.
This is the first thing you have to tackle. I dont think instantiating and destroying alot of gameobjects every time a cell is changed does your games performance any good. The better option would be to just push those objects to a pool/buffer from which the next cell to be loaded can fetch objects again. This way you dont have to instantiate much new objects and technically never have to destroy a single one. What remains is initalisation.
As of your cells, consider geometrical hashing. Just put all your cells into a flat array which represents a grid of cells. Find out the player position by applying a hash which converts player coordinates into array indices. See if it changed, if so, unload/load new cells. Represent the cells itself by centerpoint and extends.
So in a large RPG setting where cells may contain none, or many of a single type of item, wouldn’t pooling items just eventually lead back to the memory problem? Because players would travel around the world and I would have to instantiate items to make up for the lack of number of items in the pool and then leaving that cell now means I have X amount more of that item type in the pool that may or may not be used. Or maybe I’m not understanding how the pooling works.
To clear this up, im talking about a global pool for all cells. As I see it you are talking about a pool for each single cell?
Well, of course you have to increase the pool if the objects contained there are not enough. Even if they remain unused there, its still way better than a) loading all objects of all cells and b) instantiating/destroying objects all over again. Considering you will always use/reuse the same objects, itll be way more performant. You still have the option to dynamically reduce the pooled objects as soon as you find them being unused. But yea, the general idea is to have those objects available when you need them rather then instantiating them again and again.
Here are some things I’ve found implementing something similar:
You can annotate your objects with a value that indicates how common they are. If there’s a unique object that only occurs in one cell, you can expunge it from the pool when you’re a sufficient number of cells away. If it’s a very common item like a bush, you might never expunge it.
Try to do the absolute minimum of work in Awake, and implement Start as a coroutine so you can distribute its work over multiple frames. Also implement your pool objects’ spawn initialization method as a coroutine. This spreads out the CPU load to prevent stutters when spawning objects into the scene.
Depending on how big the cells are and how far the player can see, you may need to load more than 1 cell out from the player’s current cell. Smaller cells are usually better. If your cells are really big and contain a lot of objects, you could get stutter when the player crosses a cell boundary.