Diese Seite beschreibt häufige Leistungsprobleme und bewährte Verfahren zu deren Minderung.
Skriptberechnung
Aufwändige Operationen im Luau-Code benötigen länger zur Verarbeitung und können somit die Bildrate beeinträchtigen. Sofern nicht parallel ausgeführt, wird Luau-Code synchron ausgeführt und blockiert den Hauptthread, bis er auf eine Funktion stößt, die den Thread yieldet.
Häufige Probleme
Intensive Operationen auf Tabellenstrukturen - Komplexe Operationen wie Serialisierung, Deserialisierung und tiefes Klonen verursachen hohe Leistungskosten, insbesondere bei großen Tabellenstrukturen. Dies gilt insbesondere, wenn diese Operationen rekursiv sind oder über sehr große Datenstrukturen iterieren.
Hochfrequente Ereignisse - Teure Operationen an framebasierten Ereignissen von RunService ohne Frequenzbeschränkung führen dazu, dass diese Operationen in jedem Frame wiederholt werden, was oft zu einer unnötigen Erhöhung der Rechenzeit führt. Diese Ereignisse umfassen:
Minderung
- Rufen Sie Code in Ereignissen von RunService sparsam auf und beschränken Sie die Verwendung auf Fälle, in denen eine hochfrequente Ausführung erforderlich ist (zum Beispiel, um die Kamera zu aktualisieren). Sie können den meisten anderen Code in anderen Ereignissen oder weniger häufig in einer Schleife ausführen.
- Unterteilen Sie große oder teure Aufgaben mit task.wait(), um die Arbeit über mehrere Frames zu verteilen.
- Identifizieren und optimieren Sie unnötig teure Operationen und verwenden Sie Multithreading für rechenintensive Aufgaben, die nicht auf das Datenmodell zugreifen müssen.
- Bestimmte serverseitige Skripte können von der nativem Codegenerierung profitieren, einem einfachen Flag, das ein Skript in Maschinencode anstelle von Bytecode kompiliert.
MicroProfiler-Scopes
| Scope | Assoziierte Berechnung |
| RunService.PreRender | Code, der beim PreRender-Ereignis ausgeführt wird |
| RunService.PreSimulation | Code, der beim Stepped-Ereignis ausgeführt wird |
| RunService.PostSimulation | Code, der beim Heartbeat-Ereignis ausgeführt wird |
| RunService.Heartbeat | Code, der beim Heartbeat-Ereignis ausgeführt wird |
Für weitere Informationen zur Fehlersuche bei Skripten mit dem MicroProfiler siehe die debug-Bibliothek, die Funktionen zum Taggen spezifischer Codes und zur weiteren Erhöhung der Spezifität umfasst, wie debug.profilebegin und debug.profileend. Viele von Skripten aufgerufene Roblox-API-Methoden verfügen ebenfalls über ihre eigenen zugehörigen MicroProfiler-Tags, die nützliche Signale geben können.
Skriptspeichernutzung
Speicherlecks können auftreten, wenn Sie Skripte schreiben, die Speicher verbrauchen, den der Garbage Collector nicht ordnungsgemäß freigeben kann, wenn er nicht mehr in Gebrauch ist. Lecks sind speziell auf dem Server weit verbreitet, da sie über viele Tage online bleiben können, während eine Client-Sitzung viel kürzer ist.
Die folgenden Speicherwerte in der Entwicklerkonsole können auf ein Problem hinweisen, das weiterer Untersuchung bedarf:
- LuaHeap - Hoher oder wachsender Verbrauch deutet auf ein Speicherleck hin.
- InstanceCount - Konsistent wachsende Zahlen von Instanzen deuten darauf hin, dass Referenzen auf einige Instanzen in Ihrem Code nicht vom Garbage Collector bereinigt werden.
- PlaceScriptMemory - Bietet eine skriptweise Aufschlüsselung der Speichernutzung.
Häufige Probleme
Verbindungsüberbleibsel - Die Engine bereinigt niemals Events, die mit einer Instanz verbunden sind, und jegliche Werte, die im verbundenen Callback referenziert werden. Daher sind aktive Verbindungen von Ereignissen und Code innerhalb der verbundenen Instanzen, verbundener Funktionen und referenzierter Werte, außerhalb des Geltungsbereichs für den Garbace Collector, selbst nachdem die Ereignisse ausgelöst wurden.
Obwohl Ereignisse getrennt werden, wenn die Instanz, zu der sie gehören, zerstört wird, ist ein häufiger Fehler anzunehmen, dass dies auf Player-Objekte zutrifft. Nachdem ein Benutzer ein Spiel verlässt, zerstört die Engine nicht automatisch ihr repräsentatives Player-Objekt und Charaktermodell, sodass Verbindungen zu diesem Player-Objekt und Instanzen unter dem Charaktermodell, wie beispielsweise CharacterAdded, weiterhin Speicher verbrauchen, wenn Sie sie in Ihren Skripten nicht trennen. Dies kann im Laufe der Zeit zu erheblichen Speicherlecks auf dem Server führen, da Hunderte von Benutzern das Spiel betreten und verlassen.
Tabellen - Objekte in Tabellen einzufügen, sie jedoch nicht zu entfernen, wenn sie nicht mehr benötigt werden, verursacht unnötigen Speicherverbrauch, insbesondere bei Tabellen, die Benutzerdaten beim Betreten verfolgen. Zum Beispiel erstellt das folgende Codesample eine Tabelle, die Benutzerinformationen jedes Mal hinzufügt, wenn ein Benutzer eintritt:
Beispiellocal playerInfo = {}Players.PlayerAdded:Connect(function(player)playerInfo[player] = {} -- einige Informationenend)Wenn Sie diese Einträge nicht entfernen, wenn sie nicht mehr benötigt werden, wächst die Tabelle weiterhin in der Größe und verbraucht mehr Speicher, wenn mehr Benutzer der Sitzung beitreten. Jeglicher Code, der über diese Tabelle iteriert, wird auch rechenintensiver, je größer die Tabelle wird.
Minderung
Um alle verwendeten Werte zu bereinigen und Speicherlecks zu verhindern:
Trennen Sie alle Verbindungen - Überprüfen Sie Ihren Code und stellen Sie sicher, dass jede Verbindung über einen der folgenden Pfade bereinigt wird:
- Manuelles Trennen mit der Funktion Disconnect().
- Zerstören der Instanz, zu der das Ereignis gehört, mit der Funktion Destroy().
- Zerstören des Skriptobjekts, auf das die Verbindung zurückverfolgt wird.
Entfernen Sie Spielerobjekte und Charaktere nach dem Verlassen - Aktivieren Sie Workspace.PlayerCharacterDestroyBehavior, um Spielerobjekte und Charaktermodelle automatisch zu zerstören, nachdem ein Benutzer das Spiel verlässt. Wenn Sie es vorziehen, können Sie sie stattdessen manuell bereinigen:
Beispiel der Bereinigung von Spielern und Charakterenlocal Players = game:GetService("Players")Players.PlayerAdded:Connect(function(player)player.CharacterRemoving:Connect(function(character)task.defer(character.Destroy, character)end)end)Players.PlayerRemoving:Connect(function(player)task.defer(player.Destroy, player)end)
Physikberechnung
Exzessive Physiksimulation kann ein wesentlicher Grund für die erhöhte Berechnungszeit pro Frame sowohl auf dem Server als auch auf dem Client sein.
Häufige Probleme
Exzessive Frequenz des Physik-Zeitsschrittes - Standardmäßig erfolgt das Stepping-Verhalten im adaptiven Modus, bei dem die Physik mit 60 Hz, 120 Hz oder 240 Hz schrittweise verarbeitet wird, abhängig von der Komplexität des Physikmechanismus.
Ein fester Modus mit verbesserter Genauigkeit der Physik ist ebenfalls verfügbar, der alle Physik-Assemblierungen zwingt, mit 240 Hz (viermal pro Frame) zu schrittweise zu arbeiten. Dies führt zu deutlich mehr Berechnung jeder Frame.
Übermäßig viele oder komplexe simulierte Objekte - Je mehr 3D-Assemblierungen simuliert werden, desto länger dauert die Berechnung der Physik jeder Frame. Oft haben Spiele Objekte, die simuliert werden, die es nicht müssen oder Mechanismen mit mehr Einschränkungen und Gelenken, als sie tatsächlich benötigen.
Übermäßig präzise Kollisionserkennung - Mesh-Teile haben eine CollisionFidelity-Eigenschaft zur Erkennung von Kollisionen, die eine Vielzahl von Modi mit unterschiedlichen Leistungsanforderungen bietet. Der Modus für präzise Kollisionserkennung bei Mesh-Teilen hat die teuersten Leistungskosten und benötigt länger für die Berechnung durch die Engine.
Minderung
Verankern Sie Teile, die keine Simulation benötigen - Verankern Sie alle Teile, die nicht durch Physik betrieben werden müssen, wie statische NPCs.
Verwenden Sie adaptives physikalisches Stepping - Adaptives Stepping passt die Rate der physikalischen Berechnungen dynamisch an, wodurch es möglich wird, Physikaktualisierungen in einigen Fällen seltener durchzuführen.
Verringern Sie die Komplexität der Mechanismen
- Minimieren Sie nach Möglichkeit die Anzahl der physikalischen Einschränkungen oder Gelenke in einer Assemblierung.
- Reduzieren Sie die Menge an Selbstkollision innerhalb eines Mechanismus, z. B. durch das Anwenden von Begrenzungen oder Kollisionseinschränkungen an Ragdoll-Gliedmaßen, um zu verhindern, dass sie miteinander kollidieren.
Reduzieren Sie die Nutzung präziser Kollisionstreue für Meshes
Verwenden Sie für kleine oder nicht interaktive Objekte, bei denen Benutzer selten den Unterschied bemerken würden, die Box-Treue.
Für kleine bis mittelgroße Objekte verwenden Sie je nach Form entweder Box- oder Hüllentreue.
Für große und sehr komplexe Objekte erstellen Sie nach Möglichkeit benutzerdefinierte Kollisionen mit unsichtbaren Teilen.
Für Objekte, die keine Kollisionen benötigen, deaktivieren Sie Kollisionen und verwenden Sie Box- oder Hüllentreue, da die Kollision-Geomatrie weiterhin im Speicher gespeichert wird.
Sie können Kollision-Geometrie zu Debugging-Zwecken im Studio darstellen, indem Sie Kollisionsfidelity im Widget Visualisierungsoptionen in der oberen rechten Ecke des 3D-Viewport aktivieren.
Alternativ können Sie den Filter CollisionFidelity=PreciseConvexDecomposition im Explorer anwenden, welcher eine Anzahl aller Mesh-Teile mit präziser Treue anzeigt und Ihnen ermöglicht, diese einfach auszuwählen.
Für eine eingehende Anleitung zur Auswahl einer Kollisionstreu-Option, die Ihre Präzisions- und Leistungsanforderungen in Einklang bringt, siehe Setzen von physikalischen und Renderingparametern.
MicroProfiler-Scopes
| Scope | Assoziierte Berechnung |
| physicsStepped | Gesamte physikalische Berechnung |
| worldStep | Diskrete physikalische Schritte, die in jedem Frame durchgeführt werden |
Physikspeicherverbrauch
Die Bewegung der Physik und die Kollisionserkennung verbrauchen Speicher. Mesh-Teile haben eine CollisionFidelity-Eigenschaft, die den Ansatz bestimmt, der zur Bewertung der Kollisionseinschränkungen des Meshs verwendet wird.
Häufiges Problem
Die Standard- und präzisen Kollisionsmodi verbrauchen erheblich mehr Speicher als die beiden anderen Modi mit niedrigeren Kollisionstreueformen.
Wenn Sie hohe Speicherverbrauchswerte unter PhysicsParts feststellen, sollten Sie in Betracht ziehen, die Kollisionsfidelity von Objekten in Ihrem Spiel zu reduzieren.
Wie man mildert
Um den Speicherverbrauch für Kollisionstreue zu reduzieren:
- Deaktivieren Sie die Kollisionen an Teilen, die keine Kollisionen benötigen, indem Sie BasePart.CanCollide, BasePart.CanTouch und BasePart.CanQuery auf false setzen.
- Reduzieren Sie die Fidelity der Kollisionen mit der CollisionFidelity-Einstellung. Box hat den geringsten Speicherverbrauch, während Default und Precise im Allgemeinen teurer sind.
- Es ist generell sicher, die Kollisionstreue eines kleinen verankerten Teils auf Box zu setzen.
- Für sehr komplexe große Meshes sollten Sie möglicherweise Ihr eigenes Kollisionsmesh aus kleineren Objekten mit Box-Kollisionstreue erstellen.
Humanoide
Humanoid ist eine Klasse, die eine Vielzahl von Funktionen für Spieler und Nicht-Spieler-Charaktere (NPCs) bereitstellt. Obwohl leistungsfähig, ist ein Humanoid mit erheblichen Berechnungskosten verbunden.
Häufige Probleme
- Alle HumanoidStateTypes auf NPCs aktiviert lassen - Es gibt einen Leistungsaufwand, wenn bestimmte HumanoidStateTypes aktiviert bleiben. Deaktivieren Sie alle, die nicht für Ihre NPCs benötigt werden. Zum Beispiel, es ist sicher, den Zustand Climbing zu deaktivieren, es sei denn, Ihr NPC soll Leitern erklimmen.
- Instanziierung, Modifizierung und Respawn von Modellen mit Humanoids oder skinned MeshParts häufig - Dies kann für die Engine intensiv verarbeiten werden, insbesondere wenn diese Modelle geschichtete Kleidung verwenden. Dies kann auch besonders problematisch bei Spielen sein, in denen Avatare häufig respawnen.
- Im MicroProfiler sind lange Tags updateInvalidatedFastClusters (über 4 ms) oft ein Signal, dass die Instanziierung/Modifikation von Avataren übermäßige Invalidierungen auslöst.
- Humanoide in Fällen verwenden, in denen sie nicht erforderlich sind - Statische NPCs, die sich nicht bewegen, benötigen im Allgemeinen keine Humanoid-Klasse.
- Animationen an einer großen Anzahl von NPCs vom Server abspielen - NPC-Animationen, die auf dem Server ausgeführt werden, müssen auf dem Server simuliert und an den Client repliziert werden. Dies kann unnötige Overhead verursachen.
- Unnötige Größen- und Maßänderungen durchführen - Größen-/Maßänderungen führen dazu, dass FastCluster neu aufgebaut wird. Versuchen Sie, dies während des Spiels zu reduzieren, wenn Sie Leistungsprobleme in Bezug auf FastCluster feststellen. Ebenso können andere Eigenschaftsänderungen dazu führen, dass FastCluster neu aufgebaut wird, also reduzieren Sie diese Änderungen im Allgemeinen so weit wie möglich.
Minderung
- Animationen von NPCs auf dem Client abspielen - In Spielen mit einer großen Anzahl von NPCs sollten Sie in Erwägung ziehen, die Animator auf dem Client zu erstellen und die Animationen lokal auszuführen. Dies reduziert die Last auf dem Server und die Notwendigkeit unnötiger Replikation. Außerdem sind zusätzliche Optimierungen möglich (wie das Abspielen von Animationen nur für NPCs, die sich in der Nähe des Charakters befinden).
- Leistungsfreundliche Alternativen zu Humanoiden verwenden - NPC-Modelle müssen nicht zwangsläufig ein Humanoid-Objekt enthalten.
- Für statische NPCs verwenden Sie einen einfachen AnimationController, da sie sich nicht bewegen müssen, sondern nur Animationen abspielen müssen.
- Für sich bewegende NPCs sollten Sie erwägen, eigenen Bewegungskontroller zu implementieren und einen AnimationController für Animationen zu verwenden, je nach der Komplexität Ihrer NPCs.
- Deaktivieren Sie nicht verwendete Humanoid-Zustände - Verwenden Sie Humanoid:SetStateEnabled(), um nur erforderliche Zustände für jeden Humanoid zu aktivieren.
- Poolen von NPC-Modellen mit häufigem Respawn - Anstatt einen NPC vollständig zu zerstören, senden Sie ihn in einen Pool inaktiver NPCs. Auf diese Weise können Sie, wenn ein neuer NPC wiederbelebt werden muss, einfach einen der NPCs aus dem Pool reaktivieren. Dieser Prozess nennt sich Pooling, was die Anzahl der Male minimiert, die Charaktere instanziiert werden müssen.
- NPCs nur spawnen, wenn Benutzer in der Nähe sind - Spawnen Sie NPCs nicht, wenn Benutzer nicht im Bereich sind, und entfernen Sie sie, wenn Benutzer ihren Bereich verlassen.
- Änderungen an der Avatarhierarchie nach der Instanziierung vermeiden - Bestimmte Änderungen an einer Avatarhierarchie haben erhebliche Auswirkungen auf die Leistung. Einige Optimierungen sind verfügbar:
- Für benutzerdefinierte prozedurale Animationen aktualisieren Sie nicht die Eigenschaften JointInstance.C0 und JointInstance.C1. Aktualisieren Sie stattdessen die Eigenschaft Motor6D.Transform.
MicroProfiler-Scopes
| Scope | Assoziierte Berechnung |
| stepHumanoid | Humanoid-Kontrolle und Physik |
| stepAnimation | Humanoid- und Animator-Animation |
| updateInvalidatedFastClusters | Assoziiert mit der Instanziierung oder Modifikation eines Avatars |
Rendering
Ein wesentlicher Teil der Zeit, die der Client in jedem Frame für das Rendern des Szenarios benötigt, entfällt auf das Rendering der Szene im aktuellen Frame. Der Server führt kein Rendering durch, sodass dieser Abschnitt ausschließlich für den Client gedacht ist.
Draw-Calls
Ein Draw-Call ist eine Gruppe von Anweisungen der Engine an die GPU, um etwas zu rendern. Draw-Calls haben einen erheblichen Overhead. Im Allgemeinen gilt: Je weniger Draw-Calls pro Frame, desto weniger Rechenzeit wird für das Rendern eines Frames aufgewendet.
Sie können sehen, wie viele Draw-Calls gerade stattfinden, indem Sie den Render Stats ⟩ Timing-Bereich im Studio anzeigen. Sie können Render Stats im Client anzeigen, indem Sie ShiftF2 drücken.
Je mehr Objekte in Ihrer Szene in einem bestimmten Frame gezeichnet werden müssen, desto mehr Draw-Calls werden an die GPU gesendet. Die Roblox-Engine nutzt jedoch einen Prozess namens Instancing, um identische Meshes mit denselben Texturmerkmalen in einen einzelnen Draw-Call zu konsolidieren. Genauer gesagt, mehrere Meshes, die denselben MeshContent verwenden, werden in einem einzelnen Draw-Call verarbeitet, wenn:
- SurfaceAppearances identisch sind, falls vorhanden, andernfalls, wenn TextureContents identisch sind.
- Materialien identisch sind, wenn sowohl SurfaceAppearance als auch MeshPart.TextureID nicht vorhanden sind.
Weitere häufige Probleme
Exzessive Objektdichte - Wenn eine große Anzahl von Objekten mit hoher Dichte konzentriert ist, erfordert das Rendern dieses Bereichs der Szene mehr Draw-Calls. Wenn Sie feststellen, dass Ihre Bildrate sinkt, wenn Sie auf ein bestimmtes Gebiet der Karte schauen, kann dies ein gutes Signal dafür sein, dass die Objektdichte in diesem Bereich zu hoch ist.
Objekte wie Decals, Texturen und Partikel batchen schlecht und führen zu zusätzlichen Draw-Calls. Achten Sie besonders auf diese Objekttypen in einer Szene. Insbesondere haben Eigenschaftsänderungen an ParticleEmitters einen dramatischen Einfluss auf die Leistung.
Verpasste Instanzierungsmöglichkeiten - Oft enthält eine Szene das gleiche Mesh, das mehrmals dupliziert wird, wobei jedoch jede Kopie des Meshs unterschiedliche Mesh- oder Textur-Asset-IDs hat. Dies verhindert Instanzierungen und kann zu unnötigen Draw-Calls führen.
Ein häufige Ursache für dieses Problem ist, wenn eine gesamte Szene auf einmal importiert wird, anstatt individuelle Assets in Roblox zu importieren und diese nach dem Import zu duplizieren, um die Szene zusammenzustellen.
Selbst ein einfaches Skript wie dieses kann Ihnen helfen, Mesh-Teile mit demselben Namen zu identifizieren, die unterschiedliche Mesh-IDs verwenden:
for _,descendant in workspace:GetDescendants() doif descendant:IsA("MeshPart") thenprint(descendant.Name .. ", " .. descendant.MeshId)endendDie Ausgabe (mit Stack Lines aktiviert) könnte folgendermaßen aussehen. Wiederholte Zeilen zeigen die Wiederverwendung desselben Meshs an, was gut ist. Einzigartige Zeilen sind nicht unbedingt schlecht, könnten jedoch, je nach Ihrem Benennungsschema, auf Duplikate in Ihrem Spiel hindeuten:
LargeRock, rbxassetid://106420009602747 (x144) -- gutLargeRock, rbxassetid://120109824668127LargeRock, rbxassetid://134460273008628LargeRock, rbxassetid://139288987285823LargeRock, rbxassetid://71302144984955LargeRock, rbxassetid://90621205713698LargeRock, rbxassetid://113160939160788LargeRock, rbxassetid://135944592365226 -- alle möglichen DuplikateExzessive Objektkomplexität - Obwohl nicht so wichtig wie die Anzahl der Draw-Calls beeinflusst die Anzahl der Dreiecke in einer Szene, wie lange ein Frame zum Rendern benötigt. Szenen mit einer sehr großen Zahl sehr komplexer Meshes sind ein häufiges Problem, ebenso wie Szenen, in denen die MeshPart.RenderFidelity-Eigenschaft auf Precise bei zu vielen Meshes festgelegt ist.
Exzessives Schattenwerfen - Die Handhabung von Schatten ist ein aufwendiger Prozess, und Karten, die eine hohe Anzahl und Dichte von Lichtobjekten enthalten, die Schatten werfen (oder eine hohe Anzahl und Dichte kleiner Teile, die von Schatten beeinflusst werden), können Leistungsprobleme aufweisen.
Hohe Transparenzüberzeichnung - Das Platzieren von Objekten mit teilweiser Transparenz nahe beieinander zwingt die Engine, die überlappenden Pixel mehrfach zu rendern, was die Leistung beeinträchtigen kann. Weitere Informationen zur Identifizierung und Behebung dieses Problems finden Sie unter Layered Transparency löschen.
Unnötige Bewegung von skinned MeshParts - Skinned MeshParts, die Teil eines Modells ohne Humanoid sind, werden mithilfe räumlich organisierter FastClusters gruppiert. Wenn sich diese MeshParts bewegen, müssen sie ständig zu diesen räumlichen Clustern hinzugefügt und daraus entfernt werden, was dazu führt, dass die Cluster neu aufgebaut werden müssen, was die Leistung beeinträchtigt.
- Eine sehr effektive Lösung besteht darin, einen Humanoid innerhalb des Modells einzubetten. Das Vorhandensein eines Humanoiden überschreibt das standardmäßige räumliche Clusterverhalten, was die Verwendung eines einzigen, einheitlichen FastClusters für das gesamte Modell vorschreibt. Folglich erfordern Positionsaktualisierungen keine Clusterneubauten mehr, wodurch der Leistungsengpass gemindert wird. Diese Technik sollte ausschließlich für MeshParts mit erwarteter Bewegung reserviert werden, da sie zusätzlichen Speicheraufwand verursachen kann und die Vorteile der räumlichen Optimierung negiert. Wir empfehlen, Ihr Spiel immer nach solchen Änderungen zu profilieren. Weitere Informationen finden Sie unter Tipps zur Humanoid-Leistung.
Zu viele Teile in einem Model - Zu viele Teile in einem Modell könnten aufgrund der Potenzialität, dass sich eine Eigenschaft eines Teils ändert, das zu einem vollständigen Neubau führt, zu oft zu Neubauten führen. Finden Sie das richtige Gleichgewicht an Teilen in einem Modell, wenn es FastCluster verwendet.
Minderung
Instanziieren identischer Meshes und Reduzierung der Anzahl einzigartiger Meshes - Wenn Sie sicherstellen, dass alle identischen Meshes die gleichen zugrunde liegenden Asset-IDs haben, kann die Engine diese erkennen und in einem einzigen Draw-Call rendern. Stellen Sie sicher, dass Sie jedes Mesh in einer Karte nur einmal hochladen und anschließend im Studio für die Wiederverwendung duplizieren, anstatt große Karten als Ganzes zu importieren, was dazu führen könnte, dass identische Meshes separate Content-IDs haben und von der Engine als einzigartige Assets erkannt werden. Pakete sind ein hilfreicher Mechanismus für die Wiederverwendung von Objekten.
Culling - Culling beschreibt den Prozess, Zeichnungsaufrufe für Objekte zu eliminieren, die nicht in das endgültige gerenderte Frame einfließen. Standardmäßig überspringt die Engine Zeichnungsaufrufe für Objekte außerhalb des Sichtfelds der Kamera (Frustum-Culling) und Teile, Meshes und Terrain, die von anderen Objekten verdeckt sind (Occlusion-Culling). In bestimmten Szenarien, wie Innenumgebungen, könnten Sie in der Lage sein, ein Zimmer- oder Portalssystem zu implementieren und Objekte manuell zu cull, um die Draw-Calls oder die gesamte Rechenlast weiter zu reduzieren.
Verringern des Detailgrads für Modelle - Aktivieren Sie Instanz-Streaming und setzen Sie die LevelOfDetail-Eigenschaft Ihrer Weltmodelle auf SLIM, um optimierte leichte SLIM-Meshes für Modelle zu rendern, während sich der Abstand zur Kamera erhöht.
Verringern des Detailgrads für Avatare - Aktivieren Sie Instanz-Streaming und setzen Sie die Workspace.EnableSLIMAvatars-Eigenschaft, um Plattform-Avatar-Modelle mit beliebig vielen Accessoires oder Kleidungsschichten als optimierte leichte Avatar-Repräsentationen mit vollständiger Unterstützung für Animationen darzustellen, während sich der Abstand zur Kamera erhöht.
Verringern der Renderingtreue - Setzen Sie MeshPart.RenderFidelity auf Automatic oder Performance. Dies ermöglicht es den Meshes, auf weniger komplexe Alternativen zurückzugreifen, wodurch die Anzahl der Polygone verringert werden kann, die gezeichnet werden müssen.
Deaktivieren der Schattenwurf auf geeigneten Teilen und Lichtobjekten - Die Roblox-Engine verringert automatisch die Schattenqualität, während das Grafikqualitätsniveau des Clients sinkt, wobei Schatten letztendlich bei Qualitätsniveaus unter 4 vollständig deaktiviert werden. Sie können jedoch selektiv die Schattenwurf-Eigenschaften auf Lichtobjekten und Teilen deaktivieren, um die Leistung zu verbessern, während Schatten aktiviert sind, und die Wahrscheinlichkeit erhöhen, dass Schatten weiterhin aktiviert bleiben. Einige Beispiele für Optimierungen, die Sie entweder beim Bearbeiten oder dynamisch zur Laufzeit vornehmen können:
Verwenden Sie die BasePart.CastShadow-Eigenschaft, um den Schattenwurf auf kleinen Teilen zu deaktivieren, wo Schatten wahrscheinlich nicht sichtbar sind. Diese Strategie ist besonders effektiv, wenn sie auf Teile angewendet wird, die weit entfernt von der Kamera des Benutzers sind.
Deaktivieren Sie Schatten bei beweglichen Objekten, wenn möglich.
Deaktivieren Sie Light.Shadows bei Lichtinstanzen, bei denen das Objekt keine Schatten werfen muss.
Begrenzen Sie die Reichweite und den Winkel von Lichtinstanzen.
Verwenden Sie weniger Lichtinstanzen.
Erwägen Sie, Lichter, die sich außerhalb eines bestimmten Bereichs befinden, oder auf Zimmerbasis in Innenumgebungen zu deaktivieren.
MicroProfiler-Scopes
| Scope | Assoziierte Berechnung |
| Prepare and Perform | Gesamtes Rendering |
| Perform/Scene/computeLightingPerform | Lichtgitter- und Schattenaktualisierungen |
| LightGridCPU | Aktualisierungen des Voxel-Lichtgitters |
| ShadowMapSystem | Schattenmapping |
| Perform/Scene/UpdateView | Vorbereitung für Rendering und Partikelaktualisierungen |
| Perform/Scene/RenderView | Rendering und Nachbearbeitung |
Netzwerk und Replikation
Netzwerk und Replikation beschreiben den Prozess, durch den Daten zwischen dem Server und verbundenen Clients gesendet werden. Informationen werden in jedem Frame zwischen Client und Server gesendet, aber größere Datenmengen erfordern mehr Rechenzeit.
Häufige Probleme
Exzessiver Remote-Verkehr - Das Senden einer großen Menge von Daten über RemoteEvent oder RemoteFunction-Objekte oder das häufige Aufrufen dieser kann zu einer großen Menge CPU-Zeit führen, die für die Verarbeitung eingehender Pakete in jedem Frame aufgewendet wird. Häufige Fehler sind:
- Replikation von Daten in jedem Frame, die nicht repliziert werden müssen.
- Replikation von Daten auf Benutzerinteraktionen ohne Mechanismus zur Drosselung.
- Das Senden von mehr Daten als erforderlich. Zum Beispiel das Senden des gesamten Inventars des Spielers, wenn er einen Artikel kauft, anstatt nur die Einzelheiten des gekauften Artikels zu senden.
Erstellung oder Entfernung komplexer Instanzenbäume - Wenn eine Änderung am Datenmodell auf dem Server vorgenommen wird, wird diese an die verbundenen Clients repliziert. Das bedeutet, dass die Erstellung und Zerstörung großer Instanzhierarchien wie Karten zur Laufzeit sehr netzwerkintensiv sein kann.
Ein häufiger Übeltäter hier sind die komplexen Animationsdaten, die von Animation Editor-Plugins in Riggs gespeichert werden. Wenn diese nicht vor der Veröffentlichung des Spiels entfernt werden und das animierte Modell regelmäßig geklont wird, wird eine große Menge an Daten unnötig repliziert.
Serverseitiger TweenService - Wenn TweenService verwendet wird, um ein Objekt serverseitig zu tween, wird die tweened Eigenschaft in jedem Frame an jeden Client repliziert. Dies führt nicht nur dazu, dass das Tween ruckelt, während die Latenz der Clients schwankt, sondern verursacht auch viel unnötigen Netzwerkverkehr.
Minderung
Sie können die folgenden Taktiken anwenden, um unnötige Replikation zu reduzieren:
- Vermeiden Sie es, große Datenmengen auf einmal über entfernte Ereignisse zu senden. Senden Sie stattdessen nur notwendige Daten mit einer geringeren Frequenz. Beispielsweise replizieren Sie den Zustand eines Charakters, wenn er sich ändert, anstatt in jedem Frame.
- Teilen Sie komplexe Instanzenbäume wie Karten auf und laden Sie sie in Stücke, um die Arbeit über mehrere Frames zu verteilen.
- Bereinigen Sie Animationsmetadaten, insbesondere das Animationsverzeichnis von Riggs, nach dem Import.
- Begrenzen Sie unnötige Instanzreplikation, insbesondere in Fällen, in denen der Server keine Kenntnis von den erstellten Instanzen haben muss. Dazu gehören:
- Visuelle Effekte wie eine Explosion oder einen magischen Zauber. Der Server muss nur den Standort kennen, um das Ergebnis zu bestimmen, während die Clients die visuellen Effekte lokal erstellen können.
- Ansichtmodelle von Gegenständen in der Ego-Perspektive.
- Tween-Objekte auf dem Client anstelle des Servers.
MicroProfiler-Scopes
| Scope | Assoziierte Berechnung |
| ProcessPackets | Verarbeitung eingehender Netzwerkpakete, wie z. B. Ereignisaufrufe und Eigenschaftsänderungen |
| Allocate Bandwidth and Run Senders | Ausgehende Ereignisse, die auf Servern relevant sind |
Asset-Speicherverbrauch
Der leistungsstärkste Mechanismus, der Erstellern zur Verfügung steht, um die Speichernutzung des Clients zu verbessern, ist die Aktivierung des Instanz-Streaming.
Instanz-Streaming
Instanz-Streaming lädt Teile des Datenmodells selektiv aus, die nicht erforderlich sind, was zu erheblich reduzierten Ladezeiten führen und die Fähigkeit des Clients erhöhen kann, Abstürze zu verhindern, wenn er unter Druck steht.
Wenn Sie auf Speicherprobleme stoßen und das Instanz-Streaming deaktiviert ist, ziehen Sie in Betracht, Ihr Spiel zu aktualisieren, um es zu unterstützen, insbesondere wenn Ihre 3D-Welt groß ist. Instanz-Streaming basiert auf dem Abstand im 3D-Raum, sodass größere Welten natürlicherweise stärker davon profitieren.
Wenn das Instanz-Streaming aktiviert ist, können Sie die Aggressivität davon erhöhen. Ziehen Sie beispielsweise in Betracht:
- Verwenden von Enum.ModelStreamingMode.Persistent wo möglich. Sie müssen möglicherweise Ihre Skripte aktualisieren, wenn Sie es als Kompatibilitätsmaßnahme verwenden.
- Reduzieren Sie den Wert von Workspace.StreamingMinRadius und Workspace.StreamingTargetRadius.
Für weitere Informationen zu Streaming-Optionen und deren Vorteilen siehe Streaming-Eigenschaften.
Weitere häufige Probleme
Asset-Duplikation - Ein häufiger Fehler besteht darin, dass dasselbe Asset mehrfach hochgeladen wird, was zu unterschiedlichen Asset-IDs führt. Dies kann dazu führen, dass derselbe Inhalt mehrere Male in den Speicher geladen wird.
Exzessives Asset-Volumen - Selbst wenn Assets nicht identisch sind, gibt es Fälle, in denen Chancen zur Wiederverwendung desselben Assets und zur Einsparung von Speicher verpasst werden.
Audiodateien - Audiodateien können überraschend zur Speichernutzung beitragen, insbesondere wenn Sie alle auf einmal in den Client laden, anstatt nur das, was Sie für einen Teil des Spiels benötigen. Für Strategien siehe Ladezeiten.
Hochauflösende Texturen - Der Verbrauch von Grafikspeicher für eine Textur ist nicht mit der Größe der Textur auf der Festplatte verbunden; die Anzahl der Pixel in der Textur bestimmt den Speicherverbrauch. Zum Beispiel verbraucht eine 1024x1024-Pixel-Textur viermal so viel Grafikspeicher wie eine 512x512-Pixel-Textur.
Bilder, die in Roblox hochgeladen werden, werden in ein festes Format umgewandelt, sodass es keinen Speichergewinn ergibt, Bilder in einem Farbschema hochzuladen, das weniger Bytes pro Pixel enthält. Ebenso kann das Komprimieren von Bildern vor dem Hochladen oder das Entfernen des Alphakanals von Bildern, die ihn nicht benötigen, die Bildgröße auf der Festplatte verringern, aber nicht die Speichernutzung verbessern.
Wenn ein Spiel geladen wird, beginnt die Engine automatisch mit niedrigeren Qualitätstexturen und steigert dann die Qualität basierend auf dem verfügbaren Gerätespeicher, der Entfernung zur Kamera, dem Anteil an Bildschirmraum, den die Textur einnimmt, und anderen Faktoren. Dennoch kann das strategische Dimensionieren Ihrer Texturen die Speichernutzung in Ihrem Spiel verbessern.
Minderung
Laden Sie Assets nur einmal hoch - Verwenden Sie die gleiche Asset-ID über Objekte hinweg und stellen Sie sicher, dass die gleichen Assets, insbesondere Meshes und Bilder, nicht mehrfach separat hochgeladen werden.
Finden und beheben Sie doppelte Assets - Suchen Sie identische Mesh-Teile und Texturen, die mehrmals mit unterschiedlichen IDs hochgeladen wurden.
- Obwohl es keine API gibt, um die Ähnlichkeit von Assets automatisch zu erkennen, können Sie alle Bild-Asset-IDs in Ihrem Platz sammeln (entweder manuell oder mit einem Skript), diese herunterladen und mithilfe von externen Vergleichstools vergleichen.
- Für Mesh-Teile ist die beste Strategie, einzigartige Mesh-IDs zu nehmen und sie nach Größe zu organisieren, um Duplikate manuell zu identifizieren.
- Anstatt separate Texturen für verschiedene Farben zu verwenden, laden Sie eine einzelne Textur hoch und verwenden Sie die Eigenschaft SurfaceAppearance.Color, um verschiedene Tönungen anzuwenden.
Importieren Sie Assets in Karten einzeln - Anstatt eine gesamte Karte auf einmal zu importieren, importieren und rekonstruieren Sie die Assets in der Karte individuell. Der Importer führt keine De-Duplizierung von Meshes durch, sodass, wenn Sie eine große Karte mit vielen separaten Fliesen importieren, jede dieser Fliesen als separates Asset importiert wird (selbst wenn sie Duplikate sind). Dies kann im Laufe der Zeit zu Leistungs- und Speicherproblemen führen, da jedes Mesh als Einzelnes behandelt wird und Speicher und Draw-Calls verbraucht.
Begrenzen Sie die Pixelanzahl von Bildern auf nicht mehr als die notwendige Menge. Sofern ein Bild keinen großen physischen Bereich auf dem Bildschirm einnimmt, benötigt es im Allgemeinen höchstens 512x512 Pixel. Die meisten kleinen Bilder sollten kleiner als 256x256 Pixel sein.
Verwenden Sie Trim-Sheets, um maximale Texturwiederverwendung in 3D-Karten sicherzustellen. Für Schritte und Beispiele, wie man Trim-Sheets erstellt, siehe Trim-Sheets erstellen.
Sie sollten auch in Betracht ziehen, Sprite-Sheets zu verwenden, um viele kleinere UI-Bilder als ein einzelnes Bild zu laden. Sie können dann ImageLabel.ImageRectOffset und ImageLabel.ImageRectSize verwenden, um Teile des Sheets anzuzeigen.
Ladezeiten
Viele Spiele implementieren benutzerdefinierte Ladebildschirme und verwenden die Methode ContentProvider:PreloadAsync(), um Assets anzufordern, damit Bilder, Geräusche und Meshes im Hintergrund heruntergeladen werden.
Der Vorteil dieses Ansatzes ist, dass er sicherstellt, dass wichtige Teile Ihres Spiels vollständig geladen sind, ohne Pop-ins. Ein häufiger Fehler ist jedoch, diese Methode übermäßig zu nutzen, um mehr Assets vorzuholen, als tatsächlich erforderlich sind.
Ein Beispiel für eine schlechte Praxis besteht darin, die gesamte Workspace zu laden. Obwohl dies das Textur-Pop-in verhindern kann, erhöht es erheblich die Ladezeiten.
Eine ähnliche Praxis besteht darin, die ContentProvider.RequestQueueSize zu verwenden, um sicherzustellen, dass alle angeforderten Assets das Laden abgeschlossen haben. Dies stellt jedoch dasselbe Problem von erheblich erhöhten Ladezeiten dar, während es aufgrund seiner schwankenden Natur auch eine unzuverlässige Methode ist.
Verwenden Sie stattdessen die Methode ContentProvider:PreloadAsync() nur in notwendigen Situationen, zu denen gehören:
- Bilder im Ladebildschirm.
- Wichtige Bilder in Ihrem Spielmenü, wie Schaltflächenhintergründe und -symbole.
- Wichtige Assets im Start- oder Spawnbereich.
Wenn Sie eine große Anzahl von Assets laden müssen, empfehlen wir, einen Laden überspringen-Button bereitzustellen.