Where to start Optimizing - best Practice

Hi my name is Mitja and I’m currently working on my diploma Project which is a 2.5d Sidescroller with a bit of the looks of Playdeads Inside.



The volumetric Light/Fog you can see is achieved through Aura by Raphael Ernealsten (I hope I wrote that right) but will probably be branched off due too the lack of mobile compability.


The game is aimed mostly at mobile Android and IOS. As this is the first real 3D game (or at least with 3D Assets) I make in Unity I have a bunch of questions.


I have a shitload of draw Calls and Batches:


As you can see in the first picture there is quite some Objects. Everything is Low Poly tried to be modelled as low as possible. Everything is done as prefab and if doable share the same material (for ex. a lot of trees share the same). Still its a lot of different assets Around 300 though we don’t use every single one. Every Prefab except for Characters are set to static including lights. All the assets use basically the same Shader ‘Standard’



I just started to read into Unity optimization with texture atlasses and Baked Lighting.


So to my questions:


  • Where to start with optimization? Texture atlasses, dynamic Batching or what else?
  • If I try my luck with texture atlasses can I really put every single Object (even if it is hundreds of objects) onto one Atlass?
  • As you can see there is Rain as particle System using Spread Sheets, also there is a lightning particle Effect and I plan to use a fake volumetric Fog Particle System to fake vol fog, is that too much for mobile?
  • I use Unitys global Fog and don’t know if thats also a big Hit on the performance.

I would describe myself as intermediate Unity User
Thanks in advance for your help.

Cheers Mitja

Hi, it is a good looking game for mobile. First of all, why verts and tris so much? I think you should decrease little bit.
Shadows and lightning have affect your performance. If you have static map, you should use baked lightning for lightmaps.
Also, if you use different shaders of your materials. It costs your performance. Try to use mobile shaders, unlit materials are not affect for lightning measurement.

First of all, cool looking game.

501 batches are not that much and should be no problem at all.
There is no need to look for any graphics optimization (like FOG, etc.), as your GPU currently needs just 3.5ms, which is not much at all.


Your game seems to require a lot of CPU, you are at 12ms. So it would be nice to see an image of the profiler, so we can nail down if and where you waste CPU cycles.


Since most of your 3D meshes seem to be background ‘eye candy’ i would suggest looking into Graphics.DrawMeshInstanced. That alone could save you hell of a lot of performance and it seems you don’t need collision with those background meshes, so that would be a perfect scenario for DrawMeshInstanced.
It might look like a graphics optimization, which it also is, but it also will drastically reduce the amount of game objects the CPU needs to handle.


Edit: I should really learn reading…501 batches are kind of much for mobile

Hey thank you for your fast answer and your compliment.

I will definetly look into this DrawMeshInstanced, most of the Objects if not all except for the Ground are just Eye candy even NPCs won’t attack you or something because the game is more like “Oxenfree” with UI Decisions and general decision making.


If you have experience with this DrawMeshInstanced can you maybe elaborate about it a little?


If you say 501 Batches are a lot for mobile where would you think I should start after DrawMeshInstanced or does it also reduce the batches/Drawcalls?

Hey DaDonik,


I just tested DrawMeshInstanced. I didn’t see your “this needs to be done per Mesh of course” so I tried going through it with a for loop which obviously ended in horrible performance.


After rereading your answer the Problem is it is about 250 different Assets. Which would mean at least 150 (that is the amount in the level) different meshes. So 150 DrawMeshInstanced calls.


I didn’t see your “this needs to be done per Mesh of course” so I tried going through it with a for loop which obviously ended in horrible performance.


Before I start sorting and setting up things do you think this would even turn out in better DrawCalls performance?

Thank you for your help

Thanks @DaDonik and also thanks @abalta I noticed that the Ground has way too many polys so I will reduce it today also I tested writing a Script that takes a List of Prefabs to instance and a list of Hierarchy Groups that should be instanced. The Script now automatically Instances the given Group.


It only works for now if the public Arrays have the same length and will replace them according to their index:


PrefabArray 0 will Instance for Target Array 0

Here is the Script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GPUInstancing : MonoBehaviour {

	public GameObject[] assetBlueprints; //Mesh that will be used for gpu instancing
	public GameObject[] objectsToInstance; //SceneGroups that shall be replaced by instancing
	private Transform[][] targetChildrenArrays;
	private List<Matrix4x4>[] arraysOfPositionLists;

	
	void Start() {
		//Initialize Array of Transform Arrays
		targetChildrenArrays = new Transform[objectsToInstance.Length][];
		InitiateChildren(objectsToInstance);
		InitiateInstancePositions();
		DestroyOldObjects();
	}

	void Update() {
		//Barrels
		InstanceObjects(assetBlueprints,0, 0);
		//Pines
		//InstanceObjects(assetBlueprints,13, 13);
		//Baldtrees
		//InstanceObjects(assetBlueprints,7, 7);
		//Baldtrees Healthy
		//InstanceObjects(assetBlueprints,8, 8);
		//Bushes
		//InstanceObjects(assetBlueprints,16, 16);
	}

	private void InitiateChildren(GameObject[] substituteArray) {
		for(int i = 0; i < objectsToInstance.Length; i++) {
			targetChildrenArrays _= substituteArray*.GetComponentsInChildren<Transform>();*_

_ for(int j = 0; j < targetChildrenArrays*.Length; j ++) {
//Debug.Log(targetChildrenArrays[j]);
}
}
}*_

* private void InitiateInstancePositions() {*
* arraysOfPositionLists = new List[targetChildrenArrays.Length];*
* for(int i = 0; i < objectsToInstance.Length ; i++) {*
_ arraysOfPositionLists = new List();
for(int j = 0; j < targetChildrenArrays*.Length; j++) {
if(targetChildrenArrays[j].gameObject.GetComponent() != null) {
arraysOfPositionLists.Add(targetChildrenArrays[j].transform.localToWorldMatrix);
//Debug.Log(arraysOfPositionLists[j-1]);
}
else {
continue;
}
}
}
}*_

* private void DestroyOldObjects() {*
* for(int i = 0; i < targetChildrenArrays.Length; i++) {*
_ for(int j = 0; j < targetChildrenArrays*.Length; j ++) {
Destroy(targetChildrenArrays[j].gameObject);
}
}
}*_

* private void InstanceObjects(GameObject[] meshArray, int meshArrayElement, int targetPositionList) {*
* //Graphics.DrawMeshInstanced(meshArray[meshArrayElement].gameObject.GetComponent().sharedMesh, 0, meshArray[meshArrayElement].gameObject.GetComponent().sharedMaterial, arraysOfPositionLists[targetPositionList]);*
* //Debug.Log(“Mesh Instanced” + meshArray[meshArrayElement]); *
* //Need to make assetsBlueprints and objectsToInstance the same size so it can automatically do this step*
* for(int i = 0; i < objectsToInstance.Length; i++) {*
Graphics.DrawMeshInstanced(meshArray.gameObject.GetComponent().sharedMesh, 0, meshArray_.gameObject.GetComponent().sharedMaterial, arraysOfPositionLists*);
//Debug.Log(“Mesh Instanced” + meshArray);
}
}
}*

If you want it to just Instance one Group with one Prefab you can comment in the upper part in “Instance Objects” and comment out the lower part also make sure to use the Function in Update
With this kind of instancing its already less Calls but not that many(about 80-90 less) - So I will sit down this weekend and redo my scene with less Assets and use gpu instancing more.
But maybe some1 could use this script or even tell me if everything is fine_