Part one of this series covered my walled off experimentation with procedural enemy flanking behaviours. It was fun, interesting work, and at the end I had the beginning of a real solution.
Normally, this is where I’d point out that there is a world of difference between a little test and implementing something in game. One big difference between the test and the real arenas is the lack of complicated geometry. Entities need to use traditional, graph based pathfinding to navigate through the arena, so we need to integrate the force based solution with the graph based pathfinding. In my experience, that’s easier said than done.
However, compared to the nightmares I’ve had to deal with in the past, this was smooth sailing. Partly that was because I already did a lot of the work. For instance, converting a pathfinding direction into a specific place to move is not a new function that I need to write and bug test, as I did that for the riot police. But mostly this was because I figured out the general solution to the problem, detailed in the last article, so it was vastly easier to narrow in on the specific problems of the interfacing between the direction an NPC wants to go, and the specific path they have to follow to get there.
This one change leads instantly to significantly better gameplay. I can and will talk about that at length, but since it ties in with the NMC, I’ll silo that off into its own design focused article.
The technical performance is better than I feared, but worse than I hoped. I stress tested with 256 guys, and was down to about eight frames per second, albeit in the editor. That’s an unrealistic test, but of real concern were the dips into the thirties with a high-normal amount of entities active. That’s not the kind of experience I want for the players.
My experience with Unity is that the framerate in editor is always significantly lower than in a build, which is to be expected, but that it is something of a canary in the coal mine for performance. It’s something to keep an eye on. Luckily, I’ve implemented only the most basic performance optimizations, such as the occasional ring buffer or object pool. There is a ton of optimization juice left to squeeze.













