- Home /
Mobile performance tips
Hello,
I am trying to make RTS/TD game for mobile platforms and I would be glad, if you could share some performance optimization tips with me.
I made simple scene with A* navigation plugin. I used blender plane (4000 tris) as terrain. Than I spawn simple rigged and animated humanoid characters (800 tris), that move towards their goal and every bit of time check for near enemies. Shaders are all mobile difuse. no shadows, no point/spot lights.
When there is 50 or more of such moving characters on the screen, the FPS drops on my Galaxy S3 (25-30 FPS), which is fairly fast device. I can not find the bottle neck. When I use animated characters without AI script, the game runs well. When I use cubes instead of characters with AI, the game also runs well. But together it is quite slow.
Is there something, I could do to improve the performance even, when there is 50 or more characters in the scene? Is it possible to batch them some way? I am afraid, that when I add more details to the scene, slower mobile devices won't handle it.
Pseudo AI script is here:
private void makeDecision() {
//nothing to do, find tower or base to attack
if(currentState == STATE.IDLE) {
move();
findTarget();
}
if(currentState == STATE.MOVING_TO_TOWER) {
move();
}
move();
StartCoroutine(findTower());
}
if(currentState == STATE.ATTACKING_TOWER) {
if(!base.target) {
currentState = STATE.IDLE;
return;
}
RotateTowards (base.target.parent.position - transform.position);
StartCoroutine(attack());
}
if(currentState == STATE.MOVING_TO_FRIEND) {
if(!base.target) {
currentState = STATE.IDLE;
return;
}
if(base.target && Vector3.Distance(transform.position, base.target.position) <= transform.GetComponent<CapsuleCollider>().radius/2 + base.target.GetComponent<CapsuleCollider>().radius/2) {
currentState = STATE.ATTACKING_FRIEND;
} else {
move();
if(Time.time - lastRepathTime > 0.3F) {
doRepath();
lastRepathTime = Time.time;
}
}
}
if(currentState == STATE.ATTACKING_FRIEND) {
if(!base.target) {
currentState = STATE.IDLE;
return;
}
RotateTowards (base.target.position - transform.position);
if(base.target && Vector3.Distance(transform.position, base.target.position) <= transform.GetComponent<CapsuleCollider>().radius/2 + base.target.GetComponent<CapsuleCollider>().radius/2) {
currentState = STATE.ATTACKING_FRIEND;
}
StartCoroutine("attack");
if(base.target && Vector3.Distance(transform.position, base.target.position) > transform.GetComponent<CapsuleCollider>().radius/2 + base.target.GetComponent<CapsuleCollider>().radius/2) {
currentState = STATE.IDLE;
}
}
}
Screenshot of the scene is also included (it's from editor, so do not mind the FPS value).
Answer by maddFrogg · Apr 25, 2014 at 08:52 AM
Take a look at this: http://www.gamasutra.com/blogs/AmirFassihi/20130828/199134/0__60_fps_in_14_days_What_we_learned_trying_to_optimize_our_game_using_Unity3D.php
I learnt some new things, I hope you do aswell.
Thank you. There is a lot of interesting info there, however I fulfill the most of it. I was wondering from the beginning, if there is a trick to batch rigged animated character, but I can not find anything. Is it even possible?
Answer by socialspiel · Apr 25, 2014 at 09:11 AM
We made the experience, that not the tris are what limits you, but the draw calls. We optimised our character meshes so that they consist of only one mesh (1 drawcall per mesh and material) and so we got the draw calls down from 100 per character to 4 and where suddenly able to display 180+ of them.
Use the "Stats" button in the "Game" window and take a look at the draw calls and try to figure out the draw call limit for your device.
I estimated them by spawning one enemy after another until i noticed a significant fps drop. when you spawn the same amount of enemies in the editor you get an approximate draw call limit.
I thought about that. $$anonymous$$y characters actually are just one mesh with one material. Every one takes only 1 draw call. They are holding weapons, but these could be dynamically batched, so they are not the problem. What about the animation? Could be a humanoid rig with 22 bones the main problem?
Answer by zugsoft · Apr 25, 2014 at 10:35 AM
You have 44'000 poly with 50xAI, it's normal to have some bad performance. 1/Limit the number of enemies, 50 it's too much 2/Reduce again the Poly if possible. 3/Detect the distance between you and the enemies, and show/hide the enemies
4/Reduce the AI speed loop by 10 for example 5/Limit the number of AI at the same time, if your enemies are a object in a table, you can add a property int AIEnabled=0; and check if your enemy has a AIEnabled or not.
//Add a property to your enemy object
int AIEnabled=0;
//Declare counter as public variable
int counter = 0;
int slowBy=10;
//In your AI loop
counter++;
if(counter==slowBy){
For each enemies>0{
if(enemy.AIEnabled==0 && enemy.live>0){
//Calculate AI
}else{
enemy.AIEnabled--;
}
}
counter=0;
}
I have something similar in my AI script. After each decision I set timer variable to current time and when the difference between current time and time variable is smaller than certain treshold I just skip that round. For example in finding new target:
private void findTarget() {
if(Time.time - findTargetStartTime < 0.3F)
return;
findTargetStartTime = Time.time;
StartCoroutine(findTower());
}
What performance impact could have an animation? $$anonymous$$ecanim humanoid RIG with 22 bones and mocap animation (1 frame = 1 keyframe with all the bones animated)?
I don't think your problem is animation, the poly count is the same, physics are the same. Polygon impact GPU Physics impact CPU AI impact CPU
But you can try to delete the animation to see the improvement.
findTargetStartTime is global or a enemy proprety ? Did you try a random number(0.2f to 0.5f) ins$$anonymous$$d of 0.3F ?
findTargetStartTime is enemy private property and threshold is still 0.3f. should it be random? I think, that I see small improvement (sometimes +10 fps improvement), when I turn off animations, but I have just my device to test it. The animation files are quite big (300 - 400 kB), because of every frame animated. No extrapolation. Could it be the reason?
I don't know if you use a Thread or not for each enemy AI, but if your loop is under the same Thread, and your threshold is 0.3f, your AI is launch at the same time for your 50 enemies.
I made a 2D game for iOS with 40 enemies, so the GPU is not the problem. The enemies pathFinding is under the update method, so only 1 Thread. I must launch my pathFinding with a random threshold, and with a maximum time, else I had 3 frames per seconds.
The best solution is different for each Game, you must find where is your problem. 1 / CPU or GPU ? Reduce the polygon to 50 to see the impact(if only 1-2fps it's not a GPU problem) Increase your threshold to 0.6f Use a random threshold Use Thread Reduce enemies.
I forgot about profiler (editor profiler, not mobile) :D Alright...it says, that AI takes about 30% of frame time, animator.update and camera.render 20% each. Reducing tne enemy count is the last option. TD game would be boring, if there would be to few enemies. I could use also your advice about random threshold.