Techniken und Konversion

*Dieser Inhalt wurde mit KI (Beta) übersetzt und kann Fehler enthalten. Um diese Seite auf Englisch zu sehen, klicke hier.

Dieser Leitfaden umreißt mehrere Techniken zur effizienten und effektiven Nutzung des Instanzstreamings im Spiel. Obwohl es keine "One-Size-Fits-All"-Lösung für die Gestaltung eines Streaming-Spiels oder die Umwandlung eines Nicht-Streaming-Spiels in ein Streaming-Spiel gibt, werden Sie mit diesen übergeordneten Schritten die meisten Anforderungen erfüllen.

Streaming-Eigenschaften

Sobald StreamingEnabled im Studio für das Workspace-Objekt aktiviert ist, legen Sie seine zugehörigen Eigenschaften auf die folgenden empfohlenen Werte fest:

EigenschaftEmpfehlung
EnableSLIMAvatarsVerwenden Sie Enabled, um Standard-Rig-Avatare als leichte, animierte Stellvertreter wiederzugeben, wenn es sinnvoll ist. Siehe SLIM-Avatare für weitere Informationen.
ModelStreamingBehaviorVerwenden Sie Improved, um das effizienteste Streaming für Modelle mit Nachkommen von BasePart zu aktivieren.
StreamingIntegrityModeVerwenden Sie PauseOutsideLoadedArea, um die Integrität des Spiels zu wahren, ohne unnötig oder zu oft zu pausieren.
StreamingMinRadiusVerwenden Sie den Standardwert von 64, um zu maximieren, wie stark die Engine das Spiel für Geräte mit niedrigen Spezifikationen verkleinern kann.
StreamingTargetRadiusVerwenden Sie den Standardwert von 1024, um ein gutes Gleichgewicht zwischen Sichtbarkeit für Spieler auf High-End-Geräten und einem angemessenen Speicherbedarf zu erreichen.
StreamOutBehaviorVerwenden Sie Opportunistic, um es dem Client zu ermöglichen, Inhalte aggressiv zu Müll zu sammeln, was den Speicherbedarf erheblich reduziert und hilft, Speicherüberläufe zu verhindern.

Modell-Stufen der Detailgenauigkeit

Model.LevelOfDetail hilft dabei, nicht gestreamte Model-Inhalte mit leichten Composite- oder Imposter-Meshes zu füllen, sodass die Welt visuell vollständig aussieht. SLIM (Scalable Lightweight Interactive Model)-Composite-Meshes sind besonders effektiv, da Spieler oft keinen Unterschied zwischen einem SLIM-Mesh und dem vollständig gestreamten Original erkennen können.

Für die besten Ergebnisse:

  • Gruppieren Sie Teile, die räumlich und logisch zusammenhängen, zum Beispiel alle Teile eines Autos.
  • Setzen Sie LevelOfDetail auf SLIM für Modelle, die statische Meshes und Teile enthalten. Modelle, die skinned Meshes enthalten, zur Laufzeit modifiziert werden oder Animationen abspielen, werden nicht unterstützt.
  • Halten Sie das räumliche Ausmaß jedes Modells unter ~64 kubischen Stüden, um die Wahrscheinlichkeit zu erhöhen, dass das gesamte tatsächliche Modell zusammen gestreamt wird. Wenn ein Modell sehr große Ausmaße hat, zerlegen Sie es in kleinere modulare Modelle und wenden Sie ein entsprechendes LevelOfDetail für jedes an.

Modellstruktur

Über das Setzen der Modell-Stufen der Detailgenauigkeit hinaus hat die Struktur und die Einstellungen Ihrer Modelle einen erheblichen Einfluss darauf, wie gut das Streaming funktioniert. Während Sie ein bestehendes Spiel aufbauen oder umwandeln:

  • Verwenden Sie atomare Modelle für logische Gruppierungen — Wenn ein Skript auf alle Teile innerhalb eines Modells zugreifen muss, setzen Sie seinen ModelStreamingMode auf Atomic. Dies ermöglicht es clientseitigen Skripten, sicher auf Instanzen innerhalb des Modells zuzugreifen, ohne übermäßigen Gebrauch von WaitForChild() zu machen (obwohl solche Skripte dennoch WaitForChild() für das gesamte atomare Modell verwenden müssen).

  • Minimieren Sie persistente ModellePersistente Modelle werden nach dem Beitritt geladen und werden nie gestreamt, sodass sie dauerhaft Speicherplatz beanspruchen. Setzen Sie den ModelStreamingMode eines Modells auf Persistent, wenn es immer verfügbar und für Skripte zugänglich sein muss.

  • Zerlegen Sie Container-Modelle — Ein häufiges Muster im Nicht-Streaming ist ein einzelnes großes Model, das viele NPCs, Requisiten oder ähnliche Gruppierungen enthält. Unter Streaming verringern Container-Modelle die Streaming-Effizienz und sind nicht optimal für die Modell-Stufen der Detailgenauigkeit, die am besten mit eng gruppierten Instanzen funktionieren. Zerlegen Sie Container-Modelle in kleinere Modelle mit physisch nahen oder logisch verwandten Teilen.

  • Flachere tief verschachtelte Modellhierarchien — Das Verschachteln eines persistenten Modells innerhalb eines atomaren Modells zwingt das atomare Modell effektiv dazu, sich wie ein persistentes Modell zu verhalten. Flache Hierarchien sind unter Streaming einfacher zu verstehen.

SLIM-Avatare

Plattform-Avatare außerhalb des derzeit gestreamten Bereichs sind standardmäßig nicht sichtbar, aber das Aktivieren von Workspace.EnableSLIMAvatars rendert Standard-Rig-Avatare als leichte, animierte Stellvertreter, wenn es angemessen ist. Effektiv rendert die Engine:

  • Eine SLIM-Version, wenn ein tatsächliches Avatar-Modell gestreamt wird.
  • Tauscht SLIM/tatsächlich je nach verfügbaren Ressourcen innerhalb des Streaming-Radius, da SLIM-Modelle erheblich weniger kostenintensiv zu rendern sind als das vollauflösende Modell.
  • Drosselt SLIM-Animationen anhand der Wichtigkeit der Szene und der Bandbreite.

Das Avatar-Level-of-Detail-System hat spezifische Optimierungen für Avatare. Folgendes beschreibt, was von SLIM verarbeitet wird und was nicht:

Skriptmuster

Die folgenden Skriptmuster sind am stärksten vom Streaming betroffen. Die richtige Strategie hängt von der Absicht des Codes ab, daher listet jedes Muster mehrere Optionen auf, wo es angebracht ist.

Direkter Index zu Nachkommen

Das Indizieren in Workspace-Nachkommen mit dem .-Operator erzeugt einen Fehler, wenn eine Instanz im Pfad derzeit nicht gestreamt wird. Das Gleiche gilt für FindFirstChild(), FindFirstChildWhichIsA() und FindFirstChildOfClass(), die nil zurückgeben, wenn das Kind nicht gestreamt ist.

Nachkommen-Suche

local house1 = workspace:FindFirstChild("House1") -- nil, wenn "House1" nicht gestreamt ist
local door = workspace.House1.Door -- Fehlerhaft, wenn "House1" oder "Door" nicht gestreamt ist

Ein ähnliches Muster ist der Zugriff auf die Humanoid oder andere Charakter-Nachkommen direkt in einer Player.CharacterAdded-Verbindung. Unter Streaming wird das Charaktermodell an Workspace angehängt, bevor alle Nachkommen repliziert sind, sodass direkter Zugriff fehlschlägt.

Charakter-Nachkommen

local Players = game:GetService("Players")
local player = Players.LocalPlayer
player.CharacterAdded:Connect(function(character)
local humanoid = character.Humanoid
end)

Wenn das Skript ohne eine Instanz nicht weiterkommen kann, warten Sie auf sie mit WaitForChild():

Nachkommen-Suche

local house1 = workspace:WaitForChild("House1")
local door = house1:WaitForChild("Door")

Instanzen, die remote gesendet werden

Ein RemoteEvent/RemoteFunction-Signal und die Instanz, auf die es verweist, reisen unabhängig voneinander, sodass das Signal auf dem Client ankommen kann, bevor die Instanz vorhanden ist — oder die Instanz möglicherweise nie vorhanden ist. Zwei wahrscheinliche Ursachen sind:

  • Unter Streaming kann es eine geringe Verzögerung geben, zwischen dem Zeitpunkt, an dem ein Teil/Modell auf dem Server erstellt wird, und dem Zeitpunkt, an dem es an die Clients repliziert wird. Effektiv kann ein Teil, auf das durch ein RemoteEvent/RemoteFunction verwiesen wird, einfach noch nicht existieren, selbst innerhalb eines gestreamten Bereichs.

  • Das Senden eines Teile/Modell-Referenz vom Server zum Client über ein RemoteEvent oder RemoteFunction erfordert, dass die Instanz an den empfangenden Client repliziert wird. Das Senden eines Instanzpfades als Zeichenfolge hat dasselbe Problem, da der Pfad möglicherweise zu einem nicht existierenden Ort auf dem Client führt:

    Client-Skript

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
    remoteEvent.OnClientEvent:Connect(function(data)
    local checkpoint = data.checkpoint -- Fehler, wenn "checkpoint" nicht gestreamt ist
    local level = workspace.Levels[data.levelPath] -- Fehler, wenn der Pfad nicht gestreamt ist
    end)

Wenn das empfangende Clientskript die Instanz benötigt, um fortzufahren, fügen Sie WaitForChild() hinzu, bevor Sie es verwenden. Beachten Sie, dass dies unbegrenzt warten kann, wenn die Instanz niemals gestreamt wird, erwägen Sie daher, ein Timeout als zweiten Parameter für WaitForChild() hinzuzufügen.

Client-Desynchronisation

Die clientseitige Desynchronisation sollte als Ausnahme behandelt werden, nicht als Standarddesignmuster. Das Einführen von nur clientseitigen Kopien oder das Umordnen von Instanzen lokal kann schwerwiegende Probleme verursachen. Überprüfen Sie Ihren Code auf Stellen, die sich auf solche Änderungen verlassen, um auf dem Client bestehen zu bleiben.

Zum Beispiel kann das lokale Umordnen einer Instanz von ReplicatedStorage zu Workspace diese Instanz berechtigen, gestreamt zu werden. Ebenso erzeugt das Klonen einer Instanz lokal (Instance:Clone()) aus ReplicatedStorage in Workspace eine nur clientseitige Kopie, die nicht mehr Teil des Replikationspools des Servers ist und keine Aktualisierungen von Eigenschaften von der ursprünglichen serverbesitzenden Instanz erhält.

Das gleiche Konzept gilt, wenn Instance:Destroy() auf dem Client für ein serverbesessenes Objekt aufgerufen wird. Dies entfernt die Instanz lokal, aber der Server hat sie weiterhin, sodass sie erneut mit ihrem ursprünglichen Zustand gestreamt wird, wenn sie berechtigt ist.

Proaktives Streaming

Wenn das nächste Ziel eines Spielers vorhersehbar ist, führen Sie serverseitige Aufrufe zu Player:RequestStreamAroundAsync() durch, um temporäre Bereiche für das vorübergehende Laden zu streamen, oder verwenden Sie Player:AddReplicationFocus() in einem begrenzten Umfang für Bereiche, die geladen bleiben sollen, bis sie ausdrücklich freigegeben werden.

Zum Beispiel, wenn ein Spielercharakter durch eine CFrame-Änderung an das Haus eines anderen Spielers an einen weit entfernten Ort teleportiert wird, können Sie den Zielbereich vorab abrufen, um Pop-ins zu minimieren und einen reibungsloseren Übergang zu ermöglichen. Das folgende Skript zeigt, wie ein Client-zu-Server-Remote-Ereignis ausgelöst werden kann, um einen Spielercharakter mit einer Vorabrufmethode zu bewegen. Wenn die Vorabrufanfrage erfolgreich ist, wenn die Funktion zurückkehrt, sollte der minimale Radius um den Zielort beim Client vorhanden sein.

Server-Skript - Spielercharakter teleportieren

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local teleportEvent = ReplicatedStorage:WaitForChild("TeleportEvent")
local function teleportPlayer(player, teleportTarget)
-- Streaming um den Zielort anfordern
player:RequestStreamAroundAsync(teleportTarget)
-- Charakter teleportieren
local character = player.Character
if character and character.Parent then
local currentPivot = character:GetPivot()
character:PivotTo(currentPivot * CFrame.new(teleportTarget))
end
end
-- Ruf die Teleport-Funktion auf, wenn der Client das Remote-Ereignis auslöst
teleportEvent.OnServerEvent:Connect(teleportPlayer)

Instanzeigenschaftslesungen

Sobald eine Instanz gestreamt wird, werden ihre Aktualisierungen der Eigenschaften nicht mehr an diesen Client repliziert. Das Lesen von Eigenschaften wie BasePart.Position funktioniert weiterhin, gibt jedoch den letzten replizierten Wert zurück, der beliebig veraltet sein kann.


local Players = game:GetService("Players")
local player = Players.LocalPlayer
-- Position kann veraltet sein, wenn "target" gestreamt wurde
local dist = (target.Position - player.Character.HumanoidRootPart.Position).Magnitude

Bewegen Sie die Logik auf den Server, da serverseitige Skripte zu jeder Zeit alle Instanzen sehen. Dies ist im Allgemeinen die zuverlässigste Option für Distanzüberprüfungen und andere positionsabhängige Logik.

Warten auf den kritischen Pfad

Einige Nicht-Streaming-Spiele laden ihre Karte, indem sie sie von ReplicatedStorage in Workspace klonen und dann warten, bis sie auf dem Client erscheint, bevor sie einen Ladebildschirm dismissieren und die Bereitstellung signalisieren. Unter Streaming hängt dies unendlich — der Charakter des Clients ist noch nicht erschienen, sodass es keinen Replikationsfokus gibt und die räumliche Karteninstanz nie gestreamt wird.

Verschieben Sie die Logik des Ladebildschirms, sodass sie nicht von einer bestimmten räumlichen Instanz abhängig ist; signalisieren Sie beispielsweise die Bereitschaft, nachdem der Charakter erschienen ist und der unmittelbare Umgebung gestreamt wurde.

Signaländerungsbehandlung

Signale wie Instance.ChildAdded/Instance.ChildRemoved und CollectionService-Signale wie GetInstanceAddedSignal() oder GetInstanceRemovedSignal() fire auch bei Streaming ein/aus, ununterscheidbar von echten Spawns/Entfernungen. Empfängerskripte können den Unterschied nur durch das Signal selbst nicht erkennen, sodass Logik, die annimmt, dass ein Signal einem "realen" Ereignis entspricht, aktualisiert werden muss.

Überprüfen Sie Skripte auf Signal-Listener, die möglicherweise unterbrochen oder erheblich verändert werden könnten, wenn sie durch Streaming ein- und/oder ausgestart werden. Wenn Sie beispielsweise Audio- oder visuelle Effekte abspielen, wenn ein feindlicher NPC ursprünglich in die Welt auftaucht, weisen Sie jedem Feind ein Attribut wie Spawned beim ersten Spawn zu und überspringen sie das Abspielen der gleichen Audio/Effekte bei zukünftigen Streamings des Feindes.

Attributverfolgung

local CollectionService = game:GetService("CollectionService")
local TAG_NAME = "Enemy"
CollectionService:GetInstanceAddedSignal(TAG_NAME):Connect(function(enemy)
if not enemy:GetAttribute("Spawned") then
-- Setzen Sie das "Spawned"-Attribut beim Feind für den ersten Spawn
enemy:SetAttribute("Spawned", true)
-- Spielen Sie Audio-/visuelle Effekte für diesen ersten Spawn
playSpawnEffects(enemy)
end
end)

Iteration über Sammlungen

Clientseitige Sammlungsiterationen wie Instance:GetChildren() und Instance:GetDescendants() geben nur den gestreamten Teil der Nachkommen zurück. Dies gilt selbst dann, wenn der Elternteil selbst immer repliziert wird, wie z. B. ein Folder, der direkt unter Workspace liegt, dessen räumliche Nachkommen ein- und ausgestreamt werden.


local Players = game:GetService("Players")
local player = Players.LocalPlayer
-- Der Ordner "Homes" wird immer repliziert, aber seine Kinder werden gestreamt
-- Diese Schleife kann Häuser verpassen, die derzeit nicht gestreamt sind
for _, home in workspace.Homes:GetChildren() do
if home.Settings.Owner.Value == player.Name then
return home
end
end

Wenn eine vollständige Enumeration erforderlich ist, führen Sie den Scan auf dem Server durch und geben Sie das Ergebnis bei Bedarf an den Spieler über ein RemoteEvent weiter.

Räumliche Abfragen

Clientseitige räumliche Abfragen wie WorldRoot:Raycast(), WorldRoot:GetPartBoundsInBox() und Model:GetBoundingBox() spiegeln nur gestreamte Inhalte wider. Ob das ein Problem darstellt, hängt davon ab, wofür die Abfrage verwendet wird.

Verwenden Sie den Server für Abfragen, deren Ergebnis die gesamte Welt widerspiegeln muss, z. B. für einen Strahl, der überprüft, ob der Spieler Sichtkontakt zu einem weit entfernten Ziel hat.

Andere Muster

Die folgenden Muster könnten ebenfalls zutreffen und sollten sorgfältig geprüft werden:

  • Ein Sound oder AudioPlayer, der an ein 3D-Objekt angehängt ist, stoppt, wenn dieses Objekt gestreamt wird. Für Umgebungsgeräusche, die unabhängig vom Streaming bestehen bleiben sollten, hängen Sie den Schallemitter an ein persistentes Modell oder an einen nicht-streamenden Container an.

  • In-Game-UI-Objekte wie BillboardGui oder SurfaceGui sowie visuelle Effekte wie Beams oder Highlights, deren Adornee oder Anhängsel gestreamt werden, hören einfach auf, gerendert zu werden. Dies könnte das beabsichtigte Verhalten sein, aber Sie sollten es überprüfen.

  • BasePart.Touched-Ereignisse, ProximityPrompts, DragDetectors und ClickDetectors funktionieren nicht für Spieler, deren Client nicht das entsprechende Teil/modell gestreamt hat. Wenn eine Interaktion aus jeder Entfernung möglich sein muss, muss das Modell persistent sein oder die Interaktion benötigt einen anderen Mechanismus.

  • Für PathfindingService und clientseitiges Pfadfinden sieht der Pfadfinder nur gestreamte Geometrie auf dem Client und kann durch Hindernisse routen, die auf dem Server existieren. Siehe hier für Strategien.

Realistische Testbedingungen

Sobald die Skripte aktualisiert sind, testen Sie das Spiel gründlich. Streamingfehler treten oft nur an den Rändern des gestreamten Bereichs oder während Übergängen auf, weshalb es nicht ausreichend ist, nur in der Nähe des Spawns oder in der Zielradius zu testen.

  • Testen Sie mit Workspace.StreamingTargetRadius, das auf seinen Minimalwert (64) eingestellt ist. Einige Streamingfehler treten nur auf, wenn der gestreamte Bereich klein ist.

  • Spielen Sie die vollständigen Traversalmuster durch, teleportieren Sie zwischen weit entfernten Bereichen und besuchen Sie Bereiche erneut, nachdem Sie sie verlassen haben. Dies sind die Situationen, die das Streaming ein- und ausüben.

  • Verwenden Sie das Streaming-Debug-Overlay, um die aktiven Streaming-Einstellungen, aktuell geladenen Regionen und den Laufzeit-Streaming-Zustand zu überwachen.

  • Achten Sie auf die Ausgabe-Fenster und die Entwicklerkonsole auf Fehler, da viele der Skriptmuster Fehler anstelle von stillschweigenden Fehlverhalten erzeugen. Achten Sie besonders auf Fehler der Form attempt to index nil with ..., die häufig auf einen fehlenden WaitForChild()-Aufruf hinweisen.

  • Statten Sie Tools aus, feuern Sie Waffen ab und lösen Sie verschiedene Spielinteraktionen aus.

©2026 Roblox Corporation. Roblox, das Roblox-Logo und "Powering Imagination" gehören zu unseren eingetragenen und nicht eingetragenen Markenzeichen in den USA und anderen Ländern.