Diese Seite beschreibt häufige Leistungsprobleme und bewährte Verfahren zu deren Minderung.
Skriptberechnung
Aufwendige Operationen im Luau-Code benötigen länger zur Verarbeitung und können somit die Bildrate beeinträchtigen. Sofern sie nicht parallel ausgeführt werden, läuft Luau-Code synchron 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 das Durchlaufen sehr großer Datenstrukturen beinhalten.
- Hochfrequente Ereignisse - Teure Operationen an framebasierten Ereignissen von RunService zu binden, ohne die Frequenz zu begrenzen, bedeutet, dass diese Operationen in jedem Frame wiederholt werden, was oft zu einer unnötigen Erhöhung der Berechnungszeit führt. Diese Ereignisse umfassen:
Minderung
- Rufen Sie Code bei RunService-Ereignissen sparsam auf und beschränken Sie die Nutzung auf Fälle, in denen eine hochfrequente Ausführung unerlässlich 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.
- Teilen Sie große oder teure Aufgaben auf, indem Sie task.wait() verwenden, 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 nativem Code-Generierung profitieren, einem einfachen Flag, das ein Skript in Maschinencode anstelle von Bytecode kompiliert.
- Wenn Ihre Server nach der Optimierung immer noch CPU-Ressourcen benötigen, können Sie mit Extended Services for Compute mehr reservieren.
MicroProfiler-Bereiche
| Bereich | 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 zum Debuggen von Skripten mit dem MicroProfiler siehe die debug-Bibliothek, die Funktionen zum Taggen spezifischer Codes und zur weiteren Spezifizierung enthält, wie debug.profilebegin und debug.profileend. Viele Roblox-API-Methoden, die von Skripten aufgerufen werden, haben ebenfalls ihre eigenen zugehörigen MicroProfiler-Tags, die nützliche Signale liefern können.
Skriptspeicherverbrauch
Speicherlecks können auftreten, wenn Sie Skripte schreiben, die Speicher verbrauchen, den der Garbage Collector nicht ordnungsgemäß freigeben kann, wenn er nicht mehr verwendet wird. Lecks sind insbesondere auf dem Server weit verbreitet, da sie viele Tage online sein 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 Anzahl von Instanzen deutet darauf hin, dass Referenzen auf einige Instanzen in Ihrem Code nicht vom Garbage Collector gesammelt werden.
- PlaceScriptMemory - Bietet eine skriptweise Aufschlüsselung des Speicherverbrauchs.
Häufige Probleme
Verbindungen verbunden lassen - Die Engine sammelt niemals Ereignisse, die mit einer Instanz verbunden sind, und alle Werte, die innerhalb des verbundenen Rückrufs referenziert werden. Daher sind aktive Verbindungen von Ereignissen und Code innerhalb der verbundenen Instanzen, verbundene Funktionen und referenzierte Werte für den Speicher-Garbage-Collector außerhalb des Geltungsbereichs, 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 auch für Player-Objekte gilt. Nachdem ein Benutzer ein Spiel verlässt, zerstört die Engine nicht automatisch ihr repräsentatives Player-Objekt und Charaktermodell, sodass Verbindungen zu dem Player-Objekt und Instanzen unter dem Charaktermodell, wie CharacterAdded, weiterhin Speicher verbrauchen, wenn Sie sie in Ihren Skripten nicht trennen. Dies kann im Laufe der Zeit zu sehr erheblichen Speicherlecks auf dem Server führen, da Hunderte von Benutzern dem Spiel beitreten und es verlassen.
Tabellen - Objekte in Tabellen einzufügen, aber sie nicht zu entfernen, wenn sie nicht mehr benötigt werden, verursacht unnötigen Speicherverbrauch, insbesondere für Tabellen, die Benutzerdaten verfolgen, wenn sie beitreten. Zum Beispiel erstellt das folgende Codebeispiel eine Tabelle, die bei jedem Beitritt eines Benutzers Informationen hinzufügt:
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 weiter in der Größe und verbraucht mehr Speicher, während mehr Benutzer der Sitzung beitreten. Jeder Code, der über diese Tabelle iteriert, wird ebenfalls rechenintensiver, je größer die Tabelle wird.
Minderung
Um alle verwendeten Werte zur Vermeidung von Speicherlecks zu bereinigen:
Trennen Sie alle Verbindungen - Gehen Sie durch Ihren Code und stellen Sie sicher, dass jede Verbindung über einen der folgenden Wege 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.
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 möchten, können Sie sie stattdessen manuell bereinigen:
Beispiel für die 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
Übermäßige Physiksimulation kann eine Hauptursache für erhöhte Berechnungszeit pro Frame sowohl auf dem Server als auch auf dem Client sein.
Häufige Probleme
Übermäßige Frequenz der Physik-Zeitstempel - Standardmäßig erfolgt das Stepping-Verhalten im adaptiven Modus, bei dem die Physik mit 60 Hz, 120 Hz oder 240 Hz schrittweise erfolgt, abhängig von der Komplexität des Physikmechanismus.
Ein fester Modus mit verbesserter Genauigkeit der Physik ist ebenfalls verfügbar, der alle Physikbaugruppen zwingt, mit 240 Hz (viermal pro Frame) zu schrittweise. Dies führt zu erheblich mehr Berechnungen in jedem Frame.
Übermäßige Anzahl von komplexen simulierten Objekten - Je mehr 3D-Baugruppen simuliert werden, desto länger dauern die Physikberechnungen in jedem Frame. Oft haben Spiele Objekte, die simuliert werden, die nicht simuliert werden müssen, oder sie haben Mechanismen, die mehr Einschränkungen und Gelenke haben, als sie benötigen.
Übermäßig präzise Kollisionserkennung - Mesh-Teile haben eine CollisionFidelity-Eigenschaft zur Erkennung von Kollisionen, die eine Vielzahl von Modi mit unterschiedlichen Leistungsbeeinträchtigungen bietet. Der präzise Kollisionserkennungsmodus für Mesh-Teile hat die teuersten Leistungskosten und benötigt länger zur Berechnung durch die Engine.
Minderung
Verankern Sie Teile, die keine Simulation benötigen - Verankern Sie alle Teile, die nicht von der Physik gesteuert werden müssen, wie statische NPCs.
Verwenden Sie adaptives Physik-Stepping - Adaptives Stepping passt die Rate der Physikberechnungen für Physikmechanismen dynamisch an, sodass Physikaktualisierungen in einigen Fällen seltener vorgenommen werden können.
Reduzieren Sie die Komplexität von Mechanismen
- Minimieren Sie, wo möglich, die Anzahl der physikalischen Einschränkungen oder Gelenke in einer Baugruppe.
- Reduzieren Sie die Menge an Selbstkollision innerhalb eines Mechanismus, indem Sie beispielsweise Einschränkungen oder keine Kollisionen auf Ragdoll-Gliedmaßen anwenden, um zu verhindern, dass sie miteinander kollidieren.
Reduzieren Sie die Verwendung von 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.
Verwenden Sie für kleine bis mittelgroße Objekte je nach Form Box- oder Hüllentreue.
Erstellen Sie für große und sehr komplexe Objekte benutzerdefinierte Kollisionen mit unsichtbaren Teilen, wenn möglich.
Für Objekte, die keine Kollisionen erfordern, deaktivieren Sie Kollisionen und verwenden Sie Box- oder Hüllentreue, da die Kollisionsgeometrie weiterhin im Speicher gespeichert wird.
Sie können die Kollisionsgeometrie zu Debugging-Zwecken im Studio anzeigen, indem Sie Kollisionsgenauigkeit im Widget Visualisierungsoptionen in der oberen rechten Ecke des 3D-Viewport aktivieren.
Alternativ können Sie den Filter CollisionFidelity=PreciseConvexDecomposition auf den Explorer anwenden, der eine Zählung aller Mesh-Teile mit der präzisen Treue anzeigt und es Ihnen ermöglicht, sie leicht auszuwählen.
Für eine detaillierte Anleitung zur Auswahl einer Kollisionstreue-Option, die Ihre Präzisions- und Leistungsanforderungen ausbalanciert, siehe Setzen von Physik- und Renderingparametern.
MicroProfiler-Bereiche
| Bereich | Assoziierte Berechnung |
| physicsStepped | Gesamte Physikberechnung |
| worldStep | Diskrete Physikschritte, die in jedem Frame durchgeführt werden |
Physikspeicherverbrauch
Die Bewegung und Kollisionserkennung der Physik verbraucht Speicher. Mesh-Teile haben eine CollisionFidelity-Eigenschaft, die den Ansatz bestimmt, der verwendet wird, um die Kollisionsgrenzen des Meshs zu bewerten.
Häufiges Problem
Die Standard- und präzisen Kollisionserkennungsmodi verbrauchen erheblich mehr Speicher als die beiden anderen Modi mit niedrigeren Kollisionstreueformen.
Wenn Sie hohe Speicherverbrauchswerte unter PhysicsParts sehen, müssen Sie möglicherweise die Kollisionstreue von Objekten in Ihrem Spiel reduzieren.
Wie man mildert
Um den Speicherverbrauch für Kollisionstreue zu reduzieren:
- Für Teile, die keine Kollisionen benötigen, deaktivieren Sie deren Kollisionen, indem Sie BasePart.CanCollide, BasePart.CanTouch und BasePart.CanQuery auf false setzen.
- Reduzieren Sie die Treue von Kollisionen mit der Einstellung CollisionFidelity. Box hat die geringsten Speicherüberhead, und Default und Precise sind im Allgemeinen teurer.
- Es ist im Allgemeinen sicher, die Kollisionstreue eines kleinen verankerten Teils auf Box zu setzen.
- Für sehr komplexe große Meshes möchten Sie möglicherweise Ihr eigenes Kollisionsmesh aus kleineren Objekten mit Box-Kollisionsgenauigkeit erstellen.
Humanoide
Humanoid ist eine Klasse, die eine Vielzahl von Funktionen für Spieler- und Nicht-Spieler-Charaktere (NPCs) bereitstellt. Obwohl sie leistungsstark ist, bringt ein Humanoid erhebliche Berechnungskosten mit sich.
Häufige Probleme
- Alle HumanoidStateTypes bei NPCs aktiviert lassen - Es gibt Kosten für die Leistung, wenn bestimmte HumanoidStateTypes aktiviert bleiben. Deaktivieren Sie alle, die für Ihre NPCs nicht benötigt werden. Zum Beispiel, es ist sicher, den Zustand Climbing zu deaktivieren, es sei denn, Ihr NPC wird Leitern erklimmen.
- Instanziieren, Modifizieren und Respawnen von Modellen mit Humanoids oder skinnierten MeshParts häufig
- Dies kann für die Engine aufwendig sein, insbesondere wenn diese Modelle geschichtete Kleidung verwenden. Dies kann auch besonders problematisch in Spielen sein, in denen Avatare häufig respawnen.
- Im MicroProfiler sind lange updateInvalidatedFastClusters-Tags (über 4 ms) oft ein Signal dafür, 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 auf 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 Überlastung verursachen.
- Unnötige Größen- und Maßänderungen durchführen - Größen-/Maßänderungen führen dazu, dass FastCluster neu aufgebaut werden. Versuchen Sie, dies während des Spiels zu reduzieren, wenn Sie Leistungsprobleme im Zusammenhang mit FastCluster feststellen. Ähnliche Änderungen an anderen Eigenschaften können ebenfalls dazu führen, dass FastCluster neu aufgebaut wird, daher sollten Sie diese Änderungen im Allgemeinen so weit wie möglich reduzieren.
Minderung
- NPC-Animationen auf dem Client abspielen - In Spielen mit einer großen Anzahl von NPCs sollten Sie in Betracht ziehen, den Animator auf dem Client zu erstellen und die Animationen lokal auszuführen. Dies reduziert die Belastung des Servers und die Notwendigkeit für unnötige Replikation. Es ermöglicht auch zusätzliche Optimierungen (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 unbedingt 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 in Betracht ziehen, Ihren eigenen Bewegungscontroller zu implementieren und je nach Komplexität Ihrer NPCs einen AnimationController für Animationen zu verwenden.
- Deaktivieren Sie ungenutzte Humanoid-Zustände - Verwenden Sie Humanoid:SetStateEnabled(), um nur notwendige Zustände für jeden Humanoid zu aktivieren.
- Poolen Sie NPC-Modelle mit häufigem Respawn - Anstatt einen NPC vollständig zu zerstören, senden Sie den NPC in einen Pool inaktiver NPCs. Auf diese Weise können Sie, wenn ein neuer NPC respawnen muss, einfach einen der NPCs aus dem Pool reaktivieren. Dieser Prozess wird Pooling genannt, was die Anzahl der Instanziierungen von Charakteren minimiert.
- Spawn Sie NPCs nur, wenn Benutzer in der Nähe sind - Spawn Sie NPCs nicht, wenn Benutzer nicht in Reichweite sind, und entfernen Sie sie, wenn Benutzer ihre Reichweite verlassen.
- Vermeiden Sie Änderungen an der Avatar-Hierarchie, nachdem sie instanziiert wurde - Bestimmte Änderungen an einer Avatar-Hierarchie 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 Motor6D.Transform-Eigenschaft.
MicroProfiler-Bereiche
| Bereich | Assoziierte Berechnung |
| stepHumanoid | Humanoid-Steuerung und Physik |
| stepAnimation | Humanoid- und Animator-Animation |
| updateInvalidatedFastClusters | Assoziiert mit der Instanziierung oder Modifikation eines Avatars |
Rendering
Ein erheblicher Teil der Zeit, die der Client in jedem Frame verbringt, wird für das Rendern der Szene im aktuellen Frame verwendet. Der Server führt kein Rendering durch, daher ist dieser Abschnitt ausschließlich für den Client.
Draw Calls
Ein Draw Call ist eine Reihe von Anweisungen von der Engine an die GPU, um etwas zu rendern. Draw Calls haben erhebliche Overheadkosten. 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 derzeit stattfinden, mit dem Element Render Stats ⟩ Timing im Studio. 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 Instanzierung, um identische Meshes mit denselben Texturmerkmalen in einen einzigen Draw Call zu konsolidieren. Insbesondere werden mehrere Meshes mit demselben MeshContent in einem einzigen Draw Call behandelt, wenn:
- SurfaceAppearances identisch sind, wenn vorhanden, andernfalls, wenn TextureContents identisch sind.
- Materialien identisch sind, wenn sowohl SurfaceAppearance als auch MeshPart.TextureID nicht existieren.
Weitere häufige Probleme
Übermäßige Objekt-Dichte - 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 einen bestimmten Teil 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 lassen sich nicht gut bündeln und führen zu zusätzlichen Draw Calls. Achten Sie besonders auf diese Objekttypen in einer Szene. Insbesondere Änderungen an den Eigenschaften von ParticleEmitters können erhebliche Auswirkungen auf die Leistung haben.
Verpasste Instanzierungsmöglichkeiten - Oft enthält eine Szene dasselbe Mesh, das mehrmals dupliziert wird, aber jede Kopie des Mesh hat unterschiedliche Mesh- oder Textur-Asset-IDs. Dies verhindert die Instanzierung und kann zu unnötigen Draw Calls führen.
Eine häufige Ursache für dieses Problem ist, wenn eine gesamte Szene auf einmal importiert wird, anstatt einzelne Assets in Roblox zu importieren und sie dann 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 so aussehen. Wiederholte Zeilen deuten auf die Wiederverwendung desselben Meshs hin, was gut ist. Einzigartige Zeilen sind nicht unbedingt schlecht, könnten aber je nach Ihrem Namensschema auf doppelte Meshes in Ihrem Spiel hinweisen:
LargeRock, rbxassetid://106420009602747 (x144) -- gutLargeRock, rbxassetid://120109824668127LargeRock, rbxassetid://134460273008628LargeRock, rbxassetid://139288987285823LargeRock, rbxassetid://71302144984955LargeRock, rbxassetid://90621205713698LargeRock, rbxassetid://113160939160788LargeRock, rbxassetid://135944592365226 -- alle möglichen DuplikateÜbermäßige 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 Anzahl von sehr komplexen Meshes sind ein häufiges Problem, ebenso wie Szenen mit der MeshPart.RenderFidelity-Eigenschaft, die auf Precise bei zu vielen Meshes gesetzt ist.
Übermäßiges Schattenwerfen - Die Handhabung von Schatten ist ein kostspieliger Prozess, und Karten, die eine hohe Anzahl und Dichte von Lichtobjekten enthalten, die Schatten werfen (oder eine hohe Anzahl und Dichte von kleinen Teilen, die von Schatten beeinflusst werden), können Leistungsprobleme haben.
Hohe Transparenzüberzeichnung - Das Platzieren von Objekten mit partieller Transparenz in der Nähe zueinander zwingt die Engine, die überlappenden Pixel mehrfach zu rendern, was die Leistung beeinträchtigen kann. Für weitere Informationen zur Identifizierung und Behebung dieses Problems siehe Layered Transparencies löschen.
Unnötige Bewegung von skinnierten MeshParts - Skinierte MeshParts, die Teil eines Modells ohne Humanoid sind, werden mithilfe von räumlich organisierten FastClusters gruppiert. Wenn sich diese MeshParts bewegen, müssen sie kontinuierlich zu und von diesen räumlichen Clustern hinzugefügt und entfernt werden, was dazu führt, dass die Cluster neu aufgebaut werden und die Leistung beeinträchtigt wird.
- Eine sehr effektive Lösung besteht darin, einen Humanoid innerhalb des Modells einzubetten. Die Anwesenheit eines Humanoids überschreibt das standardmäßige räumliche Clustering-Verhalten und zwingt die Verwendung eines einzigen, einheitlichen FastClusters für das gesamte Modell. Folglich erfordern Positionsaktualisierungen keine Clusterneubauten mehr, wodurch der Leistungsengpass gemildert wird. Diese Technik sollte ausschließlich für MeshParts mit erwarteter Bewegung reserviert werden, da sie zusätzlichen Speicheraufwand verursachen und die Vorteile der räumlichen Optimierung negieren kann. Wir empfehlen, Ihr Spiel nach solchen Änderungen immer zu profilieren. Siehe Humanoid-Leistungstipps für weitere Informationen.
Zu viele Teile in einem Model - Zu viele Teile in einem Modell könnten aufgrund der Möglichkeit, dass sich die Eigenschaften eines Teils ändern, was einen vollständigen Neubau erfordert, häufiger zu Neubauten führen. Finden Sie das richtige Gleichgewicht der Teile in einem Modell, wenn es FastCluster verwendet.
Minderung
- Instanzierung identischer Meshes und Reduzierung der Anzahl einzigartiger Meshes - Wenn Sie sicherstellen, dass alle identischen Meshes dieselben zugrunde liegenden Asset-IDs haben, kann die Engine sie erkennen und in einem einzigen Draw Call rendern. Stellen Sie sicher, dass Sie jedes Mesh in einer Karte nur einmal hochladen und dann im Studio duplizieren, um es wiederzuverwenden, anstatt große Karten als Ganzes zu importieren, was dazu führen könnte, dass identische Meshes separate Inhalts-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, Draw Calls für Objekte zu eliminieren, die nicht in den endgültigen gerenderten Frame einfließen. Standardmäßig überspringt die Engine Draw Calls für Objekte außerhalb des Sichtfelds der Kamera (Frustum-Culling) und Teile, Meshes und Terrain, die von anderen Objekten verdeckt werden (Occlusion-Culling). In bestimmten Szenarien, wie z.B. in Innenräumen, könnten Sie in der Lage sein, ein Raum- oder Portal-System zu implementieren und Objekte manuell zu cullieren, um die Draw Calls oder die gesamte Rechenlast weiter zu reduzieren.
- Reduzierung des Detailgrads für Modelle - Aktivieren Sie Instanz-Streaming und setzen Sie die LevelOfDetail-Eigenschaft Ihrer Weltmodelle auf SLIM, um optimierte, leichte SLIM-Meshes zu rendern, wenn die Entfernung zur Kamera zunimmt.
- Reduzierung des Detailgrads für Avatare - Aktivieren Sie das Instanz-Streaming und setzen Sie Workspace.EnableSLIMAvatars, um Plattformavatare als optimierte, leichte SLIM-Darstellungen mit vollständiger Animationsunterstützung zu rendern, wenn die Entfernung zur Kamera zunimmt.
- Reduzierung der Rendergenauigkeit - Setzen Sie MeshPart.RenderFidelity auf Automatic oder Performance. Dies ermöglicht es Meshes, auf weniger komplexe Alternativen zurückzugreifen, was die Anzahl der Polygone reduzieren kann, die gezeichnet werden müssen.
- Deaktivieren von Schattenwurf auf geeigneten Teilen und Lichtobjekten - Die Roblox-Engine verringert automatisch die Schattenqualität, wenn die Grafikqualität des Clients abnimmt, und deaktiviert schließlich die Schatten vollständig bei Qualitätsstufen unter 4. 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 zu erhöhen, dass Schatten aktiviert bleiben. Einige Beispiele für Optimierungen, die Sie entweder zur Bearbeitungszeit oder dynamisch zur Laufzeit vornehmen können:
Verwenden Sie die BasePart.CastShadow-Eigenschaft, um den Schattenwurf auf kleinen Teilen zu deaktivieren, bei denen Schatten wahrscheinlich nicht sichtbar sind. Diese Strategie ist besonders effektiv, wenn sie auf Teile angewendet wird, die weit vom Kamera des Benutzers entfernt sind.
Deaktivieren Sie Schatten auf beweglichen Objekten, wenn möglich.
Deaktivieren Sie Light.Shadows auf Lichtinstanzen, bei denen das Objekt keine Schatten werfen muss.
Begrenzen Sie den Bereich und den Winkel von Lichtinstanzen.
Verwenden Sie weniger Lichtinstanzen.
Ziehen Sie in Betracht, Lichter, die außerhalb eines bestimmten Bereichs liegen oder von Raum zu Raum in Innenräumen, zu deaktivieren.
MicroProfiler-Bereiche
| Bereich | Assoziierte Berechnung |
| Prepare and Perform | Gesamtes Rendering |
| Perform/Scene/computeLightingPerform | Lichtgitter- und Schattenaktualisierungen |
| LightGridCPU | Voxel-Lichtgitteraktualisierungen |
| ShadowMapSystem | Schattenmapping |
| Perform/Scene/UpdateView | Vorbereitung für Rendering und Partikelaktualisierungen |
| Perform/Scene/RenderView | Rendering und Nachbearbeitung |
Netzwerk und Replikation
Netzwerk und Replikation beschreibt den Prozess, durch den Daten zwischen dem Server und verbundenen Clients gesendet werden. Informationen werden zwischen dem Client und dem Server in jedem Frame gesendet, aber größere Datenmengen erfordern mehr Rechenzeit.
Häufige Probleme
Übermäßiger Remote-Verkehr - Das Senden einer großen Menge an Daten über RemoteEvent oder RemoteFunction-Objekte oder deren sehr häufige Aufrufe können dazu führen, dass eine große Menge an CPU-Zeit 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 bei Benutzereingaben ohne einen Mechanismus zur Drosselung.
- Versenden von mehr Daten als erforderlich. Zum Beispiel das Senden des gesamten Inventars des Spielers, wenn er einen Gegenstand kauft, anstatt nur die Einzelheiten des gekauften Gegenstands.
Erstellung oder Entfernung komplexer Instanzbäume - Wenn eine Änderung am Datenmodell auf dem Server vorgenommen wird, wird sie an verbundene Clients repliziert. Das Erstellen und Zerstören großer Instanzhierarchien wie Karten zur Laufzeit kann sehr netzwerkintensiv sein.
Ein häufiger Übeltäter hier sind die komplexen Animationsdaten, die von Animation Editor-Plugins in Riggs gespeichert werden. Wenn diese nicht entfernt werden, bevor das Spiel veröffentlicht wird, 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, wenn 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 Remote-Ereignisse zu senden. Senden Sie stattdessen nur notwendige Daten mit einer niedrigeren Frequenz. Zum Beispiel, replizieren Sie den Zustand eines Charakters, wenn er sich ändert, anstatt in jedem Frame.
- Teilen Sie komplexe Instanzbäume wie Karten auf und laden Sie sie in Stücken, um die Arbeit der Replikation ü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.
- Modelle für die Ansicht von Gegenständen aus der ersten Person.
- Tween-Objekte auf dem Client anstelle des Servers.
MicroProfiler-Bereiche
| Bereich | Assoziierte Berechnung |
| ProcessPackets | Verarbeitung eingehender Netzwerkpakete, wie Ereignisaufrufe und Eigenschaftsänderungen |
| Allocate Bandwidth and Run Senders | Ausgehende Ereignisse, die auf Servern relevant sind |
Asset-Speicherverbrauch
Der Mechanismus mit dem größten Einfluss, den Ersteller zur Verbesserung des Speicherverbrauchs des Clients nutzen können, ist die Aktivierung des Instanz-Streamings.
Instanz-Streaming
Instanz-Streaming lädt selektiv Teile des Datenmodells, die nicht benötigt werden, was zu erheblich verkürzten Ladezeiten führen kann und die Fähigkeit des Clients erhöht, Abstürze zu verhindern, wenn er unter Speicherbelastung steht.
Wenn Sie auf Speicherprobleme stoßen und das Instanz-Streaming deaktiviert ist, sollten Sie in Betracht ziehen, Ihr Spiel zu aktualisieren, um es zu unterstützen, insbesondere wenn Ihre 3D-Welt groß ist. Instanz-Streaming basiert auf der Entfernung im 3D-Raum, sodass größere Welten natürlicherweise mehr davon profitieren.
Wenn das Instanz-Streaming aktiviert ist, können Sie die Aggressivität erhöhen. Zum Beispiel sollten Sie in Betracht ziehen:
- Die Verwendung von Enum.ModelStreamingMode.Persistent wo möglich zu reduzieren. Möglicherweise müssen Sie Ihre Skripte aktualisieren, wenn Sie es als Kompatibilitätsmaßnahme verwenden.
- Den Workspace.StreamingMinRadius und Workspace.StreamingTargetRadius zu reduzieren.
Für weitere Informationen zu Streaming-Optionen und deren Vorteilen siehe Streaming-Eigenschaften.
Weitere häufige Probleme
Asset-Duplikation - Ein häufiger Fehler ist es, dass dasselbe Asset mehrfach hochgeladen wird, was zu unterschiedlichen Asset-IDs führt. Dies kann dazu führen, dass derselbe Inhalt mehrfach in den Speicher geladen wird.
Übermäßiges Asset-Volumen - Selbst wenn Assets nicht identisch sind, gibt es Fälle, in denen Gelegenheiten zur Wiederverwendung desselben Assets und zur Einsparung von Speicher verpasst werden.
Audio-Dateien - Audio-Dateien können einen überraschenden Beitrag zum Speicherverbrauch leisten, 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 Grafikspeicherverbrauch für eine Textur ist unabhängig von der Größe der Textur auf der Festplatte; die Anzahl der Pixel in der Textur bestimmt den Speicherverbrauch. Zum Beispiel verbraucht eine 1024x1024 Pixel große Textur viermal so viel Grafikspeicher wie eine 512x512 Textur.
Bilder, die in Roblox hochgeladen werden, werden in ein festes Format umgewandelt, sodass es keinen Speichergewinn gibt, wenn Bilder in einem Farbmodell hochgeladen werden, das mit weniger Bytes pro Pixel verbunden ist. Ebenso kann das Komprimieren von Bildern vor dem Hochladen oder das Entfernen des Alpha-Kanals von Bildern, die ihn nicht benötigen, die Bildgröße auf der Festplatte verringern, verbessert jedoch nicht den Speicherverbrauch.
Wenn ein Spiel geladen wird, beginnt die Engine automatisch mit niedrigeren Qualitätstexturen und erhöht dann die Qualität basierend auf dem verfügbaren Gerätespeicher, der Entfernung zur Kamera, der Menge an Bildschirmfläche, die die Textur einnimmt, und anderen Faktoren. Dennoch kann das strategische Anpassen Ihrer Texturen den Speicherverbrauch in Ihrem Spiel verbessern.
Minderung
Laden Sie Assets nur einmal hoch - Verwenden Sie dieselbe Asset-ID über Objekte hinweg und stellen Sie sicher, dass dieselben Assets, insbesondere Meshes und Bilder, nicht mehrfach separat hochgeladen werden.
Finden und beheben Sie doppelte Assets - Suchen Sie nach identischen Mesh-Teilen und Texturen, die mehrfach mit unterschiedlichen IDs hochgeladen wurden.
- Obwohl es keine API gibt, um Ähnlichkeiten von Assets automatisch zu erkennen, können Sie alle Bild-Asset-IDs in Ihrem Ort sammeln (entweder manuell oder mit einem Skript), sie herunterladen und mit 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 einzige Textur hoch und verwenden Sie die SurfaceAppearance.Color-Eigenschaft, um verschiedene Farbtöne anzuwenden.
Importieren Sie Assets in der Karte separat - Anstatt eine gesamte Karte auf einmal zu importieren, importieren und rekonstruieren Sie die Assets in der Karte einzeln. Der Importer führt keine Duplikation von Meshes durch, sodass, wenn Sie eine große Karte mit vielen separaten Fliesen importieren, jede dieser Fliesen als separates Asset importiert wird (auch wenn sie Duplikate sind). Dies kann zu Leistungs- und Speicherproblemen führen, da jedes Mesh individuell behandelt wird und Speicher und Draw Calls benötigt.
Begrenzen Sie die Pixel von Bildern auf nicht mehr als die notwendige Menge. Es sei denn, ein Bild nimmt einen großen physischen Platz auf dem Bildschirm ein, benötigt es normalerweise höchstens 512x512 Pixel. Die meisten kleineren Bilder sollten kleiner als 256x256 Pixel sein.
Verwenden Sie Trim Sheets, um maximale Texturwiederverwendung in 3D-Karten sicherzustellen. Für Schritte und Beispiele zur Erstellung von Trim Sheets 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
Bevor ein Spieler mit dem Spielen beginnen kann, muss der Client alles in ReplicatedFirst und ReplicatedStorage, die anfänglichen Inhalte von Workspace und alle Assets, die Ihre Skripte vorladen, erhalten. Je mehr Inhalte der Client während dieser Zeit herunterladen und verarbeiten muss, desto länger warten die Spieler. Für eine Aufschlüsselung jedes Schrittes im Beitrittsprozess siehe Identifizieren von Leistungsproblemen.
Häufige Probleme
Instanz-Streaming deaktiviert - Ohne Instanz-Streaming muss das gesamte Workspace an den Client repliziert werden, bevor der Spieler eintreten kann, sodass Spiele mit vielen Teilen, Modellen und Assets proportional längere Ladezeiten haben.
Übermäßiger Inhalt in ReplicatedStorage - Alles in ReplicatedStorage wird an jeden Client repliziert, bevor der Spieler etwas sehen oder damit interagieren kann, unabhängig davon, ob der Client es sofort benötigt. Kostenlose Modelle und geerbte Assets können stillschweigend erheblichen Ballast hinzufügen.
Schwerer ReplicatedFirst-Inhalt - ReplicatedFirst wird vor allem anderen repliziert und dient hauptsächlich dazu, einen benutzerdefinierten Ladebildschirm zu halten. Jedes zusätzliche Asset oder Skript darin verzögert den Beginn der Hauptreplikationsphase.
Übermäßige Verwendung von PreloadAsync() - Viele Spiele implementieren benutzerdefinierte Ladebildschirme und verwenden ContentProvider:PreloadAsync(), um Assets anzufordern, damit Bilder, Sounds und Meshes im Hintergrund heruntergeladen werden. Dieser Ansatz stellt sicher, dass wichtige Teile Ihres Spiels vollständig geladen sind, ohne dass sie plötzlich erscheinen, aber ein häufiger Fehler ist es, mehr Assets vorzuladen, als tatsächlich benötigt werden.
Ein Beispiel für eine schlechte Praxis ist das Laden des gesamten Workspace. Während dies das Pop-in von Texturen verhindern könnte, erhöht es die Ladezeiten erheblich.
Eine ähnliche Praxis besteht darin, ContentProvider.RequestQueueSize zu verwenden, um sicherzustellen, dass alle angeforderten Assets mit dem Laden fertig sind. Dies führt jedoch zum gleichen Problem der erheblich erhöhten Ladezeiten und ist auch eine unzuverlässige Methode aufgrund ihrer schwankenden Natur.
Teure Startskripte - Schweres Skripting in LocalScripts, das während des Beitrittsprozesses ausgeführt wird, wie das synchrone Laden großer Datenstrukturen, verzögert den Übergang von der Replikation zu einem spielbaren Zustand.
Minderung
Aktivieren Sie das Instanz-Streaming - Streaming sendet nur den Teil Ihrer Welt in der Nähe des Spawnpunkts des Spielers beim ersten Beitritt und streamt den Rest progressiv, während sich der Spieler durch Ihr Spiel bewegt. Für Spiele, die es noch nicht verwenden, ist Streaming eine der effektivsten Methoden zur Reduzierung der Ladezeiten. Streaming ist standardmäßig für neue Orte aktiviert. Für Einrichtungsschritte, Eigenschaftsreferenzen und Migrationsanleitungen siehe Instanz-Streaming.
Halten Sie ReplicatedStorage schlank - Speichern Sie nur das, was Clients im Moment ihres Beitritts benötigen, wie gemeinsame ModuleScripts, die LocalScripts benötigen. Verschieben Sie große Modelle, Audio und andere Assets, die nicht sofort benötigt werden, in ServerStorage und verschieben Sie sie nur auf den Client, wenn ein Spieler sie benötigt. Überprüfen Sie ReplicatedStorage regelmäßig auf Inhalte, die Sie nicht mehr verwenden.
Halten Sie ReplicatedFirst schlank - Beschränken Sie ReplicatedFirst auf das, was Ihr Ladebildschirm benötigt, wie grundlegende UI-Elemente anstelle von großen Texturen oder 3D-Modellen, und vermeiden Sie schwere Berechnungen in Skripten, die von ihm ausgeführt werden. Für eine Anleitung zum Erstellen eines benutzerdefinierten Ladebildschirms, einschließlich wann ReplicatedFirst:RemoveDefaultLoadingScreen() aufgerufen werden sollte, siehe Ladebildschirme.
Laden Sie nur notwendige Assets vor - Verwenden Sie 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.
Verschieben Sie nicht wesentliche Startarbeiten - Laden Sie Inhalte, die ein Spieler noch nicht erreicht hat, wie UI-Panels, die sie noch nicht geöffnet haben, nur, wenn sie zuerst benötigt werden. Für Initialisierungsarbeiten, die frühzeitig erfolgen müssen, verteilen Sie sie über mehrere Frames, anstatt sie alle auf einmal auszuführen. Für Techniken siehe Skriptberechnung.