Interesting dilemma with chunk loading and object pooling...

So I have this topdown 2D game I’m creating with a large overworld, covered with trees, rocks, bushes, etc… which the player can destroy for resources. (think Forager, Stardew Valley, etc)

So the things I had to tackle were object pooling, since there are hundreds of prefabs needing to load in and out as the player moves around… and of course I also need chunk loading to deal with segmenting the overworld into pieces that a computer can actually handle.

I have implemented chunk loading, and I also implemented object pooling… and while everything works, there’s one nagging issue that’s been bothering me… and that’s when you reach the “edge” of a chunk and step into the next one, it’s needing to load 3 new chunks and unload 3 old chunks. That’s cool, but this is taking upwards of 40-50ms and causes a noticeable stutter when moving around between chunks.

At first I was enabling/disabling objects which I noticed in the profiler was causing considerable slowdown… SetActive takes a lot of resources… so I figured, okay, I’ll just set the object’s transform to something way off screen and add it back to the pool, so it can just get assigned a new position when I take it from the pool.

This works well, and considerably improved performance… but as I stated earlier… it’s still at 40-50ms when moving to new chunks… and that is because of… SetParent. See, my whole chunk loading system works off the idea that I place 9 “chunk” objects in my scene in a grid, and when I need to load in bushes, trees, rocks from the pool, they get parented to their respective chunk. The reason being that it’s super easy to save and load chunks this way since I can just save the hierarchy of the chunk. So if I want to save everything in chunk X/Y, I would just save all of the data inside the chunk (all of the prefab objects) and when I want to load, I just load it all in. This is a really simple way of making a chunk loader… but the problem is all of the objects need to be parented to their chunks in order to save/load.

The problem is SetParent is taking the biggest hit to my performance… but I don’t see a solution to this. I mean I could assign some tag or variable to each object to associate it with a specific “chunk” but then I would need to loop through all objects and compare that variable to save them to their proper chunk file… which I’m not sure is going to be a faster solution.

Take note that I’m saving chunks as they unload, since I want to save any changes the player made.

So how would I manage to load/unload these objects without parenting them to a chunk, but while keeping them “assigned” to a particular chunk? If I can avoid parenting all together, it’s going to significantly cut down on the performance hit. What other method of chunk loading could I possibly do which would be more efficient and not involve parenting?

I ended up figuring out a solution. My issue was that I was getting CPU spikes due to loading 9 chunks of objects on the same tick. Since the game technically only needs to concern itself with the single chunk the player is in (for saving changes the player makes to objects) then I full load only that chunk when it’s entered. For the 8 surrounding chunks I now do a time offset parent method using a coroutine,which I currently have set to 0.2-0.4 seconds random range. This means objects far away are not all trying to parent themselves to their chunk at the same time, and instead it disperses the parenting across 0.4 seconds of time.

Since I don’t plan to have the player launching through a full chunk within 0.4 seconds, this shouldn’t be an issue when it comes to interacting with these “ghost” objects… since the player will never reach a ghost chunk before everything is parented.

End result is that I went from 40-50ms spikes down to 8-11ms spikes, which is almost completely unnoticeable to the naked eye when running around the world.

I think I can further reduce this down by optimizing the prefab objects better. :slight_smile:

Oh and I also set up a check to make sure the chunk parent object isn’t null when prefabs try to parent themselves 0.4 seconds later (as the player could quickly go back to the previous chunk, causing objects to be left hanging with no chunk to parent to) So all I do is if the parent is now null I just reset the position of that prefab and return it to the object pool.

I think the only thing that might help future proof this is to tie the parenting random range to the speed of the player’s movement… So if the player somehow gets a powerup or rides a horse or something to move really fast, I can adjust the parent delay to be sooner… of course that would result in CPU spikes as it’s tightening the range of all the parenting objects… but it’s better than stuff breaking.

2 Likes