Suche…


Bemerkungen

  1. Deaktivieren Sie nach Möglichkeit Skripts für Objekte, wenn diese nicht benötigt werden. Wenn Sie beispielsweise ein Skript für ein feindliches Objekt haben, das der Spieler sucht und auf den Spieler schießt, sollten Sie dieses Skript deaktivieren, wenn der Feind beispielsweise zu weit vom Spieler entfernt ist.

Schnelle und effiziente Prüfungen

Vermeiden Sie unnötige Operationen und Methodenaufrufe, wo immer Sie können, insbesondere bei einer Methode, die oftmals pro Sekunde aufgerufen wird, z. B. Update .

Entfernungs- / Entfernungsprüfungen

Verwenden Sie sqrMagnitude anstelle des magnitude wenn Sie Entfernungen vergleichen. Dies vermeidet unnötige sqrt . Beachten Sie, dass bei der Verwendung von sqrMagnitude auch die rechte Seite ein Quadrat sein muss.

if ((target.position - transform.position).sqrMagnitude < minDistance * minDistance))

Bounds Checks

Objektschnittstellen können grob geprüft werden, indem geprüft wird, ob sich deren Grenzen zwischen Collider / Renderer schneiden. Die Bounds Struktur verfügt auch über eine praktische Intersects Methode, mit deren Hilfe festgestellt werden kann, ob sich zwei Grenzen schneiden.

Bounds helfen uns auch, eine Bounds.SqrDistance Annäherung an den tatsächlichen Abstand zwischen Objekten zu erhalten (siehe Bounds.SqrDistance ).

Vorsichtsmaßnahmen

Die Begrenzungsprüfung funktioniert für konvexe Objekte sehr gut, aber Begrenzungsprüfungen bei konkaven Objekten können abhängig von der Form des Objekts zu viel höheren Ungenauigkeiten führen.

Die Verwendung von Mesh.bounds wird nicht empfohlen, da sie lokale Mesh.bounds zurückgibt. Verwenden MeshRenderer.bounds stattdessen MeshRenderer.bounds .

Coroutine Power

Verwendungszweck

Wenn Sie einen lang andauernden Vorgang ausführen, der auf der nicht-Thread-sicheren Unity-API beruht , können Sie Coroutines verwenden , um ihn auf mehrere Frames aufzuteilen und Ihre Anwendung reaktionsfähig zu halten.

Coroutines helfen auch, teure Aktionen in jedem n-ten Frame auszuführen , anstatt diese Aktion in jedem Frame auszuführen .

Aufteilen langlaufender Routinen auf mehrere Frames

Coroutines helfen dabei, lange laufende Vorgänge auf mehrere Frames zu verteilen, um die Framerate Ihrer Anwendung zu erhalten.

Routinen, die prozedural malen oder Terrain erzeugen oder Lärm erzeugen, sind Beispiele, für die eine Coroutine-Behandlung erforderlich ist.

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
    }
}

Der obige Code ist ein leicht verständliches Beispiel. In Produktionscode ist es besser , die pro Pixel Prüfung zu vermeiden , die überprüft , wenn sie Nutzen yield return wird (vielleicht tun es alle 2-3 Zeilen) und vorab berechnen for Schleifenlänge im Voraus.

Kostspielige Aktionen seltener durchführen

Mit Coroutinen können Sie seltener teure Aktionen ausführen, sodass die Leistung nicht so groß ist, wie dies bei jedem Frame der Fall wäre.

Nehmen Sie das folgende Beispiel direkt aus dem Handbuch :

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);
    }
}

Durch die Verwendung der CullingGroup-API können Annäherungstests noch weiter optimiert werden.

Häufige Fehler

Ein häufiger Fehler, den Entwickler begehen, ist der Zugriff auf Ergebnisse oder Nebenwirkungen von Coroutinen außerhalb der Coroutine. Coroutines geben die Kontrolle an den Aufrufer zurück, sobald eine yield return Anweisung gefunden wird und das Ergebnis oder der Nebeneffekt noch nicht ausgeführt wird. Zur Umgehung Probleme , bei denen Sie das Ergebnis / Nebenwirkung außerhalb des Koroutine verwenden müssen, überprüfen Sie diese Antwort .

Zeichenketten

Man könnte argumentieren, dass es in Unity größere Ressourcenfresser gibt als die demütige Saite, aber dies ist einer der einfacheren Aspekte, die frühzeitig behoben werden können.

String-Operationen bauen Müll auf

Die meisten String-Vorgänge erzeugen winzige Mengen an Müll, aber wenn diese Vorgänge im Verlauf einer einzelnen Aktualisierung mehrmals aufgerufen werden, stapeln sie sich. Im Laufe der Zeit wird die automatische Speicherbereinigung ausgelöst, was zu sichtbaren CPU-Spitzen führen kann.

Zwischenspeichern Sie Ihre Stringoperationen

Betrachten Sie das folgende Beispiel.

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");
}

Es kann dumm und überflüssig wirken, aber wenn Sie mit Shadern arbeiten, können Sie auf Situationen wie diese stoßen. Das Zwischenspeichern der Schlüssel macht einen Unterschied.

Bitte beachten Sie, dass Zeichenketten und Konstanten erzeugen keinen Müll, da sie statisch in den Programmstapel Raum injiziert werden. Wenn Sie zur Laufzeit Strings generieren und garantiert jedes Mal dieselben Strings wie im obigen Beispiel generieren, ist das Zwischenspeichern definitiv hilfreich.

Für andere Fälle, in denen der erzeugte String nicht jedes Mal gleich ist, gibt es keine andere Alternative zum Generieren dieser Strings. Daher ist die Gedächtnisspitze, bei der jedes Mal manuell Strings erzeugt werden, normalerweise vernachlässigbar, sofern nicht Zehntausende von Strings gleichzeitig erzeugt werden.

Die meisten Stringoperationen sind Debug-Nachrichten

Stringoperationen für Debug-Nachrichten ausführen, z. Debug.Log("Object Name: " + obj.name) ist in Ordnung und kann während der Entwicklung nicht vermieden werden. Es ist jedoch wichtig sicherzustellen, dass irrelevante Debug-Nachrichten nicht im freigegebenen Produkt landen.

Eine Möglichkeit besteht darin, das Conditional-Attribut in Ihren Debug-Aufrufen zu verwenden. Dadurch werden nicht nur die Methodenaufrufe entfernt, sondern auch alle String-Vorgänge, die darin ausgeführt werden.

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);
    }
}

Dies ist ein vereinfachtes Beispiel. Vielleicht möchten Sie etwas Zeit investieren, um eine vollständigere Protokollierungsroutine zu entwerfen.

Stringvergleich

Dies ist eine geringfügige Optimierung, die jedoch erwähnenswert ist. Der Vergleich von Strings ist etwas komplizierter als man denkt. Das System versucht standardmäßig, kulturelle Unterschiede zu berücksichtigen. Sie können stattdessen einen einfachen binären Vergleich verwenden, der schneller abschneidet.

// 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>();

Referenzen zwischenspeichern

Zwischenspeichern von Referenzen, um die teuren Aufrufe insbesondere in der Update-Funktion zu vermeiden. Dies kann durch Cachen dieser Referenzen beim Start (falls verfügbar oder falls verfügbar) und Überprüfen auf null / bool flat erfolgen, damit die Referenz nicht erneut abgerufen wird.

Beispiele:

Komponentenverweise zwischenspeichern

Veränderung

void Update()
{
    var renderer = GetComponent<Renderer>();
    renderer.material.SetColor("_Color", Color.green);
}

zu

private Renderer myRenderer;
void Start()
{
    myRenderer = GetComponent<Renderer>();
}

void Update()
{
    myRenderer.material.SetColor("_Color", Color.green);
}

Objektreferenzen zwischenspeichern

Veränderung

void Update()
{
    var enemy = GameObject.Find("enemy");
    enemy.transform.LookAt(new Vector3(0,0,0));
}

zu

private Transform enemy;

void Start()
{
    this.enemy = GameObject.Find("enemy").transform;
}

void Update()
{
    enemy.LookAt(new Vector3(0, 0, 0));
}

Zusätzlich können Sie kostspielige Anrufe wie Mathfax-Anrufe zwischenspeichern.

Vermeiden Sie den Aufruf von Methoden, die Strings verwenden

Vermeiden Sie den Aufruf von Methoden, die Strings verwenden, die Methoden akzeptieren. Bei diesem Ansatz werden Reflexionen verwendet, die das Spiel verlangsamen können, insbesondere wenn sie in der Aktualisierungsfunktion verwendet werden.

Beispiele:

    //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();

Vermeiden Sie leere Einheitsmethoden

Vermeiden Sie leere Einheitsmethoden. Abgesehen von einem schlechten Programmierstil ist das Runtime-Scripting mit einem geringen Aufwand verbunden. In vielen Fällen kann sich dies auf die Leistung auswirken.

void Update
{
}

void FixedUpdate
{
}


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow