Dies ist der fünfte Artikel in einer Reihe von Artikeln, die Optimierungs-Tipps für Ihre Unity-Projekte aufzeigen. Verwenden Sie sie als Leitfaden für die Ausführung mit höheren Bildraten und weniger Ressourcen. Wenn Sie diese bewährten Verfahren ausprobiert haben, sollten Sie sich die anderen Seiten dieser Reihe ansehen:
- Konfigurieren Sie Ihr Unity-Projekt für eine bessere Leistung
- Leistungsoptimierung für High-End-Grafiken
- Verwaltung der GPU-Nutzung für PC- und Konsolenspiele
- Fortgeschrittene Programmierung und Code-Architektur
Die Physik kann ein kompliziertes Gameplay schaffen, aber das geht auf Kosten der Leistung. Sobald Sie diese Kosten kennen, können Sie die Simulation so anpassen, dass Sie sie angemessen verwalten können. Nutzen Sie diese Tipps, um Ihre Ziel-Framerate einzuhalten und eine flüssige Wiedergabe mit der in Unity integrierten Physik (NVIDIA PhysX) zu erreichen.
Die in der Physik verwendeten Meshes durchlaufen einen Prozess, der Cooking genannt wird. Dadurch wird das Netz so vorbereitet, dass es mit Physikabfragen wie Raycasts, Kontakten usw. arbeiten kann.
Ein MeshCollider hat mehrere CookingOptions, die Ihnen helfen, das Mesh für die Physik zu validieren. Wenn Sie sicher sind, dass Ihr Netz diese Prüfungen nicht benötigt, können Sie sie deaktivieren, um die Garzeit zu verkürzen.
Deaktivieren Sie in den CookingOptions für jeden MeshCollider die Optionen EnableMeshCleaning, WeldColocatedVertices und CookForFasterSimulation. Diese Optionen sind für prozedural generierte Meshes zur Laufzeit nützlich, können aber deaktiviert werden, wenn Ihre Meshes bereits die richtigen Dreiecke haben.
Wenn Sie speziell auf den PC abzielen, stellen Sie sicher, dass Use Fast Midphase aktiviert ist. In der mittleren Phase der Simulation wird auf einen schnelleren Algorithmus von PhysX 4.1 umgeschaltet (was dazu beiträgt, eine kleine Gruppe von sich möglicherweise überschneidenden Dreiecken für Physikabfragen einzugrenzen). Nicht-Desktop-Plattformen müssen weiterhin den langsameren Algorithmus verwenden, der die R-Bäume.
Mesh Colliders sind in der Regel teuer, daher sollte man in Erwägung ziehen, komplexere Mesh Colliders durch primitive oder vereinfachte zu ersetzen, um die ursprüngliche Form anzunähern.
Weitere Informationen finden Sie in der CookingOptions-Dokumentation.
Wenn Sie Meshes während des Spiels prozedural erzeugen, können Sie zur Laufzeit einen Mesh Collider erstellen. Wenn Sie jedoch eine Mesh Collider-Komponente direkt zum Mesh hinzufügen, wird die Physik auf dem Hauptthread gekocht. Dies kann erhebliche CPU-Zeit in Anspruch nehmen.
Verwenden Sie Physics.BakeMesh, um ein Mesh für die Verwendung mit einem Mesh Collider vorzubereiten, und speichern Sie die gebackenen Daten mit dem Mesh selbst. Ein neuer Mesh Collider, der dieses Mesh referenziert, wird diese vorgebackenen Daten wiederverwenden, anstatt das Mesh erneut zu backen. Dies kann dazu beitragen, die Ladezeit der Szene oder die Instanziierungszeit zu verkürzen. Um die Leistung zu optimieren, können Sie das Mesh-Cooking auf einen anderen Thread auslagern mit dem C#-Auftragssystem.
In diesem Beispiel erfahren Sie, wie Sie Meshes über mehrere Threads hinweg backen können.
Aktivieren Sie in den Player-Einstellungen das Kontrollkästchen Prebake Collision Meshes, wenn möglich. Wir empfehlen außerdem, die Einrichtung der Kollisionsmatrix zu überprüfen, um sicherzustellen, dass sich die Spieler- und Spielmechanikobjekte in den richtigen Ebenen befinden.
Das Entfernen von Callbacks aus Triggern für unnötige Layer kann ein großer Gewinn sein, also versuchen Sie, Ihre Layer-Kollisionsmatrix zu vereinfachen. Sie können Ihre Physikeinstellungen über Projekteinstellungen > Physik bearbeiten.
Weitere Informationen finden Sie in der Dokumentation Kollisionsmatrix.
Physik-Engines arbeiten mit einem festen Zeitintervall. Um die feste Rate zu sehen, mit der Ihr Projekt läuft, gehen Sie zu Bearbeiten > Projekteinstellungen > Zeit.
Das Feld Fester Zeitschritt definiert das Zeitdelta, das von jedem Physikschritt verwendet wird. Der Standardwert von 0,02 Sekunden (20 ms) entspricht zum Beispiel 50 Bildern pro Sekunde (fps) oder 50 Hz.
Da jedes Bild in Unity eine variable Zeitspanne benötigt, ist es nicht perfekt mit der Physiksimulation synchronisiert. Der Motor zählt bis zum nächsten physikalischen Timestep. Wenn ein Frame etwas langsamer oder schneller läuft, verwendet Unity die verstrichene Zeit, um zu wissen, wann die Physiksimulation mit dem richtigen Timestep laufen soll.
Wenn die Vorbereitung eines Rahmens lange dauert, kann dies zu Leistungsproblemen führen. Wenn Ihr Spiel also einen Spike erlebt (z. B. beim Instanziieren vieler GameObjects oder beim Laden einer Datei von der Festplatte), kann die Ausführung des Frames 40 ms oder mehr dauern. Bei der Standardeinstellung von 20 ms festem Zeitschritt würde dies dazu führen, dass zwei Physiksimulationen auf dem folgenden Bild laufen, um den variablen Zeitschritt "einzuholen".
Zusätzliche Physiksimulationen bedeuten wiederum mehr Zeit für die Verarbeitung des Bildes. Auf Low-End-Plattformen führt dies möglicherweise zu einer Abwärtsspirale bei der Leistung.
Wenn die Vorbereitung eines späteren Rahmens länger dauert, wird auch der Rückstand bei den Physiksimulationen größer. Dies führt zu noch langsameren Frames und mehr Simulationen pro Frame. Das Ergebnis ist eine schwächere Leistung.
Schließlich könnte die Zeit zwischen den Physikaktualisierungen die maximal zulässige Zeitspanne überschreiten. Nach diesem Cutoff beginnt Unity, Physik-Updates zu verwerfen, und das Spiel stottert.
Um Leistungsprobleme mit der Physik zu vermeiden:
- Reduzieren Sie die Simulationshäufigkeit: Für Low-End-Plattformen erhöhen Sie denFixed Timestep auf einen Wert, der geringfügig über der Zielbildrate liegt. Verwenden Sie zum Beispiel 0,035 Sekunden für 30 fps auf dem Handy. Dies könnte dazu beitragen, die Leistungsspirale nach unten zu verhindern.
- Verringern Sie die maximal zulässige Zeitspanne: Die Verwendung eines kleineren Wertes (z. B. 0,1 Sekunden) geht zu Lasten der Genauigkeit der Physiksimulation, begrenzt aber auch die Anzahl der Physikaktualisierungen, die in einem Frame stattfinden können. Experimentieren Sie mit den Werten, um etwas zu finden, das den Anforderungen Ihres Projekts entspricht.
- Simulieren Sie den Physikschritt gegebenenfalls manuell: Deaktivieren Sie die Option "Automatische Simulation " in den Physikeinstellungen und rufen Sie während der Aktualisierungsphase des Rahmens direkt "Physik.simulieren " auf. So können Sie aktiv bestimmen, wann der Physikschritt ausgeführt werden soll. Übergeben Sie Time.deltaTime an Physics.Simulat, um die Physik mit der Simulationszeit synchron zu halten.
- Hinweis Dieser Ansatz kann zu Instabilitäten in der Physiksimulation führen - insbesondere in Szenen mit komplexer Physik oder stark variierenden Frame-Zeiten. Verwenden Sie es mit Vorsicht.
Weitere Informationen finden Sie in der Physics.Simulate-Dokumentation.
Die Unity-Physik-Engine läuft in zwei Schritten:
- Die breite Phase: Sammelt potenzielle Kollisionen mit einem Sweep-and-Prune-Algorithmus
- Die enge Phase: Wenn die Maschine die Kollisionen tatsächlich berechnet
Die Breitphasen-Standardeinstellung von "Sweep and Prune Broadphase"(Bearbeiten > Projekteinstellungen > Physik > Breitphasentyp) kann bei Welten, die im Allgemeinen flach sind und viele Kollider haben, falsch positive Ergebnisse liefern. Wenn Ihre Szene groß und größtenteils flach ist, vermeiden Sie dieses Problem und wechseln Sie zu Automatic Box Pruning oder Multibox Pruning Broadphase. Diese Optionen teilen die Welt in ein Gitter ein, wobei jede Gitterzelle Sweep und Prune ausführt.
Mit Multibox Pruning Broadphase können Sie die Weltgrenzen und die Anzahl der Gitterzellen manuell festlegen, während das automatische Box Pruning alles für Sie berechnet.
Hier finden Sie die vollständige Liste der Physics-Eigenschaften.
Wenn Sie einen bestimmten physikalischen Körper genauer simulieren wollen, erhöhen Sie dessen Rigidbody.solverIterations. Dies überschreibt die Physics.defaultSolverIterations, die über Edit > Project Settings > Physics > Default Solver Iterations gefunden werden kann.
Um Ihre Physiksimulationen zu optimieren, stellen Sie einen relativ niedrigen Wert für defaultSolveIterations im Projekt ein. Wenden Sie dann höhere benutzerdefinierte Rigidbody.solverIterations-Werte auf die einzelnen Instanzen an, die mehr Details benötigen.
Weitere Informationen zu Rigidbody.solverIterations.
Wenn Sie eine Transformation aktualisieren, synchronisiert Unity sie nicht automatisch mit der Physik-Engine. Unity akkumuliert Transformationen und wartet darauf, dass entweder die Physikaktualisierung ausgeführt wird oder dass der Benutzer die Funktion Physics.SyncTransforms.
Wenn Sie die Physik häufiger mit Ihren Transformationen synchronisieren möchten, können Sie die Physics.autoSyncTransform auf True setzen (auch zu finden in Projekteinstellungen > Physik > Auto Sync Transforms). Wenn diese Option aktiviert ist, wird jeder Rigidbody oder Collider auf dieser Transformation zusammen mit seinen Kindern automatisch mit der Transformation aktualisiert.
Wenn dies jedoch nicht unbedingt erforderlich ist, deaktivieren Sie sie. Eine Reihe von aufeinanderfolgenden Physikabfragen (z. B. Raycasts) kann zu Leistungseinbußen führen.
Erfahren Sie mehr über Physics.SyncTransforms.
Kollisions- und Triggerereignisse (OnCollisionEnter, OnCollisionStay, OnCollisionExit, OnTriggerEnter, OnTriggerStay, OnTriggerExit) wird für jedes MonoBehaviour ausgelöst, das diese Funktionen implementiert und die Interaktionskriterien erfüllt. Diese Ereignisse werden auch an deaktivierte MonoBehaviours gesendet.
Deshalb ist es empfehlenswert, diese Funktionen nur bei Bedarf zu implementieren, da leere, nicht benötigte Funktionen aufgerufen werden. Besondere Vorsicht ist bei OnCollisionStay und OnTriggerStay geboten, da diese je nach Anzahl der beteiligten Kollider mehrfach aufgerufen werden können.
Die RückrufeMonoBehaviour.OnCollisionEnter, MonoBehaviour.OnCollisionStayund MonoBehaviour.OnCollisionExit nehmen ebenfalls eine Kollisionsinstanz als Parameter an. Diese Kollisionsinstanz wird auf dem verwalteten Heap zugewiesen und muss gelöscht werden.
Um die Menge des erzeugten Mülls zu reduzieren, aktivieren Sie Physics.reuseCollisionCallbacks(zu finden in Projekteinstellungen > Physik > Kollisionsrückrufe wiederverwenden). Wenn diese Funktion aktiviert ist, weist Unity jedem Callback nur eine einzige Kollisionspaar-Instanz zu. Dadurch wird der Abfall für den Garbage Collector (GC) reduziert und die Gesamtleistung verbessert.
Hinweis Wenn Sie die Kollisionsinstanz außerhalb der Kollisions-Callbacks für die Nachbearbeitung referenzieren, müssen SieReuse Collision Callbacks deaktivieren.
Erfahren Sie mehr über Physics.reuseCollisionCallbacks.
Statische Collider sind GameObjects mit einer Collider-Komponente, aber ohne Rigidbody. Anders als der Name vermuten lässt, können Sie einen Static Collider verschieben.
Ändern Sie einfach die Position des Physikkörpers, akkumulieren Sie die Positionsänderungen und synchronisieren Sie vor der Physikaktualisierung, ohne dass Sie eine Rigidbody-Komponente zum Static Collider hinzufügen müssen, um ihn zu bewegen. Wenn Sie jedoch möchten, dass der Static Collider auf komplexere Weise mit anderen physikalischen Körpern interagiert, geben Sie ihm einen kinematischen Rigidbody. Verwenden Sie Rigidbody.position und Rigidbody.Rotation um ihn zu bewegen, anstatt auf die Komponente Transform zuzugreifen. Dies garantiert ein besser vorhersehbares Verhalten der Physik-Engine.
Hinweis In der 2D-Physik sollten Sie die statischen Kollider nicht verschieben, da der Neuaufbau des Baums sehr zeitaufwändig ist.
Erfahren Sie mehr über Rigidbodies.
Um Kollider innerhalb einer bestimmten Entfernung und in einer bestimmten Richtung zu erkennen und zu sammeln, verwenden Sie Raycasts und andere Physikabfragen wie BoxCast.
Physikabfragen, die mehrere Kollider als Array zurückgeben, wie ÜberlappendeSphäre oder OverlapBoxzurückgeben, müssen diese Objekte auf dem verwalteten Heap zuweisen. Dies bedeutet, dass der Garbage Collector die zugewiesenen Objekte schließlich einsammeln muss, was die Leistung beeinträchtigen kann, wenn dies zum falschen Zeitpunkt geschieht.
Um diesen Overhead zu reduzieren, verwenden Sie die NonAlloc-Versionen dieser Abfragen. Wenn Sie zum Beispiel OverlapSphere verwenden, um alle potenziellen Kollisionspartner um einen Punkt herum zu sammeln, stellen Sie sicher, dass Sie OverlapSphereNonAlloc. Dies ermöglicht es Ihnen, ein Array von Kollidern (den Ergebnisparameter) zu übergeben, das wie ein Puffer wirkt.
Die NonAlloc-Methode funktioniert, ohne Müll zu erzeugen. Ansonsten funktioniert sie wie die entsprechende Zuweisungsmethode. Denken Sie nur daran, einen ausreichend großen Ergebnispuffer zu definieren, wenn Sie eine NonAlloc-Methode verwenden. Der Puffer wächst nicht, wenn der Platz knapp wird.
Weitere Informationen finden Sie in der Dokumentation der NonAlloc-Methode.
Sie können zwar Raycast-Abfragen mit Physics.Raycastausführen können, kann dies eine erhebliche Menge an CPU-Zeit in Anspruch nehmen. Dies gilt insbesondere bei einer großen Anzahl von Raycast-Vorgängen (z. B. Berechnung der Sichtlinie für 10.000 Agenten).
Verwenden Sie RaycastCommand um die Abfrage mit Hilfe des C# Job System zu stapeln. Dies entlastet den Hauptthread, so dass die Raycasts asynchron und parallel erfolgen können.
Ein Beispiel finden Sie in der Dokumentation RaycastCommands.
Verwenden Sie das Physik-Debug-Fenster(Fenster > Analyse > Physik-Debugger), um Probleme mit Kollidern oder Unstimmigkeiten zu beheben. Dieses Fenster zeigt eine farbkodierte Anzeige von Spielobjekten, die miteinander kollidieren können.
Weitere Informationen finden Sie auf der Seite Visualisierung von Physik-Debugging.
Einer unserer umfassendsten Leitfäden aller Zeiten enthält über 80 umsetzbare Tipps zur Optimierung Ihrer Spiele für PC und Konsole. Diese ausführlichen Tipps wurden von unseren Experten von Success und Accelerate Solutions erstellt und helfen Ihnen, das Beste aus Unity herauszuholen und die Leistung Ihres Spiels zu steigern.