unity3d
Optimering
Sök…
Anmärkningar
- Om möjligt kan du avaktivera skript på objekt när de inte behövs. Om du till exempel har ett skript på ett fiendeobjekt som söker efter och skjuter på spelaren överväger att inaktivera detta skript när fienden är för långt från spelaren.
Snabba och effektiva kontroller
Undvik onödiga operationer och metodsamtal var du än kan, särskilt i en metod som kallas många gånger i sekundet, som Update
.
Avstånd / intervallkontroller
Använd sqrMagnitude
istället för magnitude
när du jämför avstånd. Detta undviker onödiga sqrt
operationer. Observera att när du använder sqrMagnitude
höger sida också vara kvadratisk.
if ((target.position - transform.position).sqrMagnitude < minDistance * minDistance))
Begränsar kontroller
Objektkorsningar kan kontrolleras grovt genom att kontrollera om deras Collider
/ Renderer
gränser korsar varandra. Bounds
strukturen har också en praktisk Intersects
som hjälper till att avgöra om två gränser korsar varandra.
Bounds
hjälper oss också att beräkna en nära uppskattning av det faktiska avståndet mellan ytan och ytan (se Bounds.SqrDistance
).
förbehåll
Gränskontroll fungerar riktigt bra för konvexa objekt, men gränskontroller av konkava objekt kan leda till mycket högre felaktigheter beroende på objektets form.
Att använda Mesh.bounds
rekommenderas inte eftersom det returnerar lokala rymdgränser. Använd MeshRenderer.bounds
istället.
Coroutine Power
Användande
Om du har en lång drift som förlitar sig på det icke-tråd-säkra Unity API, använd Coroutines för att dela den över flera ramar och hålla din applikation lyhörd.
Coroutines hjälper också till att utföra dyra åtgärder varje tredje ram istället för att köra den åtgärden varje ram.
Dela upp långa körrutiner över flera ramar
Coroutines hjälper till att distribuera långvariga operationer över flera ramar för att hålla framerate för din applikation.
Rutiner som målar eller genererar terräng procedurellt eller genererar brus är exempel som kan behöva Coroutine-behandlingen.
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
}
}
Koden ovan är ett lättförståeligt exempel. I produktionskod är det bättre att undvika per-pixelkontrollen som kontrollerar när du ska
yield return
(kanske gör det var 2-3 rader) och att förberäknafor
slinglängd i förväg.
Att utföra dyra åtgärder mindre ofta
Coroutines hjälper dig att utföra dyra åtgärder mindre ofta, så att det inte är en så stor prestandahit som det skulle vara om du utför varje ram.
Ta följande exempel direkt från Manual :
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);
}
}
Närhetstester kan optimeras ytterligare genom att använda CullingGroup API .
Vanliga fallgropar
Ett vanligt misstag utvecklare gör är att få tillgång till resultat eller biverkningar av koroutiner utanför koroutinen. Coroutines returnerar kontrollen till den som ringer så snart ett yield return
har uppstått och resultatet eller biverkningen kanske inte har utförts ännu. Kontrollera detta svar för att kringgå problem där du måste använda resultatet / biverkningen utanför koroutinen.
strängar
Man kan hävda att det finns större resurshogar i enhet än den ödmjuka strängen, men det är en av de enklare aspekterna att fixa tidigt.
Strängoperationer bygger skräp
De flesta strängoperationer bygger små mängder skräp, men om dessa operationer kallas flera gånger under en enda uppdatering staplas det upp. Med tiden utlöser den den automatiska soporna, vilket kan leda till en synlig CPU-spik.
Cache dina strängoperationer
Tänk på följande exempel.
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");
}
Det kan se dumt och överflödigt ut, men om du arbetar med Shaders kan du stöta på situationer som dessa. Cachering av tangenterna kommer att göra en skillnad.
Observera att strängbokstäver och konstanter inte genererar något skräp, eftersom de injiceras statiskt i programstackutrymmet. Om du genererar strängar vid körning och garanteras generera samma strängar varje gång som exemplet ovan, kommer caching definitivt att hjälpa.
För andra fall där strängen som genereras inte är densamma varje gång finns det inget annat alternativ att generera dessa strängar. Som sådan är minnesspetsen med manuellt genererade strängar varje gång vanligtvis försumbar, såvida inte tiotusentals strängar genereras åt gången.
De flesta strängoperationer är felsökningsmeddelanden
Gör strängoperationer för felsökningsmeddelanden, dvs. Debug.Log("Object Name: " + obj.name)
är bra och kan inte undvikas under utveckling. Det är dock viktigt att se till att irrelevanta felsökningsmeddelanden inte hamnar i den släppta produkten.
Ett sätt är att använda attributet Villkorligt i dina felsökningssamtal. Detta tar inte bara bort metodsamtal, utan också alla strängoperationer som går till det.
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);
}
}
Detta är ett förenklat exempel. Du kanske vill investera lite tid på att utforma en mer fullständig skogsrutin.
Strängen jämförelse
Detta är en mindre optimering, men det är värt att nämna. Att jämföra strängar är lite mer involverat än man skulle tro. Systemet kommer att försöka ta hänsyn till kulturella skillnader som standard. Du kan välja att använda en enkel binär jämförelse istället, som fungerar snabbare.
// 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-referenser
Cache-referenser för att undvika dyra samtal särskilt i uppdateringsfunktionen. Detta kan göras genom att cachera dessa referenser vid start om de är tillgängliga eller när de är tillgängliga och kontrollera om null / bool platt för att undvika att få referensen igen.
Exempel:
Cache-komponentreferenser
förändra
void Update()
{
var renderer = GetComponent<Renderer>();
renderer.material.SetColor("_Color", Color.green);
}
till
private Renderer myRenderer;
void Start()
{
myRenderer = GetComponent<Renderer>();
}
void Update()
{
myRenderer.material.SetColor("_Color", Color.green);
}
Cache-objektreferenser
förändra
void Update()
{
var enemy = GameObject.Find("enemy");
enemy.transform.LookAt(new Vector3(0,0,0));
}
till
private Transform enemy;
void Start()
{
this.enemy = GameObject.Find("enemy").transform;
}
void Update()
{
enemy.LookAt(new Vector3(0, 0, 0));
}
Dessutom cache dyra samtal som samtal till Mathf där det är möjligt.
Undvik att ringa metoder med strängar
Undvik att ringa metoder med strängar som kan acceptera metoder. Detta tillvägagångssätt kommer att använda reflektion som kan bromsa ditt spel särskilt när det används i uppdateringsfunktionen.
Exempel:
//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();
Undvik tomma enhetsmetoder
Undvik tomma enhetsmetoder. Bortsett från att vara dålig programmeringsstil, finns det en mycket liten overhead involverad i runtime scripting. I många fall kan detta bygga upp och påverka prestandan.
void Update
{
}
void FixedUpdate
{
}