I've already searched for answers but I was not able to figure out the best approach for handling expensive functions/calculations.
In my current game (a 2d tile-based city building) the user is able to place buildings, build roads etc. All of the buildings need a connection to a junction the user has to place at the border of the map. If a building is not connected to this junction, a "Not connected to road" sign will popup above the affected building (otherwise it has to be removed). Most of the buildings have a radius and might be related to each others as well (e.g. a fire department can help all houses within a radius of 30 tiles). That is what I also need to update/check when the road connection changes.
Yesterday I ran into a big performance issue. Lets have a look at the following scenario:
A user can of course also erase buildings and roads. So if a user now breaks the connection right after the junction I need to update many buildings at the same time. I think one of the first advice would be to avoid nested loops (which definitely is a big reason in this scenario) but I have to check...
if a building is still connected to the junction in case that a road tile has been removed (I do that only for affected buildings by that road). (Might be a smaller issue in this scenario)
(Asked separately as Efficiently determining which buildings are connected by roads)
the list of radius tiles and get buildings within radius (nested loops - big issue!).
// Go through all buildings affected by erasing this road tile. foreach(var affectedBuilding in affectedBuildings) { // Get buildings within radius. foreach(var radiusTile in affectedBuilding.RadiusTiles) { // Get all buildings on Map within this radius (which is technially another foreach). var buildingsInRadius = TileMap.Buildings.Where(b => b.TileIndex == radiusTile.TileIndex); // Do stuff. } }
This all breaks down my FPS from 60 to almost 10 for one second.
So would could I do. My ideas would be:
- Not using the main thread (Update function) for this one but another thread. I might run into problems of locking when I start using multithreading.
- Using a queue to handle a lot of calculations (what would be the best approach in this case?)
- Keep more information in my objects (buildings) to avoid more calculations (e.g. buildings in radius).
Using the last approach I could remove one nesting in form this foreach instead:
// Go through all buildings affected by erasing this road tile.
foreach(var affectedBuilding in affectedBuildings) {
// Go through buildings within radius.
foreach(var buildingInRadius in affectedBuilding.BuildingsInRadius) {
// Do stuff.
}
}
But I don't know if this is enough. Games like Cities Skylines have to handle way much more buildings if the player has a big map. How do they handle those things?! There might be a updating queue since not all the buildings do update at the same time.
I'm looking forward to your ideas and comments!
Thanks a lot!