技術與轉換

*此內容是使用 AI(Beta 測試版)翻譯,可能含有錯誤。若要以英文檢視此頁面,請按一下這裡

本指南概述了幾種有效和高效使用遊戲內 實例串流 的技術。雖然設計串流遊戲沒有「一刀切」的解決方案,但遵循這些高層次的步驟將使您大致達到目標。

串流屬性

一旦在 Studio 中為 Workspace 對象切換 StreamingEnabled,請將其相關屬性設置為以下建議值:

屬性建議
EnableSLIMAvatars在適當的情況下,使用 Enabled 將標準骨架化身渲染為輕量級的動畫替代品。更多信息請參見 SLIM 角色
ModelStreamingBehavior使用 Improved 來啟用對於具有 BasePart 後代的 Models 的最有效串流。
StreamingIntegrityMode使用 PauseOutsideLoadedArea 來平衡遊戲性完整性,而不會不必要或過於頻繁地暫停。
StreamingMinRadius使用默認值 64 來最大化引擎可以為低端設備縮小遊戲的程度。
StreamingTargetRadius使用默認值 1024 來在高端設備的玩家可見性和合理的內存佔用之間取得良好平衡。
StreamOutBehavior使用 Opportunistic 來允許客戶端積極地垃圾收集內容,顯著減少內存使用並幫助防止內存不足崩潰。

模型細節層次

Model.LevelOfDetail 有助於用輕量級的合成或替代網格填充未串流的 Model 內容,使世界看起來視覺上完整。 SLIM(可擴展輕量級互動模型)特別有效,因為玩家通常無法區分 SLIM 網格和完全串流進來的原始模型。

為了獲得最佳效果:

  • 將空間上和邏輯上相關的部件分組,例如汽車的所有部件。
  • 對於包含 靜態 網格和部件的模型,將 LevelOfDetail 設置為 SLIM。在運行時修改或播放動畫的模型不受支持。
  • 將每個模型的空間範圍保持在 ~64 立方 studs 以增加整個實際模型一起串流的可能性。如果模型的範圍非常大,請將其拆分為更小的模塊模型,並對每個模型應用適當的 LevelOfDetail

模型結構

除了設置 模型細節層次 外,您的 Models 的結構和設置對串流性能有重大影響。在構建或轉換現有遊戲時:

  • 使用原子模型進行邏輯分組 — 當腳本需要訪問模型中的所有部件時,將其 ModelStreamingMode 設置為 Atomic。這允許客戶端腳本安全地訪問模型內的實例,而不會過度使用 WaitForChild()(儘管這些腳本仍必須對整個原子模型使用 WaitForChild())。

  • 最小化持久模型持久 模型在加入後加載,並且永遠不會串流出,永久佔用內存。僅當模型 必須 始終可用並可供腳本訪問時,將其 ModelStreamingMode 設置為 Persistent

  • 分解容器模型 — 一種常見的非串流模式是單個巨大的 Model,其中包含許多 NPC、道具或類似的分組。在串流下,容器模型會降低串流效率,並且對於 模型細節層次 來說並不理想,該層次在實例緊密分組時效果最佳。將容器模型分解為具有物理接近或邏輯相關部件的小模型。

  • 扁平化深度嵌套的模型層次 — 將 持久 模型嵌套在 原子 模型內部,會有效地強迫原子模型表現為持久。扁平層次在串流下更容易理解。

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 引用的部件可能根本不存在,即使在串流區域內。

  • 通過 RemoteEventRemoteFunction 將部件/模型引用從服務器發送到客戶端需要該實例已複製到接收客戶端。將實例路徑作為字符串發送也有相同的問題,因為該路徑可能解析為客戶端上不存在的位置:

    客戶端腳本
    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 可能使該實例有資格被串流出。同樣,從 ReplicatedStorage 本地克隆實例(Instance:Clone())到 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.ChildRemovedCollectionService 信號如 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 對象的 SoundAudioPlayer 在該對象串流出時停止。對於應該持續存在的環境音頻,將發射器附加到 持久 模型或非串流容器中。

  • 遊戲內 UI 對象如 BillboardGuiSurfaceGui 以及 視覺效果BeamsHighlights,如果其附著物或附加物串流出,則會停止渲染。這可能是預期的行為,但您應該驗證它。

  • BasePart.Touched 事件、ProximityPromptsDragDetectorsClickDetectors 對於未在客戶端上串流的相關部件/模型的玩家無法運行。如果必須能夠從任何範圍進行交互,則模型需要是 持久 的,或者交互需要不同的機制。

  • 對於 PathfindingService 和客戶端側的路徑尋找,路徑尋找器僅在客戶端上看到串流進的幾何形狀,並且可能會通過服務器上存在的障礙物進行路由。請參見 這裡 獲取策略。

現實測試條件

一旦 腳本更新,請徹底測試遊戲。串流錯誤通常僅在串流區域的邊緣或過渡期間顯現,因此僅在生成附近或目標半徑進行測試是不夠的。

  • 測試時將 Workspace.StreamingTargetRadius 設置為其最小值(64)。某些串流錯誤僅在串流區域較小時出現。

  • 完整地玩遍遊戲的所有遍歷模式,在遙遠的區域之間傳送,並在離開後重新訪問區域。這些是最能檢驗串流進和串流出的情況。

  • 使用 串流調試覆蓋 來監控活動的串流設置、當前加載的區域和運行時串流狀態。

  • 觀察 輸出 窗口和 開發者控制台 中的錯誤,因為許多 腳本模式 會產生錯誤而不是靜默的錯誤行為。特別注意 attempt to index nil with ... 形式的錯誤,這通常表示缺少 WaitForChild() 調用。

  • 裝備並激活 Tools,開火武器,觸發不同的遊戲交互。

AI 串流轉換技能

為了協助串流轉換和優化,Roblox 提供了一個 AI 串流技能,可從 Studio MCP 服務器 訪問。該技能自動評估您的遊戲,應用建議的配置,並清理兼容性問題,包括:


要在您的遊戲中使用 AI 技能:

  1. 重要
    備份您的遊戲。轉換過程可能很複雜,因此您應該 始終 保存備份(文件 ⟩ 發布 到 Roblox 作為)然後再運行該技能。

  2. 您可以通過 Studio 中的 模型上下文協議 (MCP) 使用任何您喜歡的 LLM 運行此技能。建議使用具有大上下文窗口的高端 AI 模型;在 Claude Opus 中,典型的轉換需要 20-30 分鐘,並使用大約 200,000 個上下文標記。

    1. 在 Studio 中打開您的遊戲。
    2. 下載該技能,並在您的 AI 客戶端中將解壓縮的文件夾(roblox-streaming-conversion)作為當前項目打開。
    3. 使用 /rbx-convert-to-streaming 運行該技能。
    4. 與任何 AI 輸出一樣,驗證結果並在 現實測試條件 下廣泛測試您的遊戲。
©2026 Roblox Corporation、Roblox、Roblox 標誌及 Powering Imagination 是我們在美國及其他國家地區的部分註冊與未註冊商標。