Techniques et conversion

*Ce contenu est traduit en utilisant l'IA (Beta) et peut contenir des erreurs. Pour consulter cette page en anglais, clique ici.

Ce guide décrit plusieurs techniques pour utiliser le streaming d'instances de manière efficace et efficiente dans le jeu. Bien qu'il n'existe pas de solution "taille unique" pour concevoir un jeu en streaming ou convertir un jeu non streamé en streaming, suivre ces étapes de haut niveau vous emmènera la plupart du chemin.

Propriétés de streaming

Une fois que StreamingEnabled est activé pour l'objet Workspace dans Studio, définissez ses propriétés connexes aux valeurs recommandées suivantes :

PropriétéRecommandation
EnableSLIMAvatarsUtilisez Enabled pour rendre les avatars de rig standard comme des remplaçants légers et animés lorsque cela est approprié. Consultez les avatars SLIM pour plus d'infos.
ModelStreamingBehaviorUtilisez Improved pour activer le streaming le plus efficace pour les Models avec des descendants BasePart.
StreamingIntegrityModeUtilisez PauseOutsideLoadedArea pour équilibrer l'intégrité du gameplay sans faire de pauses inutiles ou trop fréquentes.
StreamingMinRadiusUtilisez la valeur par défaut de 64 pour maximiser la capacité du moteur à réduire la taille du jeu pour les appareils bas de gamme.
StreamingTargetRadiusUtilisez la valeur par défaut de 1024 pour trouver un bon équilibre entre la visibilité pour les joueurs sur des appareils haut de gamme et une empreinte mémoire raisonnable.
StreamOutBehaviorUtilisez Opportunistic pour permettre au client de collecter agressivement les objets inutilisés, réduisant ainsi considérablement l'utilisation de la mémoire et aidant à prévenir les plantages dus à un manque de mémoire.

Niveau de détail du modèle

Model.LevelOfDetail aide à remplir le contenu Model non streamé avec des maillages composites ou d'imposteurs légers, rendant le monde visuellement complet. Les maillages composites SLIM (Scalable Lightweight Interactive Model) sont particulièrement efficaces, car les joueurs ne peuvent souvent pas distinguer un maillage SLIM du modèle original entièrement streamé.

Pour de meilleurs résultats :

  • Regroupez les parties qui sont spatialement et logiquement liées, par exemple toutes les parties d'une voiture.
  • Réglez LevelOfDetail sur SLIM pour les modèles contenant des maillages et parties statiques. Les modèles contenant des maillages animés, modifiés au runtime ou jouant des animations ne sont pas pris en charge.
  • Gardez l'étendue spatiale de chaque modèle en dessous de ~64 studs cubes pour augmenter la probabilité que tout le modèle réel se stream ensemble. Si un modèle a de très grandes dimensions, décomposez-le en modèles modulaires plus petits et appliquez un LevelOfDetail approprié à chacun.

Structure du modèle

Au-delà de la définition du niveau de détail du modèle, la structure et les paramètres de vos Models ont un impact significatif sur la performance du streaming. Au fur et à mesure que vous développez ou convertissez un jeu existant :

  • Utilisez des modèles atomiques pour le regroupement logique — Lorsqu'un script doit accéder à toutes les parties d'un modèle, définissez son ModelStreamingMode sur Atomic. Cela permet aux scripts côté client d'accéder en toute sécurité aux instances à l'intérieur du modèle sans utiliser de manière excessive WaitForChild() (bien que ces scripts doivent toujours utiliser WaitForChild() pour le modèle atomique global).

  • Minimisez les modèles persistants — Les modèles persistants se chargent après la connexion et ne se streament jamais, occupant ainsi de la mémoire de manière permanente. Réglez le ModelStreamingMode d'un modèle sur Persistent uniquement s'il doit rester disponible et accessible aux scripts en permanence.

  • Décomposez les modèles de conteneurs — Un modèle Model unique et énorme contenant de nombreux PNJ, objets ou regroupements similaires est un motif non streamé courant. Dans le cadre du streaming, les modèles conteneurs diminuent l'efficacité du streaming et ne sont pas optimaux pour le niveau de détail du modèle qui fonctionne mieux avec des instances étroitement regroupées. Décomposez les modèles conteneurs en modèles plus petits avec des parties physiquement proches ou logiquement liées.

  • Aplatissez les hiérarchies de modèle profondément imbriquées — Emballer un modèle persistant à l'intérieur d'un modèle atomique force effectivement le modèle atomique à se comporter comme persistant. Les hiérarchies plates sont plus faciles à comprendre sous streaming.

Avatars SLIM

Les avatars de plateforme en dehors de la zone actuellement streamée ne sont pas visibles par défaut, mais l'activation de Workspace.EnableSLIMAvatars rend les avatars de rig standard comme des remplaçants légers et animés lorsque cela est approprié. Effectivement, le moteur :

  • Rend une version SLIM lorsqu'un modèle d'avatar réel se stream.
  • Échange SLIM/réel par les ressources disponibles à l'intérieur du rayon de streaming, les modèles SLIM pouvant être significativement moins coûteux à rendre que le modèle en pleine résolution.
  • Régule les animations SLIM selon l'importance de la scène et la bande passante.

Le système de niveau de détail des avatars a des optimisations spécifiques pour les avatars. Ce qui suit décrit ce qui sera ou ne sera pas traité par SLIM :

Modèles de scripts

Les modèles de scripts suivants sont souvent affectés par le streaming. La bonne stratégie dépend de l'intention du code, c'est pourquoi chaque modèle énumère plusieurs options lorsque cela est approprié.

Index direct vers les descendants

Indexation des descendants de Workspace avec l'opérateur . génère une erreur si une instance dans le chemin n'est pas actuellement streamée. Il en va de même pour FindFirstChild(), FindFirstChildWhichIsA() et FindFirstChildOfClass() qui retournent nil si l'enfant n'a pas été streamé.

Recherche de descendants

local house1 = workspace:FindFirstChild("House1") -- nil si "House1" n'a pas été streamé
local door = workspace.House1.Door -- Cassé si "House1" ou "Door" n'a pas été streamé

Un modèle similaire consiste à accéder directement à Humanoid ou à d'autres descendants de personnage à l'intérieur d'une connexion Player.CharacterAdded. Dans le cadre du streaming, le modèle du personnage est parenté à Workspace avant que tous ses descendants ne soient répliqués, de sorte que l'indexation directe échoue.

Descendants de personnage

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

Si le script ne peut pas progresser sans une instance, attendez-la avec WaitForChild() :

Recherche de descendants

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

Instances envoyées à distance

Un signal RemoteEvent/RemoteFunction et l'instance qu'il référence voyagent indépendamment, donc le signal peut arriver sur le client avant que l'instance soit présente — ou l'instance peut ne jamais être présente du tout. Deux causes probables incluent :

  • Dans le cadre du streaming, il peut y avoir un léger délai entre la création d'une partie/modèle sur le serveur et sa réplique aux clients. Effectivement, une partie référencée par un RemoteEvent/RemoteFunction peut simplement ne pas encore exister, même à l'intérieur d'une zone streamée.

  • L'envoi d'une référence de partie/modèle du serveur au client via un RemoteEvent ou un RemoteFunction nécessite que l'instance soit répliquée au client récepteur. Envoyer un chemin d'instance sous forme de chaîne présente le même problème, car le chemin peut résoudre à un emplacement inexistant sur le client :

    Script client

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
    remoteEvent.OnClientEvent:Connect(function(data)
    local checkpoint = data.checkpoint -- Erreurs si "checkpoint" n'est pas streamé
    local level = workspace.Levels[data.levelPath] -- Erreurs si le chemin n'est pas streamé
    end)

Si le script du client a besoin de l'instance pour progresser, incluez WaitForChild() avant de l'utiliser. Notez que cela peut faire attendre indéfiniment si l'instance ne se stream jamais, envisagez donc d'ajouter un délai d'attente comme deuxième paramètre de WaitForChild().

Désynchronisation côté client

La désynchronisation côté client doit être traitée comme une exception, et non comme un modèle de conception standard. L'introduction de copies uniquement client ou le re-parentage des instances localement peuvent créer des problèmes graves. Vérifiez votre code pour détecter les endroits qui reposent sur ces types de changements persistant du côté client.

Par exemple, re-parenting d'une instance localement de ReplicatedStorage vers Workspace peut rendre cette instance éligible pour être streamée. De même, le clonage d'une instance localement (Instance:Clone()) depuis ReplicatedStorage dans Workspace crée une copie uniquement client qui ne fait plus partie du pipeline de réplication serveur et ne recevra pas de mises à jour de propriété de l'instance d'origine appartenant au serveur.

Le même concept s'applique lors de l'appel de Instance:Destroy() sur un objet appartenant au serveur. Cela supprime localement l'instance, mais le serveur l'a toujours, donc elle se stream à nouveau avec son état d'origine lorsqu'elle est éligible.

Streaming proactif

Lorsque la prochaine destination d'un joueur peut être anticipée, effectuez des appels côté serveur à Player:RequestStreamAroundAsync() pour streamer les zones transitoires pour un chargement temporaire, ou utilisez Player:AddReplicationFocus() sur une base limitée pour les zones qui devraient rester chargées jusqu'à ce qu'elles soient explicitement libérées.

Par exemple, lorsque le personnage d'un joueur est sur le point de se téléporter par changement de CFrame vers la maison d'un autre joueur dans un emplacement éloigné, vous pouvez pré-charger la zone de destination pour minimiser le temps de chargement et fournir une transition plus fluide. Le script suivant montre comment un événement à distance peut être déclenché pour déplacer un personnage joueur en utilisant une méthode de pré-chargement. Si la demande de pré-chargement est réussie lorsque la fonction retourne, le rayon minimum autour de l'emplacement cible devrait être présent sur le client.

Script serveur - Téléporter le personnage du joueur

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local teleportEvent = ReplicatedStorage:WaitForChild("TeleportEvent")
local function teleportPlayer(player, teleportTarget)
-- Demander le streaming autour de l'emplacement cible
player:RequestStreamAroundAsync(teleportTarget)
-- Téléporter le personnage
local character = player.Character
if character and character.Parent then
local currentPivot = character:GetPivot()
character:PivotTo(currentPivot * CFrame.new(teleportTarget))
end
end
-- Appeler la fonction de téléportation lorsque le client déclenche l'événement à distance
teleportEvent.OnServerEvent:Connect(teleportPlayer)

Lectures de propriétés d'instance

Une fois qu'une instance se stream hors de portée, ses mises à jour de propriétés ne sont plus répliquées à ce client. La lecture de propriétés telles que BasePart.Position continue de réussir mais retourne la dernière valeur répliquée qui peut être arbitrairement obsolète.


local Players = game:GetService("Players")
local player = Players.LocalPlayer
-- La position peut être obsolète si "target" a été streamé
local dist = (target.Position - player.Character.HumanoidRootPart.Position).Magnitude

Déplacez la logique vers le serveur, car les scripts côté serveur voient toutes les instances à tout moment. C'est généralement l'option la plus fiable pour les vérifications de distance et d'autres logiques sensibles à la position.

Attente sur le chemin critique

Certains jeux non streamés chargent leur carte en la clonant depuis ReplicatedStorage dans Workspace, puis attendent qu'elle soit présente sur le client avant de supprimer un écran de chargement et de signaler qu'ils sont prêts. Dans le cadre du streaming, cela se bloque indéfiniment — le personnage du client n'a pas encore été spawné, donc il n'y a pas de focus de réplication, et l'instance de carte spatiale ne se stream jamais.

Déplacez la logique de l'écran de chargement afin qu'elle ne dépende pas d'une instance spatiale spécifique étant présente, par exemple en signalant la préparation une fois que le personnage a été spawné et que la zone environnante immédiate a été streamée.

Gestion des changements de signal

Des signaux tels que Instance.ChildAdded/Instance.ChildRemoved et des signaux de CollectionService comme GetInstanceAddedSignal() ou GetInstanceRemovedSignal() se déclenchent également lors du streaming dans/de streaming hors, indiscernables des véritables spawns/retrait. Les scripts de réception ne peuvent pas faire la différence uniquement à partir du signal, donc la logique qui suppose qu'un signal correspond à un événement "réel" doit être mise à jour.

Auditez les scripts pour tout auditeur de signal qui pourrait se casser ou changer de manière significative lorsqu'il est déclenché par un streaming dans et/ou hors. Par exemple, si vous jouez des effets audiovisuels lors du spawn initial d'un PNJ ennemi dans le monde, assignez à chaque ennemi un attribut tel que Spawned lors du premier spawn, et évitez de rejouer les mêmes effets audio/visuels lors des futurs streams d'ennemie.

Suivi d'attributs

local CollectionService = game:GetService("CollectionService")
local TAG_NAME = "Enemy"
CollectionService:GetInstanceAddedSignal(TAG_NAME):Connect(function(enemy)
if not enemy:GetAttribute("Spawned") then
-- Définir l'attribut "Spawned" sur l'ennemi pour le spawn initial
enemy:SetAttribute("Spawned", true)
-- Jouer des effets audiovisuels pour ce spawn initial
playSpawnEffects(enemy)
end
end)

Itération sur des collections

Les itérations de collections côté client comme Instance:GetChildren() et Instance:GetDescendants() ne retournent que le sous-ensemble streamé des descendants. Cela s'applique même lorsque le parent lui-même est toujours répliqué, comme un Folder directement sous Workspace dont les descendants spatiaux se stream dans et hors.


local Players = game:GetService("Players")
local player = Players.LocalPlayer
-- Le dossier "Homes" est toujours répliqué mais ses enfants se stream dans et hors
-- Cette boucle peut manquer des maisons qui ne sont actuellement pas streamées
for _, home in workspace.Homes:GetChildren() do
if home.Settings.Owner.Value == player.Name then
return home
end
end

Si une énumération complète est requise, effectuez le scan sur le serveur et passez le résultat au joueur via un RemoteEvent si nécessaire.

Requêtes spatiales

Les requêtes spatiales côté client telles que WorldRoot:Raycast(), WorldRoot:GetPartBoundsInBox(), et Model:GetBoundingBox() ne reflètent que le contenu streamé. Si cela pose problème dépend de l'utilisation de la requête.

Utilisez le serveur pour les requêtes dont le résultat doit refléter le monde entier, par exemple un raycast qui vérifie si le joueur a une ligne de mire sur une cible éloignée.

Autres modèles

Les modèles suivants peuvent également s'appliquer et doivent être soigneusement examinés :

  • Un Sound ou AudioPlayer parenté à un objet 3D s'arrête lorsque cet objet se stream hors. Pour un audio ambiant qui doit persister indépendamment du streaming, parent l'émetteur à un modèle persistant ou à un conteneur non streamé.

  • Dans les objets d'interface utilisateur in-game comme BillboardGui ou SurfaceGui, ainsi que des effets visuels tels que Beams ou Highlights dont l'adornee ou l'attachement se stream hors, arrêtent simplement de rendre. Cela peut être le comportement souhaité, mais vous devriez le vérifier.

  • Les événements BasePart.Touched, ProximityPrompts, DragDetectors, et ClickDetectors ne fonctionnent pas pour les joueurs dont le client n'a pas l'objet/modèle associé streamé. Si l'interaction doit être possible de n'importe quelle distance, le modèle doit être persistant ou l'interaction doit utiliser un autre mécanisme.

  • Pour PathfindingService et le pathfinding côté client, le chemin ne voit que la géométrie streamée sur le client et peut passer à travers des obstacles qui existent sur le serveur. Voir ici pour des stratégies.

Conditions de test réalistes

Une fois les scripts mises à jour, testez le jeu en profondeur. Les bugs de streaming se manifestent souvent uniquement aux bords de la zone streamée ou pendant les transitions, donc tester uniquement près de spawn ou au rayon cible n'est pas suffisant.

  • Testez avec Workspace.StreamingTargetRadius réglé sur sa valeur minimale (64). Certains bugs de streaming n'apparaissent que lorsque la zone streamée est petite.

  • Jouez à travers les pleins schémas de traversée du jeu, téléportez-vous entre des zones éloignées et revenez dans des zones après les avoir quittées. Ce sont les situations qui exercent le plus de streaming dans et hors.

  • Utilisez la superposition de débogage de streaming pour surveiller les paramètres de streaming actifs, les régions actuellement chargées, et l'état de streaming en temps réel.

  • Surveillez la fenêtre Output et la Console développeur pour les erreurs, car de nombreux modèles de script produisent des erreurs plutôt que des comportements silencieux. Portez une attention particulière aux erreurs du type tentative d'indexer nil avec ... qui indiquent souvent un appel manquant à WaitForChild().

  • Équipez et activez des Tools, tirez avec des armes, et déclenchez différentes interactions de jeu.

©2026 Société Roblox. Roblox, le logo Roblox et Powering Imagination font partie de nos marques déposées aux États-Unis et dans d'autres pays.