이 가이드는 게임 내 인스턴스 스트리밍을 효율적이고 효과적으로 사용하는 여러 기법을 설명합니다. 스트리밍 게임을 설계하는 데 "모든 경우에 맞는" 솔루션은 없지만, 이러한 고수준 단계를 따르면 대부분의 작업을 수행할 수 있습니다.
스트리밍 속성
StreamingEnabled가 Studio의 Workspace 객체에 대해 활성화되면, 관련 속성을 다음의 권장 값으로 설정합니다:
| 속성 | 권장 사항 |
|---|---|
| EnableSLIMAvatars | 적절할 때 표준 리그 아바타를 경량화된 애니메이션 대체물로 렌더링하기 위해 Enabled를 사용하세요. 자세한 내용은 SLIM 아바타를 참조하세요. |
| ModelStreamingBehavior | Models에 BasePart 자손이 있는 경우 가장 효율적인 스트리밍을 활성화하기 위해 Improved를 사용하세요. |
| StreamingIntegrityMode | 불필요하게 또는 너무 자주 일시 정지하지 않고 게임 플레이 무결성을 균형 있게 유지하기 위해 PauseOutsideLoadedArea를 사용하세요. |
| StreamingMinRadius | 엔진이 저사양 장치에 맞게 게임을 최대한 축소할 수 있도록 기본값인 64를 사용하세요. |
| StreamingTargetRadius | 고사양 장치의 플레이어에 대한 가시성과 합리적인 메모리 사용량 간의 균형을 맞추기 위해 기본값인 1024를 사용하세요. |
| StreamOutBehavior | 클라이언트가 콘텐츠를 적극적으로 가비지 수집할 수 있도록 Opportunistic을 사용하여 메모리 사용량을 크게 줄이고 메모리 부족으로 인한 충돌을 방지하세요. |
모델 세부 사항
Model.LevelOfDetail은 비스트리밍 Model 콘텐츠를 경량화된 복합체 또는 임포스터 메시로 채워 세계를 시각적으로 완전하게 만듭니다. SLIM (확장 가능한 경량 상호작용 모델)은 특히 효과적이며, 플레이어는 종종 SLIM 메시와 완전히 스트리밍된 원본을 구별할 수 없습니다.
최상의 결과를 위해:
- 공간적으로 및 논리적으로 관련된 부품을 그룹화하세요. 예를 들어 자동차의 모든 부품을 그룹화합니다.
- 각 모델의 공간적 범위를 ~64 입방 스터드 이하로 유지하여 전체 실제 모델이 함께 스트리밍될 가능성을 높입니다. 모델의 범위가 매우 크면 더 작은 모듈형 모델로 나누고 각 모델에 적절한 LevelOfDetail을 적용하세요.
모델 구조
모델 세부 사항 설정 외에도 Models의 구조와 설정은 스트리밍 성능에 상당한 영향을 미칩니다. 기존 게임을 구축하거나 변환할 때:
논리적 그룹화를 위한 원자 모델 사용 — 스크립트가 모델 내의 모든 부품에 접근해야 할 때, ModelStreamingMode를 Atomic으로 설정하세요. 이렇게 하면 클라이언트 측 스크립트가 WaitForChild()를 과도하게 사용하지 않고 모델 내부의 인스턴스에 안전하게 접근할 수 있습니다 (단, 이러한 스크립트는 전체 원자 모델에 대해 여전히 WaitForChild()를 사용해야 합니다).
지속적인 모델 최소화 — 지속적인 모델은 조인 후 로드되고 절대 스트리밍되지 않으며, 메모리를 영구적으로 차지합니다. 모델의 ModelStreamingMode를 Persistent로 설정하는 것은 항상 스크립트에서 사용 가능해야 하는 경우에만 수행하세요.
SLIM 아바타
현재 스트리밍된 영역 외부의 플랫폼 아바타는 기본적으로 보이지 않지만, Workspace.EnableSLIMAvatars를 활성화하면 적절할 때 표준 리그 아바타를 경량화된 애니메이션 대체물로 렌더링합니다. 효과적으로 엔진은:
- 실제 아바타 모델이 스트리밍 아웃될 때 SLIM 버전을 렌더링합니다.
- 사용 가능한 리소스에 따라 SLIM과 전체 해상도 표현 간에 전환합니다. 스트리밍 반경 내에서도 가능합니다.
- 장면 중요성과 사용 가능한 대역폭에 따라 SLIM 애니메이션을 조절합니다.
SLIM 아바타는 몸, 머리, 레이어드 의상 및 액세서리가 있는 R15 표준 리그 플레이어 캐릭터를 지원합니다. R6 아바타, NPC 및 사용자 정의 비율의 아바타는 제외됩니다. 지원되는 아바타 구성 및 제외된 아바타 구성의 전체 목록, 성능 데이터 및 문제 해결 팁은 SLIM 아바타를 참조하세요.
스크립트 패턴
다음 스크립트 패턴은 스트리밍의 영향을 가장 많이 받습니다. 올바른 전략은 코드의 의도에 따라 다르므로 각 패턴은 적절한 여러 옵션을 나열합니다.
자손에 대한 직접 인덱스
Workspace 자손에 . 연산자를 사용하여 인덱싱하면 경로의 인스턴스가 현재 스트리밍되지 않으면 오류가 발생합니다. FindFirstChild(), FindFirstChildWhichIsA(), FindFirstChildOfClass()에도 동일하게 적용되며, 자식이 스트리밍되지 않은 경우 nil을 반환합니다.
local house1 = workspace:FindFirstChild("House1") -- "House1"이 스트리밍되지 않으면 nil
local door = workspace.House1.Door -- "House1" 또는 "Door"가 스트리밍되지 않으면 오류 발생유사한 패턴은 Player.CharacterAdded 연결 내에서 Humanoid 또는 다른 캐릭터 자손에 직접 접근하는 것입니다. 스트리밍 하에서는 캐릭터 모델이 모든 자손이 복제되기 전에 Workspace에 부모로 설정되므로 직접 인덱싱이 실패합니다.
local Players = game:GetService("Players")
local player = Players.LocalPlayer
player.CharacterAdded:Connect(function(character)
local humanoid = character.Humanoid
end)스크립트가 인스턴스 없이 진행할 수 없는 경우, WaitForChild()로 기다리세요:
local house1 = workspace:WaitForChild("House1")
local door = house1:WaitForChild("Door")원격으로 전송된 인스턴스
RemoteEvent/RemoteFunction 신호와 그것이 참조하는 인스턴스는 독립적으로 이동하므로, 신호가 클라이언트에 도착하기 전에 인스턴스가 존재하지 않거나 아예 존재하지 않을 수 있습니다. 두 가지 가능한 원인은 다음과 같습니다:
스트리밍 하에서는 서버에서 부품/모델이 생성된 후 클라이언트에 복제되기까지 약간의 지연이 있을 수 있습니다. 효과적으로, RemoteEvent/RemoteFunction로 참조된 부품은 스트리밍된 영역 내에서도 존재하지 않을 수 있습니다.
서버에서 클라이언트로 RemoteEvent 또는 RemoteFunction을 통해 부품/모델 참조를 전송하려면 인스턴스가 수신 클라이언트에 복제되어야 합니다. 문자열로 인스턴스 경로를 전송하는 것도 동일한 문제를 가지고 있으며, 경로가 클라이언트에서 존재하지 않는 위치로 해결될 수 있습니다:
클라이언트 스크립트local ReplicatedStorage = game:GetService("ReplicatedStorage")local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")remoteEvent.OnClientEvent:Connect(function(data)local checkpoint = data.checkpoint -- "checkpoint"가 스트리밍되지 않으면 오류 발생local level = workspace.Levels[data.levelPath] -- 경로가 스트리밍되지 않으면 오류 발생end)
수신 클라이언트 스크립트가 인스턴스가 필요하면 사용하기 전에 WaitForChild()를 포함하세요. 인스턴스가 스트리밍되지 않으면 무한정 대기할 수 있으므로, WaitForChild()의 두 번째 매개변수로 타임아웃을 추가하는 것을 고려하세요.
클라이언트 비동기화
클라이언트 측 비동기화는 예외로 취급해야 하며, 표준 설계 패턴이 아닙니다. 클라이언트 전용 복사본을 도입하거나 인스턴스를 로컬에서 재부모하는 것은 심각한 문제를 일으킬 수 있습니다. 이러한 유형의 변경이 클라이언트에서 지속되도록 의존하는 코드의 위치를 감사하세요.
예를 들어, ReplicatedStorage에서 Workspace로 인스턴스를 로컬에서 재부모하면 해당 인스턴스가 스트리밍 아웃될 수 있습니다. 마찬가지로, Instance:Clone()을 사용하여 ReplicatedStorage에서 Workspace로 인스턴스를 복제하면 클라이언트 전용 복사본이 생성되어 더 이상 서버 복제 파이프라인의 일부가 아니며 원래 서버 소유 인스턴스의 속성 업데이트를 받지 않습니다.
서버 소유 객체에 대해 클라이언트에서 Instance:Destroy()를 호출할 때도 동일한 개념이 적용됩니다. 이렇게 하면 인스턴스가 로컬에서 제거되지만 서버에는 여전히 존재하므로, 적격할 때 원래 상태로 다시 스트리밍됩니다.
사전 스트리밍
플레이어의 다음 목적지를 예측할 수 있는 경우, 서버 측에서 Player:RequestStreamAroundAsync()를 호출하여 임시 로딩을 위한 일시적인 영역을 스트리밍하거나, 명시적으로 해제될 때까지 로드된 상태를 유지해야 하는 영역에 대해 Player:AddReplicationFocus()를 제한적으로 사용하세요.
예를 들어, 플레이어 캐릭터가 멀리 있는 다른 플레이어의 집으로 CFrame 변경으로 텔레포트하려고 할 때, 팝인 현상을 최소화하고 부드러운 전환을 제공하기 위해 목적지 영역을 미리 가져올 수 있습니다. 다음 스크립트는 사전 가져오기 방법을 사용하여 플레이어 캐릭터를 이동시키기 위해 클라이언트-서버 원격 이벤트를 발생시키는 방법을 보여줍니다. 함수가 반환될 때 사전 가져오기 요청이 성공하면, 클라이언트에 대상 위치 주변의 최소 반경이 존재해야 합니다.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local teleportEvent = ReplicatedStorage:WaitForChild("TeleportEvent")
local function teleportPlayer(player, teleportTarget)
-- 대상 위치 주변 스트리밍 요청
player:RequestStreamAroundAsync(teleportTarget)
-- 캐릭터 텔레포트
local character = player.Character
if character and character.Parent then
local currentPivot = character:GetPivot()
character:PivotTo(currentPivot * CFrame.new(teleportTarget))
end
end
-- 클라이언트가 원격 이벤트를 발생시킬 때 텔레포트 함수 호출
teleportEvent.OnServerEvent:Connect(teleportPlayer)인스턴스 속성 읽기
인스턴스가 스트리밍 아웃되면 해당 속성 업데이트는 더 이상 클라이언트에 복제되지 않습니다. BasePart.Position과 같은 속성을 읽는 것은 계속 성공하지만 마지막으로 복제된 값을 반환하며, 이는 임의로 오래된 값일 수 있습니다.
local Players = game:GetService("Players")
local player = Players.LocalPlayer
-- "target"이 스트리밍 아웃되면 위치가 오래될 수 있습니다.
local dist = (target.Position - player.Character.HumanoidRootPart.Position).Magnitude로직을 서버로 이동하세요. 서버 측 스크립트는 항상 모든 인스턴스를 볼 수 있습니다. 이는 거리 확인 및 기타 위치 민감 로직에 대해 일반적으로 가장 신뢰할 수 있는 옵션입니다.
중요한 경로에서 대기
일부 비스트리밍 게임은 ReplicatedStorage에서 Workspace로 맵을 복제하여 로드한 다음, 클라이언트에서 이를 기다린 후 로딩 화면을 해제하고 준비 완료 신호를 보냅니다. 스트리밍 하에서는 클라이언트의 캐릭터가 아직 생성되지 않았기 때문에 복제 초점이 없으며, 공간 맵 인스턴스는 절대 스트리밍되지 않습니다.
로딩 화면 로직을 이동하여 특정 공간 인스턴스의 존재에 의존하지 않도록 하세요. 예를 들어, 캐릭터가 생성되고 즉각적인 주변 영역이 스트리밍된 후 준비 완료 신호를 보냅니다.
신호 변경 처리
Instance.ChildAdded/Instance.ChildRemoved와 CollectionService 신호인 GetInstanceAddedSignal() 또는 GetInstanceRemovedSignal()은 스트리밍 인/아웃 시에도 발생하며, 실제 생성/제거와 구별할 수 없습니다. 수신 스크립트는 신호만으로는 차이를 알 수 없으므로, 신호가 "실제" 이벤트에 해당한다고 가정하는 로직은 업데이트해야 합니다.
스트리밍 인 및/또는 스트리밍 아웃으로 트리거될 때 깨지거나 크게 변경될 수 있는 신호 리스너에 대해 스크립트를 감사하세요. 예를 들어, 적 NPC가 처음으로 세계에 생성될 때 오디오 또는 시각 효과를 재생하는 경우, 각 적에게 첫 생성 시 Spawned와 같은 속성을 할당하고, 적의 향후 스트리밍 인에서 동일한 오디오/효과를 재생하는 것을 건너뜁니다.
local CollectionService = game:GetService("CollectionService")
local TAG_NAME = "Enemy"
CollectionService:GetInstanceAddedSignal(TAG_NAME):Connect(function(enemy)
if not enemy:GetAttribute("Spawned") then
-- 초기 생성 시 적의 "Spawned" 속성 설정
enemy:SetAttribute("Spawned", true)
-- 이 초기 생성에 대한 오디오/시각 효과 재생
playSpawnEffects(enemy)
end
end)컬렉션 반복
클라이언트 측 컬렉션 반복인 Instance:GetChildren() 및 Instance:GetDescendants()는 스트리밍된 자손의 하위 집합만 반환합니다. 이는 부모 자체가 항상 복제되는 경우에도 적용되며, 예를 들어 Workspace 아래의 Folder는 공간 자손이 스트리밍 인 및 아웃됩니다.
local Players = game:GetService("Players")
local player = Players.LocalPlayer
-- "Homes" 폴더는 항상 복제되지만 자식은 스트리밍 인 및 아웃됩니다.
-- 이 루프는 현재 스트리밍되지 않은 집을 놓칠 수 있습니다.
for _, home in workspace.Homes:GetChildren() do
if home.Settings.Owner.Value == player.Name then
return home
end
end완전한 열거가 필요한 경우, 서버에서 스캔을 수행하고 필요에 따라 RemoteEvent를 통해 결과를 플레이어에게 전달하세요.
공간 쿼리
클라이언트 측 공간 쿼리인 WorldRoot:Raycast(), WorldRoot:GetPartBoundsInBox(), 및 Model:GetBoundingBox()는 스트리밍된 콘텐츠만 반영합니다. 이것이 문제가 되는지는 쿼리가 사용되는 용도에 따라 다릅니다.
결과가 전체 세계를 반영해야 하는 쿼리의 경우 서버를 사용하세요. 예를 들어, 플레이어가 먼 목표를 볼 수 있는지 확인하는 레이캐스트입니다.
기타 패턴
다음 패턴도 적용될 수 있으며 신중하게 고려해야 합니다:
3D 객체에 부모가 있는 Sound 또는 AudioPlayer는 해당 객체가 스트리밍 아웃될 때 중지됩니다. 스트리밍과 관계없이 지속해야 하는 환경 오디오의 경우, 발신기를 지속적인 모델 또는 비스트리밍 컨테이너에 부모로 설정하세요.
게임 내 UI 객체인 BillboardGui 또는 SurfaceGui와 시각 효과인 Beams 또는 Highlights는 아도르니 또는 부착물이 스트리밍 아웃되면 렌더링을 중지합니다. 이는 의도된 동작일 수 있지만, 이를 확인해야 합니다.
BasePart.Touched 이벤트, ProximityPrompts, DragDetectors, 및 ClickDetectors는 클라이언트에 해당 부품/모델이 스트리밍되지 않은 플레이어에게는 작동하지 않습니다. 모든 범위에서 상호작용이 가능해야 하는 경우, 모델은 지속적이어야 하거나 상호작용에 다른 메커니즘이 필요합니다.
PathfindingService 및 클라이언트 측 경로 찾기의 경우, 경로 찾기는 클라이언트에서 스트리밍된 기하학만 볼 수 있으며 서버에 존재하는 장애물을 통과할 수 있습니다. 전략은 여기를 참조하세요.
현실적인 테스트 조건
스크립트가 업데이트된 후, 게임을 철저히 테스트하세요. 스트리밍 버그는 종종 스트리밍된 영역의 가장자리나 전환 중에만 나타나므로, 생성 근처나 목표 반경에서만 테스트하는 것은 충분하지 않습니다.
Workspace.StreamingTargetRadius를 최소값(64)으로 설정하여 테스트하세요. 일부 스트리밍 버그는 스트리밍된 영역이 작을 때만 나타납니다.
게임의 전체 이동 패턴을 통해 플레이하고, 먼 지역 간에 텔레포트하며, 떠난 후 지역을 다시 방문하세요. 이러한 상황이 스트리밍 인 및 스트리밍 아웃을 가장 많이 발생시킵니다.
스트리밍 디버그 오버레이를 사용하여 활성 스트리밍 설정, 현재 로드된 지역 및 런타임 스트리밍 상태를 모니터링하세요.
출력 창과 개발자 콘솔에서 오류를 확인하세요. 많은 스크립트 패턴은 오류를 발생시키며 조용한 오작동을 일으키지 않습니다. attempt to index nil with ... 형식의 오류에 특히 주의하세요. 이는 종종 누락된 WaitForChild() 호출을 나타냅니다.
Tools를 장착하고 활성화하며, 무기를 발사하고 다양한 게임 상호작용을 트리거하세요.
AI 스트리밍 변환 기술
스트리밍 변환 및 최적화를 지원하기 위해 Roblox는 Studio MCP 서버에서 접근할 수 있는 AI 스트리밍 기술을 제공합니다. 이 기술은 게임을 자동으로 평가하고, 권장 구성을 적용하며, 호환성 문제를 정리합니다. 포함된 내용은 다음과 같습니다:
게임에서 AI 기술을 사용하려면:
- 중요게임을 백업하세요. 변환 과정은 복잡할 수 있으므로, 기술을 실행하기 전에 항상 백업을 저장해야 합니다 (파일 ⟩ Roblox에 게시).
Studio의 모델 컨텍스트 프로토콜(MCP)를 통해 선호하는 LLM을 사용하여 이 기술을 실행할 수 있습니다. 큰 컨텍스트 창을 가진 고급 AI 모델을 권장합니다. Claude Opus에서는 일반적인 변환이 20-30분 걸리며 약 200,000개의 컨텍스트 토큰을 사용합니다.
- Studio에서 게임을 엽니다.
- 기술을 다운로드하고 AI 클라이언트에서 압축을 푼 폴더(roblox-streaming-conversion)를 현재 프로젝트로 엽니다.
- /rbx-convert-to-streaming으로 기술을 실행합니다.
- 모든 AI 출력과 마찬가지로 결과를 검증하고 현실적인 테스트 조건에서 게임을 광범위하게 플레이 테스트하세요.

