unity3d
optimalisatie
Zoeken…
Opmerkingen
- Schakel indien mogelijk scripts voor objecten uit wanneer ze niet nodig zijn. Als u bijvoorbeeld een script hebt op een vijandelijk object dat zoekt naar en vuurt naar de speler, overweeg dan om dit script uit te schakelen wanneer de vijand bijvoorbeeld te ver van de speler verwijderd is.
Snelle en efficiënte controles
Vermijd onnodige bewerkingen en methodeaanroepen waar u maar kunt, vooral in een methode die vele malen per seconde wordt aangeroepen, zoals Update
.
Afstand / bereikcontroles
Gebruik sqrMagnitude
plaats van magnitude
bij het vergelijken van afstanden. Dit voorkomt onnodige sqrt
bewerkingen. Merk op dat bij het gebruik van sqrMagnitude
de rechterkant ook vierkant moet zijn.
if ((target.position - transform.position).sqrMagnitude < minDistance * minDistance))
Grenzencontroles
Kruispunten van objecten kunnen grof worden gecontroleerd door te controleren of hun Collider
/ Renderer
grenzen elkaar kruisen. De Bounds
structuur heeft ook een handige Intersects
methode die helpt bepalen of twee grenzen elkaar kruisen.
Bounds
helpen ons ook bij het berekenen van een benadering van de werkelijke (oppervlakte tot oppervlakte) afstand tussen objecten (zie Bounds.SqrDistance
).
Voorbehoud
Controle van grenzen werkt echt goed voor convexe objecten, maar grenscontroles op concave objecten kunnen leiden tot veel hogere onnauwkeurigheden, afhankelijk van de vorm van het object.
Het gebruik van Mesh.bounds
wordt niet aanbevolen, omdat dit grenzen van de lokale ruimte retourneert. Gebruik MeshRenderer.bounds
plaats daarvan MeshRenderer.bounds
.
Coroutine vermogen
Gebruik
Als u een langdurige operatie uitvoert die afhankelijk is van de niet-thread-safe Unity API, gebruikt u Coroutines om deze over meerdere frames te splitsen en uw applicatie responsief te houden.
Coroutines helpen ook bij het uitvoeren van dure acties elk nde frame in plaats van die actie bij elk frame uit te voeren.
Langlopende routines verdelen over meerdere frames
Coroutines helpen bij het verspreiden van langlopende bewerkingen over meerdere frames om de framerate van uw toepassing te behouden.
Routines die procedureel schilderen of terrein genereren of lawaai genereren, zijn voorbeelden waarvoor de Coroutine-behandeling nodig kan zijn.
for (int y = 0; y < heightmap.Height; y++)
{
for (int x = 0; x < heightmap.Width; x++)
{
// Generate pixel at (x, y)
// Assign pixel at (x, y)
// Process only 32768 pixels each frame
if ((y * heightmap.Height + x) % 32 * 1024) == 0)
yield return null; // Wait for next frame
}
}
De bovenstaande code is een gemakkelijk te begrijpen voorbeeld. In productiecode is het beter om de controle per pixel te vermijden die controleert wanneer het
yield return
(misschien om de 2-3 rijen) en om van tevoren de luslengtefor
berekenen.
Duurdere acties minder vaak uitvoeren
Coroutines helpen u minder vaak dure acties uit te voeren, zodat het niet zo'n groot succes is als wanneer elk frame zou worden uitgevoerd.
Neem het volgende voorbeeld rechtstreeks uit de handleiding :
private void ProximityCheck()
{
for (int i = 0; i < enemies.Length; i++)
{
if (Vector3.Distance(transform.position, enemies[i].transform.position) < dangerDistance)
return true;
}
return false;
}
private IEnumerator ProximityCheckCoroutine()
{
while(true)
{
ProximityCheck();
yield return new WaitForSeconds(.1f);
}
}
Nabijheidstests kunnen nog verder worden geoptimaliseerd met behulp van de CullingGroup API .
Gemeenschappelijke valkuilen
Een veelgemaakte fout die ontwikkelaars maken is toegang tot resultaten of bijwerkingen van coroutines buiten de coroutine. Coroutines retourneren controle naar de beller zodra een yield return
wordt aangetroffen en het resultaat of bijwerking mogelijk nog niet is uitgevoerd. Te omzeilen problemen waar je moet het resultaat / side effect te gebruiken buiten de coroutine, controleert dit antwoord .
strings
Je zou kunnen stellen dat er in Unity grotere resource hogs zijn dan de bescheiden string, maar het is een van de gemakkelijkere aspecten om al in een vroeg stadium op te lossen.
String operaties bouwen afval op
De meeste stringbewerkingen bouwen kleine hoeveelheden afval op, maar als die bewerkingen tijdens een enkele update meerdere keren worden aangeroepen, stapelt het zich op. Na verloop van tijd zal het de automatische Garbage Collection activeren, wat kan resulteren in een zichtbare CPU-piek.
Cache je string operaties
Overweeg het volgende voorbeeld.
string[] StringKeys = new string[] {
"Key0",
"Key1",
"Key2"
};
void Update()
{
for (var i = 0; i < 3; i++)
{
// Cached, no garbage generated
Debug.Log(StringKeys[i]);
}
for (var i = 0; i < 3; i++)
{
// Not cached, garbage every cycle
Debug.Log("Key" + i);
}
// The most memory-efficient way is to not create a cache at all and use literals or constants.
// However, it is not necessarily the most readable or beautiful way.
Debug.Log("Key0");
Debug.Log("Key1");
Debug.Log("Key2");
}
Het ziet er misschien gek en overbodig uit, maar als je met Shaders werkt, kun je situaties zoals deze tegenkomen. Het cachen van de toetsen zal het verschil maken.
Merk op dat stringliterals en constanten geen afval genereren, omdat ze statisch in de stapel van het programma worden geïnjecteerd. Als u tijdens het uitvoeren strings genereert en gegarandeerd dezelfde strings genereert als in het bovenstaande voorbeeld, zal caching zeker helpen.
Voor andere gevallen waarin de gegenereerde tekenreeks niet elke keer hetzelfde is, is er geen ander alternatief voor het genereren van die tekenreeksen. Als zodanig is de geheugenpiek met het handmatig genereren van tekenreeksen meestal te verwaarlozen, tenzij tienduizenden tekenreeksen tegelijkertijd worden gegenereerd.
De meeste tekenreeksbewerkingen zijn foutopsporingsberichten
Stringbewerkingen uitvoeren voor foutopsporingsberichten, dat wil zeggen. Debug.Log("Object Name: " + obj.name)
is prima en kan niet worden vermeden tijdens de ontwikkeling. Het is echter belangrijk om ervoor te zorgen dat irrelevante foutopsporingsberichten niet in het vrijgegeven product terechtkomen.
Een manier is om het kenmerk Voorwaardelijk te gebruiken in uw debug-aanroepen. Hiermee worden niet alleen de methodeaanroepen verwijderd, maar ook alle tekenreeksen die erin worden verwerkt.
using UnityEngine;
using System.Collections;
public class ConditionalDebugExample: MonoBehaviour
{
IEnumerator Start()
{
while(true)
{
// This message will pop up in Editor but not in builds
Log("Elapsed: " + Time.timeSinceLevelLoad);
yield return new WaitForSeconds(1f);
}
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
void Log(string Message)
{
Debug.Log(Message);
}
}
Dit is een vereenvoudigd voorbeeld. Misschien wilt u wat tijd besteden aan het ontwerpen van een volwaardige logboekroutine.
String vergelijking
Dit is een kleine optimalisatie, maar het is het vermelden waard. Het vergelijken van strings is iets ingewikkelder dan men zou denken. Het systeem zal standaard proberen rekening te houden met culturele verschillen. U kunt ervoor kiezen om in plaats daarvan een eenvoudige binaire vergelijking te gebruiken, die sneller presteert.
// Faster string comparison
if (strA.Equals(strB, System.StringComparison.Ordinal)) {...}
// Compared to
if (strA == strB) {...}
// Less overhead
if (!string.IsNullOrEmpty(strA)) {...}
// Compared to
if (strA == "") {...}
// Faster lookups
Dictionary<string, int> myDic = new Dictionary<string, int>(System.StringComparer.Ordinal);
// Compared to
Dictionary<string, int> myDictionary = new Dictionary<string, int>();
Cache referenties
Cachereferenties om dure oproepen te voorkomen, vooral in de updatefunctie. Dit kan worden gedaan door deze referenties in het cachegeheugen op te slaan indien beschikbaar of indien beschikbaar en te controleren op null / bool flat om te voorkomen dat de referentie opnieuw wordt opgehaald.
Voorbeelden:
Verwijzingen naar cachecomponenten
verandering
void Update()
{
var renderer = GetComponent<Renderer>();
renderer.material.SetColor("_Color", Color.green);
}
naar
private Renderer myRenderer;
void Start()
{
myRenderer = GetComponent<Renderer>();
}
void Update()
{
myRenderer.material.SetColor("_Color", Color.green);
}
Cache-objectverwijzingen
verandering
void Update()
{
var enemy = GameObject.Find("enemy");
enemy.transform.LookAt(new Vector3(0,0,0));
}
naar
private Transform enemy;
void Start()
{
this.enemy = GameObject.Find("enemy").transform;
}
void Update()
{
enemy.LookAt(new Vector3(0, 0, 0));
}
Waar mogelijk kunt u dure oproepen, zoals oproepen naar Mathf, in de cache opslaan.
Vermijd het aanroepen van methoden met tekenreeksen
Vermijd het aanroepen van methoden met tekenreeksen die methoden kunnen accepteren. Deze aanpak maakt gebruik van reflectie die je spel kan vertragen, vooral wanneer het wordt gebruikt in de updatefunctie.
Voorbeelden:
//Avoid StartCoroutine with method name
this.StartCoroutine("SampleCoroutine");
//Instead use the method directly
this.StartCoroutine(this.SampleCoroutine());
//Avoid send message
var enemy = GameObject.Find("enemy");
enemy.SendMessage("Die");
//Instead make direct call
var enemy = GameObject.Find("enemy") as Enemy;
enemy.Die();
Vermijd lege eenheidsmethoden
Vermijd lege eenheidsmethoden. Afgezien van een slechte programmeerstijl, is er een zeer kleine overhead betrokken bij runtime scripting. In veel gevallen kan dit de prestaties verhogen en beïnvloeden.
void Update
{
}
void FixedUpdate
{
}