Suggerimenti per l'ottimizzazione per Unity
Unity è un motore di gioco popolare non solo tra gli sviluppatori indipendenti ma anche tra le grandi aziende.
È dotato di un'interfaccia intuitiva, una potente pipeline di rendering, un sistema di componenti facile da usare e, infine, supporta una vasta gamma di piattaforme.
Ma con un'interfaccia facile da usare, è ancora più semplice complicare eccessivamente il gioco (ad esempio posizionando molti oggetti non necessari, ecc.), quindi è importante avere ottimizzazione in mente durante tutto il corso dello sviluppo.
Ecco alcuni suggerimenti importanti per le 3 categorie principali (Rendering, Scripting, e Audio) che ti aiuteranno a migliorare le prestazioni del tuo gioco:
Rendering
Suggerimento 1: mantieni gli oggetti con il componente Renderer non scalati
Gli oggetti non in scala sono quelli che hanno una scala pari a (1, 1, 1). In tal caso, Unity non deve eseguire calcoli aggiuntivi per ridimensionare l'oggetto in ciascun fotogramma.
Esempio: Supponiamo che tu abbia un modello di casa troppo grande o troppo piccolo per il tuo livello. La cosa naturale da fare è ridimensionarlo in questo modo:
Nella maggior parte dei casi va bene ridimensionare l'oggetto nella vista Scena, ma se prevedi di avere molte istanze duplicate di quell'oggetto, è preferibile modificare la scala nelle Impostazioni di importazione.
La prima cosa che devi fare è ridimensionare il modello finché non si adatta alle tue esigenze (es. l'edificio sopra è stato scalato da (1, 1, 1) a (0.49482, 0.49482, 0.49482)), quindi selezionare il modello nel Progetto e nelle Impostazioni di importazione annota il fattore di scala (di solito è 1 o 0,1).
Imposta il nuovo valore, che dovrebbe essere uguale al fattore di scala predefinito moltiplicato per la nuova scala (nel mio caso è 1 x 0,49482 = 0,49482), quindi premi Applica. Ora torna al modello nella vista scena e imposta nuovamente la scala su (1, 1, 1).
L'oggetto è ora ridimensionato nel modo desiderato preservando la scala predefinita (1, 1, 1).
Questo suggerimento è particolarmente importante per gli oggetti animati che utilizzano SkinnedMeshRenderer poiché questo componente è più costoso da renderizzare e avere una scala di (1, 1, 1) semplifica il processo di rendering.
Suggerimento 2: usa meno luci possibile
Sono disponibili 3 tipi di luci in Unity (luce direzionale, luce puntiforme e riflettore). In termini di prestazioni, la luce direzionale è la più economica per il rendering, poi il punto e infine il riflettore.
In generale, non vuoi avere più di una luce direzionale per scena, e per le luci spot e puntiformi cerca di averne il meno possibile (o nessuna).
In termini di ombre in tempo reale, mentre migliora l'aspetto visivo del gioco, ha un sovraccarico ad alte prestazioni, quindi in genere è meglio disabilitarle o inserirle in lightmaps e light sonde.
Suggerimento 3: utilizzare gli shader trasparenti con cautela
Utilizza gli shader trasparenti o di particelle solo su superfici che devono essere trasparenti (ad es. Recinzioni, particelle di fumo, ecc.)
Gli oggetti con trasparenza richiedono un passaggio di rendering aggiuntivo che potrebbe ridurre le prestazioni, soprattutto su piattaforme con risorse limitate, come Mobile o Web.
Scrittura
Suggerimento 1: memorizzare sempre nella cache i riferimenti ai componenti
Dovresti sempre memorizzare nella cache i riferimenti ai componenti se prevedi di accedervi a ogni aggiornamento.
Ad esempio, controlla lo script seguente:
Cattivo
using UnityEngine;
public class Script1 : MonoBehaviour
{
float someValue = 0;
// Update is called once per frame
void Update()
{
someValue = GetComponent<Script2>().someValue2;
}
}
Qui abbiamo Script1 che ottiene la variabile "someValue2" da Script2 e la assegna a una variabile locale.
Ora, chiamando solo un GetComponent ogni frame non avrà alcun impatto sulle prestazioni, tuttavia, dovresti adottare l'abitudine di memorizzare nella cache i componenti che verranno utilizzati frequentemente.
Esistono due modi per memorizzare nella cache un componente in uno script: creare una variabile pubblica e assegnarla nella vista Inspector oppure creare una variabile privata e assegnarla da Start o Awake. Controlla l'esempio qui sotto:
Bene
using UnityEngine;
public class Script1 : MonoBehaviour
{
float someValue = 0;
Script2 script2Cached;
// Use this for initialization
void Start()
{
script2Cached = GetComponent<Script2>();
}
// Update is called once per frame
void Update()
{
someValue = script2Cached.someValue2;
}
}
Molto meglio, ora è possibile accedere a Script2 a ogni aggiornamento senza sovraccarico delle prestazioni.
Fai lo stesso per i componenti integrati, come BoxCollider, Rigidbody, ecc. (eccetto Transform e GameObject, questi sono già memorizzati nella cache per impostazione predefinita, quindi puoi accedervi immediatamente).
Suggerimento 2: utilizzare SendMessage con cautela
SendMessage ti consente di chiamare una funzione specifica (se esiste) su ogni MonoBehaviour che è attached a un oggetto di gioco.
Ad esempio, quando spari con un'arma nel gioco puoi infliggere rapidamente danni quando il proiettile colpisce il nemico, senza la necessità di utilizzare GetComponent e altre cose extra.
Tuttavia, questo metodo non dovrebbe essere chiamato troppo frequentemente poiché richiede un'elevata intensità di calcolo.
Suggerimento 3: utilizza GameObject.Find e GameObject.FindWithTag con cautela
GameObject.Find, GameObject.FindWithTag e GameObject.FindGameObjectsWithTag ti consentono di cercare rapidamente gli oggetti nella scena. Questi metodi sono molto più lenti di GetComponent e devono essere utilizzati solo durante l'inizializzazione.
Suggerimento 4: evitare di utilizzare OnGUI
Storicamente OnGUI era l'unico modo per creare menu in Unity. Ma da allora è stata aggiunta un'alternativa chiamata UI Canvas che è molto migliore in termini di prestazioni e offre molte più funzionalità.
Tuttavia, OnGUI rimane ancora un modo praticabile per creare UI in Unity e se hai assolutamente bisogno di usarlo, tieni presente che OnGUI viene chiamato almeno due volte per frame (il doppio di Update e LateUpdate), quindi non farlo usalo per qualsiasi calcolo oltre all'interfaccia utente.
Una cosa che puoi fare è avere uno script separato che contenga solo OnGUI e abilitarlo/disabilitarlo quando necessario.
Per esempio:
UIScript.cs
using UnityEngine;
public class UIScript : MonoBehaviour {
void OnGUI()
{
if(GUI.Button(new Rect(5, 5, 125, 25), "Button 1"))
{
//Button 1 was pressed, Do Something
}
if (GUI.Button(new Rect(140, 5, 125, 25), "Button 2"))
{
//Button 2 was pressed, Do Something
}
}
}
Script1.cs
using UnityEngine;
public class Script1 : MonoBehaviour
{
UIScript uiScript;
// Use this for initialization
void Start()
{
uiScript = GetComponent<UIScript>();
uiScript.enabled = false;
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Tab))
{
//toggle UIScript when Tab is pressed
uiScript.enabled = !uiScript.enabled;
}
}
}
Sia UIScript che Script1 sono collegati allo stesso GameObject. Script1 controlla quando mostrare il menu.
Quando il giocatore preme Tab, l'UIScript viene abilitato, mostrando i pulsanti. Premendo nuovamente Tab lo si disattiva, nascondendo i pulsanti.
Mentre UIScript è disattivato, il metodo OnGUI non viene chiamato, il che a sua volta migliora le prestazioni.
Suggerimento 5: utilizza Profiler
Profiler è uno degli strumenti più importanti quando si tratta di identificare colli di bottiglia e cali di fps, rendendo più semplice trovare la causa esatta delle scarse prestazioni.
Audio
Le clip audio possono essere ottimizzate assicurandosi che le impostazioni di importazione siano corrette.
Le impostazioni ottimali di importazione dell'audio dipenderanno dalla lunghezza dell'audio, dalla frequenza di riproduzione e dalla piattaforma di destinazione.