Hi,
After asking a couple of times in the Answers site and getting no response I feel like I should try here.
So I’m making an RPG game for mobile devices and I worked out the AI and pathfinding, but there is just one small issue. When the enemies get a lot - 100 or more, I’m getting very low FPS on an iPad 3rd gen. Something like 15 FPS at most.
I narrowed down the problem to the fact that each enemy has a CharacterController attached to it. Here is how I did it:
Created an empty Unity project
I put 200 cubes in the scene, each with a CharacterController
I wrote a 10 line script which told them to follow the player. No pathfinding or anything fancy. Just move the cubes with CharacterController.SimpleMove()
There are no lights and no materials in the scene. Just a ground, 200 cubes + 1 for the player.
Results:
When the enemies (cubes) don’t move, the frame rate is good - 60+ FPS
When the enemies move - 10-15 FPS at most
When enemies move without a CharacterController and using just transform.Translate() - 60+ FPS
I have also tried recalculating the movement direction for each enemy once per second instead of every frame - it made no difference, so the conclusion is clear - CharacterController is the performance sink here.
Since this is a very general example and this problem applies to practically every RPG and RTS (for a mobile device at least), it makes me think that there should be an obvious solution, to this common problem. Or am I approaching it totally wrong by using CharacterController for each enemy?
Please advise!
P.S. The reason I’m using Character Controller is because I want the enemies to be able to block the player and also prevent them from going through walls and bunching together too much.
The main problem here is that you don’t want to be putting a Character Controller on every NPC/Unit. The Character Controller is a special component that can be thought of as acting outside the regular physics system. That is, it is designed to slide along walls etc and not behave in the way regular physics objects do. Therefore, every time you call Controller.Move, you are actually causing a separate collision and detection response step to be taken in the scene. This is fine for a few characters at most but you don’t really want to be using it for all of your units.
You want to try and minimize the physics as much as possible. You will probably be using Nav Agent / Nav Meshes for the navigation of your NPCs so they shouldn’t go navigating into walls anyway. Navigating units this way is pretty efficient as long as you are not constantly re-calculating new paths every frame for all your units (spread operations like this over several frames or even seconds). Nav agents will also automatically try to avoid one another I believe.
To stop you player walking through the NPCs you could try a few things - you would have to test a few things and benchmark them.
You could try making the NPC units Kinematic Rigid Bodies with colliders - Don’t simply add colliders as the update of a static collider is pretty expensive to the physics system.
You could only care about NPCs that are near the player. That is, you could have a pool of unused kinematic-rigidbody-collider game objects that are used. When an NPC gets near the player, temporarily borrow one of these objects from the pool and place it at the NPC position. You could even place triggers on your NPCs so that they are alerted to invoke this process when the player enters their trigger (triggers should be very fast).
There are several variants on step two you could play with but this system the chances are you would only ever have a couple of live collider/rigid body objects in the scene at any time.
Finally, I believe the Nav Mesh system in Unity now understands dynamic objects. I haven’t checked this out yet but you should be able to add a NavMesh Obstacle component to the player so that the NPC nav agents try to avoid him as he moves.
Just a few ideas off the top of my head…hopefully it will get you thinking in the right direction.
Thank you for your answers! Here is where I am so far:
I tried using rigid bodies with capsule colliders. Non-kinematic and moved them by modifying their velocity property. The FPS stayed the same and the enemies started to push each other and slide around like they are standing on ice. Very unpleasant.
I tried with kinematic rigid bodies. The FPS gain is massive but I can’t get the enemies to keep distance from each other instead of bunching to a single point (or line, if they are chasing the player). And again, enemies slide.
For the problem above (just local avoidance or separation) I tried implementing UnitySteer. No success. I don’t know what any of those steering behaviors is supposed to be doing (except for Steer For Target) and I can’t find any documentation or tutorials. It’s an open source library, so no big deal.
Nav Meshes - I don’t have Unity Pro so I can’t use them or the built in Local Avoidance in Unity.
My thoughts so far:
With everything I’ve tried I still got the best results with CharacterController, if I have less than 60-80 enemies. Any more than that and the FPS drops below 20 (on the iPad 3 at least, it’s 30+ FPS on an iPhone 5) and it becomes unpleasant.
@Gaski you gave me some ideas there. I could do something gimmicky with triggers, like borrowing the neighbor’s velocity and turning off the CharacterController for a period of time.
Thinking about the Pro version of A* - I’m currently using it’s pathfinding (it’s awesome and super fast, makes no impact on performance - tested it) but I’m really curious about the Local Avoidance system - if enemies keep a certain distance from each other I can completely remove the CharacterController and my problem is solved, but I’m not sure if it will work.
To summarize it all:
Performance
Pathfinding
Player blocking (player must not be able to escape if surrounded)
Local avoidance
Kinematic Rigid Bodies with A* pathfinding solves the first three problems and I’m left with local avoidance.
So it comes down to: Should I try A* Pro and it’s local avoidance?
Would you recommend any other local avoidance solution?
Sorry for the long post and thank your for the help so far!
Before going to all that effort, have you considered increasing Fixed Timestep? If it defaults to 0.02, maybe you could increase it to 0.04 to cut the Physics workload in half.
I believe Fixed Timestep will have no effect when using a character controller. As I stated in my previous response, the Character Controller has its own update frequency - the frequency at which you call its Move function. As I also stated, you really don’t want to be using the many character controllers in your scene…they are very expensive as each Move function implements a customized collision and response step.
As per my previous post, you probably want to be using a Kinematic Rigidbody with collider for your NPC. The fact that your characters are walking into to each other is a completely separate issue which needs to be solved separately…i.e by using obstacle avoidance code or Unity’s NavAgent component along with nav meshes. Whether A* Pro will give you what you need for this…I couldn’t say never having used it but I suspect so. The point I was making in my previous post was how to more efficiently move that many entities without killing your FPS and still not have the player walking through them. Navigation and avoidance is a separate process and step .
I agree with that. If I could make local avoidance work I would switch kinematic rigid bodies (P.S. - I should move them with transform.Translate(), right?). I’m getting many suggestions to use NavAgents and NavMeshes, but isn’t that Unity Pro only?
If you haven’t already…check out this tutorial project
In particular they show in Chapter 4 - Enemy Setup how to use a Kinematic Rigid Body with Collider and Nav Agents. Sounds like what you are looking for Although I haven’t actually used it yet, I have read on this forum that Unity’s Dynamic Obstacle Avoidance is pretty crappy so you will have to try it out for yourself, The Nav Agent through will certainly get your characters walking around the level and avoiding static geometry in a sensible manner. If you find Unity’s built in Dynamic Obstacle avoidance isn’t good enough you may still want to look into the A* project or one of the others on the Unity Asset store.