unity optimze 3d model reduce draw call
Functioning is a key aspect of any game and no surprise, no affair how expert the game is, if it runs poorly on the user'south machine, it will not feel as enjoyable.
Since not everyone has a loftier-end PC or device (if you are targeting mobile), it'due south of import to continue operation in mind during the whole grade of development.
At that place are multiple reasons why the game could run slowly:
- Rendering (Besides many loftier-poly meshes, complex shaders, or image furnishings)
- Audio (Mostly caused by incorrect audio import settings)
- Unoptimized Lawmaking (Scripts that incorporate performance-demanding functions in the wrong places)
In this tutorial, I will exist showing how to optimize your code with a help of Unity Profiler.
Profiler
Historically, debugging performance in Unity was a irksome job, just since then, a new characteristic has been added, chosen Profiler.
Profiler is a tool in Unity that lets yous speedily pinpoint the bottlenecks in your game by monitoring the retention consumption, which profoundly simplifies the optimization procedure.
Bad Performance
Bad performance tin can happen at any time: Let'southward say you're working on the enemy example and when you place it in the scene, information technology works fine without any bug, just as you spawn more enemies you may notice fps (frames-per-second) begin to drib.
Check the example below:
In the Scene I have a Cube with a script attached to information technology, which moves the Cube from side to side and displays the object proper noun:
SC_ShowName.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; public class SC_ShowName : MonoBehaviour { bool moveLeft = truthful; bladder movedDistance = 0; // Start is called before the first frame update void Offset() { moveLeft = Random.Range(0, 10) > five; } // Update is called in one case per frame void Update() { //Move left and right in ping-pong mode if (moveLeft) { if(movedDistance > -2) { movedDistance -= Fourth dimension.deltaTime; Vector3 currentPosition = transform.position; currentPosition.10 -= Time.deltaTime; transform.position = currentPosition; } else { moveLeft = false; } } else { if (movedDistance < ii) { movedDistance += Time.deltaTime; Vector3 currentPosition = transform.position; currentPosition.x += Time.deltaTime; transform.position = currentPosition; } else { moveLeft = true; } } } void OnGUI() { //Show object name on screen Camera mainCamera = Camera.main; Vector2 screenPos = mainCamera.WorldToScreenPoint(transform.position + new Vector3(0, i, 0)); GUI.color = Color.light-green; GUI.Characterization(new Rect(screenPos.x - 150/two, Screen.summit - screenPos.y, 150, 25), gameObject.proper noun); } }
Looking at the stats, we can see that the game runs at a adept 800+ fps, so it barely has any impact on the operation.
Just let's see what will happen when nosotros duplicate the Cube 100 times:
Fps dropped past more 700 points!
Annotation: All tests were washed with Vsync disabled
Generally, information technology's a skillful idea to start optimizing when the game begins to showroom stuttering, freezing or the fps drops beneath 120.
How to Utilize Profiler?
To start using Profiler yous'll need:
- Kickoff your game past pressing Play
- Open Profiler by going to Window -> Analysis -> Profiler (or press Ctrl + 7)
- New Window will announced that look something like this:
- It might look intimidating at beginning (specially with all those charts etc.), only it'south not the part we will exist looking at.
- Click on the Timeline tab and modify it to Hierarchy:
- You'll notice 3 sections (EditorLoop, PlayerLoop and Profiler.CollectEditorStats):
- Aggrandize the PlayerLoop to run into all the parts where the ciphering power is being spent (NOTE: If the PlayerLoop values are not updating, click on the "Clear" button at the top of the Profiler window).
For the best results, direct your game graphic symbol to the situation (or place) where the game lags the most and wait for couple of seconds.
- After waiting a bit, End the game and observe the PlayerLoop list
You need to look at GC Alloc value, which stands for Garbage Collection Allocation. This is a blazon of retention that has been allocated by the component, but is no longer needed and is waiting to be freed by the Garbage Collection. Ideally, the lawmaking should not generate any garbage (or be every bit close to 0 as possible).
Time ms is also an important value, it shows how long the lawmaking took to run in milliseconds, so ideally you should aim to reduce this value too (past caching values, avoiding calling performance demanding functions each Update, etc.).
To locate the troublesome parts faster, click on GC Alloc cavalcade to sort the values from higher to lower)
- In the CPU Usage nautical chart click anywhere to skip to that frame. Specifically, we need to look at peaks, where the fps was the everyman:
Hither is what the Profiler revealed:
GUI.Repaint is allocating 45.4KB, which is quite a lot, expanding it revealed more info:
- Information technology shows that most of the allocations are coming from GUIUtility.BeginGUI() and OnGUI() method in SC_ShowName script, knowing that we tin can begin optimizing.
GUIUtility.BeginGUI() represents an empty OnGUI() method (Yes, fifty-fifty empty OnGUI() method allocates quite a lot of retentivity).
Apply Google (or other search engine) to find the names you do non recognize.
Here is the OnGUI() part that needs to be optimized:
void OnGUI() { //Show object name on screen Photographic camera mainCamera = Camera.master; Vector2 screenPos = mainCamera.WorldToScreenPoint(transform.position + new Vector3(0, 1, 0)); GUI.color = Color.green; GUI.Label(new Rect(screenPos.ten - 150/2, Screen.height - screenPos.y, 150, 25), gameObject.name); }
Optimization
Let's begin optimizing.
Each SC_ShowName script calls its own OnGUI() method, which is non good because we have 100 instances. So what tin can be washed nearly it? The answer is, to have a single script with OnGUI() method that calls the GUI method for each Cube.
- Showtime, I replaced the default OnGUI() in SC_ShowName script with public void GUIMethod() which will be chosen from some other script:
public void GUIMethod() { //Testify object proper name on screen Camera mainCamera = Photographic camera.primary; Vector2 screenPos = mainCamera.WorldToScreenPoint(transform.position + new Vector3(0, 1, 0)); GUI.color = Color.green; GUI.Label(new Rect(screenPos.10 - 150/2, Screen.pinnacle - screenPos.y, 150, 25), gameObject.proper noun); }
- Then I created a new script and called it SC_GUIMethod:
SC_GUIMethod.cs
using UnityEngine; public grade SC_GUIMethod : MonoBehaviour { SC_ShowName[] instances; //All instances where GUI method will be called void Start() { //Notice all instances instances = FindObjectsOfType<SC_ShowName>(); } void OnGUI() { for(int i = 0; i < instances.Length; i++) { instances[i].GUIMethod(); } } }
SC_GUIMethod will exist attached to a random object in the scene and phone call all the GUI methods.
- We went from having 100 individual OnGUI() methods to having merely one, let's press play and see the result:
- GUIUtility.BeginGUI() is at present only allocating 368B instead of 36.7KB, a large reduction!
However, OnGUI() method is still allocating retentiveness, merely since we know it'southward only calling GUIMethod() from SC_ShowName script, we are going straight to debugging that method.
But the Profiler but shows global data, how practise nosotros see what exactly is happening within the method?
To debug inside the method, Unity has a handy API called Profiler.BeginSample
Profiler.BeginSample allows yous to capture a specific department of the script, showing how long it took to complete and how much memory was allocated.
- Before using the Profiler class in lawmaking, nosotros demand to import the UnityEngine.Profiling namespace at the beginning of the script:
using UnityEngine.Profiling;
- The Profiler Sample is captured by adding Profiler.BeginSample("SOME_NAME"); at the start of the capture and adding Profiler.EndSample(); at the end of the capture, like this:
Profiler.BeginSample("SOME_CODE"); //...your lawmaking goes here Profiler.EndSample();
Since I don't know what part of the GUIMethod() is causing retentivity allocations, I enclosed each line in the Profiler.BeginSample and Profiler.EndSample (Only if your method has a lot of lines, you're definitely don't need to enclose each line, just split it into even chunks and so work from there).
Here is a final method with Profiler Samples implemented:
public void GUIMethod() { //Show object proper noun on screen Profiler.BeginSample("sc_show_name part 1"); Camera mainCamera = Camera.main; Profiler.EndSample(); Profiler.BeginSample("sc_show_name part ii"); Vector2 screenPos = mainCamera.WorldToScreenPoint(transform.position + new Vector3(0, one, 0)); Profiler.EndSample(); Profiler.BeginSample("sc_show_name part 3"); GUI.color = Color.dark-green; GUI.Label(new Rect(screenPos.x - 150/2, Screen.height - screenPos.y, 150, 25), gameObject.name); Profiler.EndSample(); }
- Now I press Play and see what information technology shows in the Profiler:
- For convenience, I searched for "sc_show_" in the Profiler, since all samples start with that proper noun.
- Interesting... A lot of memory is being allocated in sc_show_names role 3, which corresponds to this office of code:
GUI.colour = Color.green; GUI.Label(new Rect(screenPos.x - 150/2, Screen.pinnacle - screenPos.y, 150, 25), gameObject.name);
After some Googling, I discovered that getting Object's name allocates quite a lot of retentiveness. The solution is to assign an Object's name to a string variable in void Start(), that manner it will only be called once.
Here is the optimized code:
SC_ShowName.cs
using UnityEngine; using UnityEngine.Profiling; public form SC_ShowName : MonoBehaviour { bool moveLeft = true; bladder movedDistance = 0; cord objectName = ""; // Start is called earlier the offset frame update void Start() { moveLeft = Random.Range(0, ten) > 5; objectName = gameObject.proper name; //Store Object name to a variable } // Update is called one time per frame void Update() { //Move left and right in ping-pong fashion if (moveLeft) { if(movedDistance > -2) { movedDistance -= Time.deltaTime; Vector3 currentPosition = transform.position; currentPosition.x -= Time.deltaTime; transform.position = currentPosition; } else { moveLeft = false; } } else { if (movedDistance < 2) { movedDistance += Time.deltaTime; Vector3 currentPosition = transform.position; currentPosition.x += Time.deltaTime; transform.position = currentPosition; } else { moveLeft = true; } } } public void GUIMethod() { //Bear witness object name on screen Profiler.BeginSample("sc_show_name part 1"); Camera mainCamera = Camera.main; Profiler.EndSample(); Profiler.BeginSample("sc_show_name office ii"); Vector2 screenPos = mainCamera.WorldToScreenPoint(transform.position + new Vector3(0, 1, 0)); Profiler.EndSample(); Profiler.BeginSample("sc_show_name function 3"); GUI.color = Color.greenish; GUI.Characterization(new Rect(screenPos.x - 150/two, Screen.superlative - screenPos.y, 150, 25), objectName); Profiler.EndSample(); } }
- Let'southward run across what the Profiler is showing:
All samples are allocating 0B, so no more memory is being allocated.
Source: https://sharpcoderblog.com/blog/unity-3d-how-to-use-profiler-to-optimize-your-code
Post a Comment for "unity optimze 3d model reduce draw call"