Sök…


Anmärkningar

  1. 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äkna for 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
{
}


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow