One of our design decisions with Trespassers was that we wanted lots of enemies at the same time. Not smart, but you should be always outnumbered. Thus, that meant that we had to make the most out of the resources available, and we are targetting mid-range desktop PCs. In fact, we have tested in an almost 4 years old Intel Core 2 Duo T6600, GeForce 310M, 4GB DDR3 notebook from Samsung, and, although not perfectly smooth as we would like, it is playable and runs with around 600(+) enemies alive in the main map. By the way, that laptop was crap, we cannot recommend you Samsung’s notebooks from our experience.
So, how did we manage to get so many enemies at once in the screen? First let me introduce the enemies in the city view:
Those tiny green dots are the enemies. They are human sized, and represent the majority of enemies that the game has to manage at that level. Every one moves following its -quite simple- AI, and when the player is near, they try to attack him. No luck if the player is inside a tank, but that’s another story. Our problem is that we cannot be executing an AI at 60 FPS for something so almost useless and numerous as those dots, but we have to bring life to them. And of course, those enemies are respawning to fill the streets to keep the city alive.
We have two, differentiated by their approach:
- Reduce the CPU usage (if you are programmer, you fall into that first)
- Improve the CPU usage (whatever who you are, you will love and hate this one)
Reducing CPU usage was the way to go, really. Why do you need to keep so many enemies active? Or their spawning points? We added a small script to every enemy or entity we wanted that, every few seconds, checks the distance to the player. In the current version, the game is played with both players maneuvering the weapons and steering of the same vehicle (could be changed). So what we had was a big amount of enemies inactive, with few of them, the ones closer to the player, active. As this was checked every 1 – 1.5 seconds, you could easily extrapolate: from 800 checks per frame, to 800/60 ~ 13 checks per frame. The ~780 saved were spent in the enemies near the players, so there is always action around, and action waiting to happen. We can easily have around 13-15 simple checks per frame, and around 70-80 (even more) active enemies without compromising CPU usage too much. Our script just allowed for some components to be enabled/disabled when needed, saving CPU this way. This old trick was used in some other parts of the code, specially for saving some Physics checks (enemies jumping mainly).
UPDATE: As the city needed to be bigger than initially expected, we ended up using areas separated by IDs. We only checked distance on those elements that were located in the same area. It may be even easier, as it depends on the position of the player, that is translated to an area ID, instead of checking distance for every object that needed to.
Improving CPU usage is quite … deceiving by its name. But works! Our problem was that the enemies’ AI couldn’t be executed for so many at once, but, as Dartacan, why don’t we execute “one for all”? Or a few, at least ;). As our enemies are so tiny and they usually appear in groups that follow the player, the player wouldn’t notice if he is confronting three different enemies close together or one that represents three. So, with one GameObject, we grouped several sprites, which didn’t hurt the CPU. Just rise the health, and you are done. The pic on the left illustrates the concept.
Of course the latter solution doesn’t look so good when the enemy is on his own, but in large groups players are not likely to notice.
We will collect more tips regarding performance, as we think that this may help others in their way.