Sunday, July 22, 2018

Unity starter tips - performance.

"Uh oh. It's another one of those starter tips thing from people that thing they know game engines."
Yeah, it's one of those things. Things that I run into my self, and had to hunt them down all over the place when my projects had issues. Of course this is by no means the be-all-end-all list. But it is a start!

1) Remember to set up your project correctly in the Player Settings, set up skinning and batching depending on what you need (Leave batching on of course...), what to log...

2) Know your target platform. Not all platforms are created equal. You won't put the same effects (or the same quality effects) in PC and mobile. Mobile will just get single digit fps, if it will even work in the first place.

3) Set up your physics layers! If you have a physics layer for your player character and you know for sure that the character will never touch or interact in any way with objects belonging to a certain physics layer, disable their interaction in the physics matrix. There is no need to let the engine try to figure out if these two are touching since you know that they won't.

4) Bake stuff in maps! Even if you are targeting DirectX12/Vulkan with all the eye-candy possible, why waste cycles with having dynamic effects enabled for something that you know is going to be static? Bake those shadows and that occlusion. You'll take a tiny bit more to set them up, but you'll save on resources that you'll be able to use elsewhere and the people playing your game won't complain about fps drops.

5) Batch stuff! And Ι'm not talking only about dynamic batching because if abused, it may end up costing frames instead of saving. If you are making a detailed environment in a 3D modeling package, when it's time to export stuff, export all the stationary objects as one mesh, so when they go over in the real time engine then they will be treated as a single mesh.

6) Texture atlas. Continuing from #5, combine your textures for that scene into one big one, so when the engine goes to load the textures it won't go like "stop rendering, read from disk, load into memory" for every single texture. Sure you can't combine eeeeverything, especially the textures for character because if those characters are used in a different scene, then the whole texture will be loaded, even the pieces that aren't used.

7) When making a new script, always remove that Update function if it won't be used. If it's left there in the script, then the engine will still go in the loop every frame to see what it should do in it. It won't skip a function just because it's empty.

8) Comparing Tags. gameObject.CompareTag("thing") is faster than gameObject.tag=="thing".
So if trying to see if this is the right tag, use CompareTag. It is a native Unity function, compiles to fewer calls, it has less of an overhead... long story short, it uses less stuff and things add up.

9) Pool objects. It's really tempting to simply have a call that creates/spawns an enemy from a script. You just write it and when the game is about to use the enemy, spawn it. Well... that introduces hiccups when the entity is spawned, especially if there are alot of stuff going on with it. And if you have lots spawning; as we said already - things add up. Have a pool of enemies ready, just disabled or hidden out of sight, and when needed teleport the enemy where it's needed and have a boolean for that enemy that he is awake. When destroyed/killed, simply hide them again and set the boolean that they are free to be used again later on. This is really especially useful for example when dealing with waves of enemy ships. When they blow up, don't Destroy() the object, simply hide them.

10) Try to use Coroutines instead of Update functions in scripts. As we said in #7, Update is always checked even if it won't do anything. One of the first ways people just starting unity will use to try and control objects, is Update. At first, my self included at first, people will put a boolean in an IF check, and if true the object will do something. That hogs the main execution thread of the project. So use Coroutines instead. What are Coroutines? They are small routines that can be spun off into their own thing, running along the main thread, but unlike Update, they don't stay there looping and never letting the main thread continue. Coroutines can be called when needed, paused and delayed and when done they stop and go away. Useful for timers and controlling stuff!