实例流式传输

*此内容使用人工智能(Beta)翻译,可能包含错误。若要查看英文页面,请点按 此处

经验中的 实例流媒体 允许 Roblox 引擎在世界各地的区域动态加载和卸载 3D 内容和相关实例。这可以以多种方式提高整体玩家体验,例如:

  • 更快的加入时间 — 玩家可以在世界的一部分开始游戏,而更多的世界在背景中加载。
  • 内存效率 — 体验可以在内存更少的设备上播放,因为内容会动态地流入和流出。更沉浸和详细的世界可以在更广范围的设备上播放。
  • 提高性能 — 服务器可以花更少的时间和带宽同步世界和其中的玩家之间的更改,因此性能得到了提高。客户花费更少的时间更新不适用于玩家的实例。
  • 细节层次 — 即使不向客户传输远程模型和地形,它们仍然可以被看到,保持体验优化,而不是完全牺牲背景视觉。

启用流媒体

通过 Studio 中 工作区 对象的 StreamingEnabled 属性启用实例流式传输。此属性不能在脚本中设置。默认情况下,Studio 创建的新地点启用了直播。

The Properties window with the StreamingEnabled property enabled.

启用后,建议您遵守以下实践:

  • 因为客户端通常不会有整个 在本地可用,使用适当的工具/API 来确保在尝试访问它们之前存在实例。例如,使用 每个模型的串流控件 , 检测实例串流 , 或使用 WaitForChild() 在可能不存在的对象上。
  • 尽量减少 3D 内容在 Workspace 之外的放置。容器中的内容,例如 ReplicatedStorageReplicatedFirst 无法进行串流,可能会对加入时间和内存使用产生负面影响。
  • 如果通过设置玩家角色的 CFrame 移动角色角色,请从服务器端 Script 进行,并使用 串流请求 更快地加载角色新位置周围的数据。
  • 手动设置玩家的 ReplicationFocus 仅在独特情况下,例如在不使用 Player.Character 的体验中。在这些情况下,确保焦点靠近玩家控制的对象(s),以确保内容继续在玩家的互动点周围传播。

技术行为

流入

默认情况下,当玩家加入启用实例流的体验时, 中的实例复制到客户端,除了以关注中/正在关注内容:

然后,在游戏期间,服务器可以向客户端流传必要的实例,因为它们是需要的。

Diagram showing when various instances and their descendants in the Workspace stream in.
1 地形被独立对待,即当体验加载时,实例复制到客户端,但地形区域只在必要时流入

模型行为

模型设置为非默认行为,例如 原子 流在特定规则下进行,如 《模型流控制》 所述。然而,默认(非原子)模型根据是否将 ModelStreamingBehavior 设置为 默认遗产 )或 改进 发送不同。

The Properties window with the ModelStreamingBehavior property set to Default.

当模型传输行为设置为默认/遗产时,容器和其非空间子孙,例如》,在玩家加入时复制到客户端。然后,当满足条件时,模型的BasePart子孙会流入。

Diagram showing default model stream in behavior.

输出流

在游戏期间,客户端可能会流出(从玩家的 Workspace 区域和包含在其中的 BaseParts 中删除)根据 StreamOutBehavior 设置的行为。过程从离玩家角色最远的区域开始(或 ReplicationFocus ),并根据需要逐渐接近。流媒体最小半径 范围内的区域永远不会流出。

当实例输出时,它会被父辈到 nil 以便任何现有的 Luau 状态重连,如果实例重新输入。因结果,移除信号如 ChildRemovedDescendantRemoving 在其 父亲祖先 上发射,但实例本身不会以与 Instance:Destroy() 调用相同的方式被摧毁。

为了进一步预测流出,请检查这些场景:

场景例子流媒体行为
一个部分是 创建 本地通过 Instance.new() 在一个 LocalScript 中。在一场“捕捉旗帜”游戏中,你创建并附加蓝色头盔部件到蓝队所有玩家通过一个 LocalScript零件不会复制到服务器,且不需要流出 除非 你将其作为服务器上的零件的后裔,例如玩家角色模型中的零件。
一个部分是 克隆 本地从 ReplicatedStorage 通过 Instance:Clone() 在一个 LocalScript 中。一个巫师角色通过激活一个 Tool 施展魔法,其上一个包含多个 特殊效果 的对象从 ReplicatedStorage 被克隆到巫师的位置的工作区。零件不会复制到服务器,且不需要流出 除非你将其变为服务器上的零件的子零件
一部分是 重新修复了ReplicatedStorage 到工作区通过一个 LocalScript一个“巫师帽”被存储在 ReplicatedStorage 。当玩家选择加入巫师团队时,帽子通过 LocalScript 移至他们的角色模型。零件仍然可以作为直播输出,因为它来自服务器并被复制到 ReplicatedStorage 。避免这种模式,因为它会导致客户端和服务器之间的同步失效,部分可能会流出;相反, 克隆 该部分。

模型行为

如果您将模型传输行为设置为 改进 ,引擎在可以传输模型时可能会流出默认非原子)模型,可能会释放客户端的内存,并减少需要属性更新的实例。

The Properties window with the ModelStreamingBehavior property set to Improved.

在 改进 模型传输行为下,从 默认 ( 非原子模型 ) 模型中流出是基于模型是否为 空间 (包含 子孙 ) 或 非空间 (没有 子孙 )。

  • 空间模型只有在最后一个剩余的 BasePart 后裔流出时才能完全流出,因为模型的一些空间部分可能靠近玩家/复制焦点,而另一些则很远。
  • 非空间模型只在祖先输出时流出,相当于遗产输出行为。

装配和机制

当少于一个部分的 装配 可以在线传输时,所有装配的部分也会在线传输。然而,组装会不会流出直到所有零件都可以流出为止。在传输期间,所有从 ConstraintsAttachments 下降的 BaseParts 和原子或持久 Models 也会串流传,有助于确保客户端上的物理更新一致。

请注意,带有 锚定部件 的装配与仅带有未锚定部件的装配处理略有不同:

装配组成流媒体行为
仅限未锚定的零件整个装配发送为原子单元。
已锚定 根部件只有需要将流式部件链接到根部件的零件、附件和约束的零件才会一起传送。

时间延迟

服务器上创建零件和复制到客户端之间可能存在轻微延迟 ~10 毫秒。在以下每个场景中,您可能需要使用 WaitForChild() 和其他技巧而不是假设事件和属性更新总是在部分串流时发生。

场景例子流媒体行为
A LocalScript 向服务器发出一个 RemoteFunction 创建零件的调用。玩家激活一个 Tool 本地地方,生成服务器上的零件,所有玩家都可以看到并与之交互。当远程函数返回到客户端时,零件可能还不存在,即使零件靠近客户端焦点并位于传输区域内。
通过 Script 将一部分添加到服务器上的角色模型,然后 RemoteEvent 发射到客户端。当玩家加入警察团队时,存储在 ServerStorage 中的“警察徽章”部分被克隆并附加到玩家的角色模型上A RemoteEvent 被该玩家的客户发射并接收,以更新本地 UI 元素。虽然客户端收到了事件信号,但没有保证该部分已经流向该客户端。
零件与服务器上的隐形区域碰撞并触发 RemoteEvent 在客户端上。一名玩家将足球踢进球门,触发了“进球得分”事件。离目标很近的其他玩家可能会在球传到他们之前看到“进球得分”事件。

流媒体属性

以下属性控制实例传播如何适用于您的体验。所有这些属性都是 不可脚本化 的,必须在 Studio 的 工作区 对象上设置。

The Properties window with the ModelStreamingBehavior, StreamingIntegrityMode, StreamingMidRadius, StreamingTargetRadius, and StreamOutBehavior property highlighted.

模型传输行为

控制是否在玩家加入时复制默认非原子)模型,或仅在必要时发送。如果此属性设置为 改进 ,那么在 Workspace 中的模型只会在需要时发送给客户端,可能会加快加入时间。请参阅技术行为获取更多细节。

流媒体完整性模式

如果玩家移至世界上尚未传播给他们的区域,您的体验可能会以意外的方式行动。 流式完整性 功能提供了一种避免这些可能带来问题的情况的方法。请参阅Enum.StreamingIntegrityMode了解更多详情。

传输最小半径

流媒体最小半径 属性表示在最高优先级下流媒体在玩家角色周围的半径(或 ReplicationFocus )。在增加默认值时,应该注意,因为这样做会需要更多的内存和更多的服务器带宽,而忽略其他组件。

传输目标半径

流媒体目标半径 属性控制在哪些情况下流媒体在玩家角色(或 ReplicationFocus )的最大距离。请注意,引擎可以保留先前加载的实例超出目标半径,内存允许。

更小的 传输目标半径 减少服务器工作量,因为服务器不会在设置值之外的额外实例进行传输。然而,目标半径也是玩家能够看到您的体验全部细节的最大距离,因此您应该选择一个值,可以在这些之间形成良好的平衡。

流出行为

StreamOutBehavior 属性设置了 传输出 行为,根据以下值之一:

设置流媒体行为
默认 默认行为,目前与 低内存 相同。
低内存 客户端仅在低内存情况下传输零件,并且可以在仅存在最小半径时移除 3D 内容。
机会主义 超出 流媒体目标半径 的区域在客户端上可以移除,即使没有内存压力。在这种模式下,客户端永远不会删除离目标半径更近的实例,除非在低内存情况下。

每个模型的流式控控制

在全球范围内,模型传输行为属性可让您控制模型在加入时如何传输。此外,为了避免基于每个模型的流媒体问题并减少使用 WaitForChild() 的使用,您可以通过自定义 Models 和其子孙的流媒体通过他们的 ModelStreamingMode 属性来减少使用。

The Properties window with the ModelStreamingMode property set to Default. The property is also highlighted.

默认/非原子

当 设置为 默认 或 非原子 时,流行为会根据是否将 模型传输行为设置为默认 ( 遗产 ) 或 改进 而变化。

模型传输行为技术行为
默认 ( 遗产 )当玩家加入时,模型会被复制。这可能会导致更多在加载时发送的实例、更多存储在内存中的实例以及需要访问模型后裔的脚本的额外复杂性。例如,单独的 LocalScript 需要在模型内的子模型 WaitForChild() 上使用 BasePart
改进 模型仅在必要时发送,可能会加快加入时间。

请参阅技术行为获取更多细节。

原子

如果 Model 被更改为 原子 ,当子孙 BasePart 有资格时,所有其子孙都会一起流传。因结果,需要访问模型中的实例的独立 LocalScript 需要使用 WaitForChild() 在模型本身,但不是在子孙 MeshPartPart 上,因为它们与模型一起发送。

原子模型只在所有下级零件都可以进行传输时才会传输出去,此时整个模型一起传输出去。如果只有某些部分的原子模型通常会被传输出去,整个模型和其子模型都留在客户端上。

A diagram showing Atomic model streaming along with children.
本地脚本

local Workspace = game:GetService("Workspace")
-- 在加载时不存在原子模型;使用 WaitForChild()
local model = Workspace:WaitForChild("Model")
-- 下级零件与模型一起流入,并立即可用
local meshPart = model.MeshPart
local part = model.Part

持久

永久 模型不受普通串行输入或输出。它们很快就会被发送为完整的原子单位,参与者加入后和 Workspace.PersistentLoaded 事件触发之前。永久模型和其子孙从来没有被流出,但要安全地在单独的 LocalScript 中处理流媒体,您应该在父模型上使用 WaitForChild() 或等待 PersistentLoaded 事件发射。

A diagram showing Persistent model streaming along with children.
本地脚本

local Workspace = game:GetService("Workspace")
-- 永久模型在加载时不存在;使用 WaitForChild()
local model = Workspace:WaitForChild("Model")
-- 下级零件与模型一起流入,并立即可用
local meshPart = model.MeshPart
local part = model.Part

持续每个玩家

将模型设置为 PersistentPerPlayer 与使用 添加的玩家行为相同,为玩家添加使用 Model:AddPersistentPlayer() 的玩家将行为相同。对于其他玩家,行为与 原子 相同。您可以通过 Model:RemovePersistentPlayer() 将模型从玩家持续中恢复。

请求区域传输

如果你将玩家角色的 CFrame 设置为未加载的区域,如果启用,将发生流媒体暂停。如果你知道角色将移动到特定区域,你可以调用 Player:RequestStreamAroundAsync() 要求服务器向客户端发送该位置周围的区域。

以下脚本显示了如何发射客户端到服务器的 远程事件 来传送玩家到一个场景,在移动角色到新的 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)
本地脚本 - 发射远程事件

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local teleportEvent = ReplicatedStorage:WaitForChild("TeleportEvent")
local teleportTarget = Vector3.new(50, 2, 120)
-- 发射远程事件
teleportEvent:FireServer(teleportTarget)

检测实例流式传输

在某些情况下,需要检测当对象输入或输出时检测到并反应该事件。有用的流媒体检测模式如下:

  1. 使用实例属性的 标签 部分或 Studio的标签编辑器,为所有受影响的对象分配一个逻辑标签CollectionService

  2. 从单个 LocalScript 检测标记对象是否通过 GetInstanceAddedSignal()GetInstanceRemovedSignal() 流入或流出,然后根据情况处理对象。例如,以下代码将标记的 Light 对象添加到"闪烁"循环中,当它们传入时移除它们,当它们传出时移除它们。

    本地脚本 - 收集服务流媒体检测

    local CollectionService = game:GetService("CollectionService")
    local tagName = "FlickerLightSource"
    local random = Random.new()
    local flickerSources = {}
    -- 检测当前和新标记的部件在线或离线传输
    for _, light in CollectionService:GetTagged(tagName) do
    flickerSources[light] = true
    end
    CollectionService:GetInstanceAddedSignal(tagName):Connect(function(light)
    flickerSources[light] = true
    end)
    CollectionService:GetInstanceRemovedSignal(tagName):Connect(function(light)
    flickerSources[light] = nil
    end)
    -- 闪烁循环
    while true do
    for light in flickerSources do
    light.Brightness = 8 + random:NextNumber(-0.4, 0.4)
    end
    task.wait(0.05)
    end

自定义暂停屏幕

Player.GameplayPaused 属性表示玩家当前的暂停状态。此属性可以用 GetPropertyChangedSignal() 连接显示或隐藏自定义 GUI。

本地脚本

local Players = game:GetService("Players")
local GuiService = game:GetService("GuiService")
local player = Players.LocalPlayer
-- 禁用默认暂停模式
GuiService:SetGameplayPausedNotificationEnabled(false)
local function onPauseStateChanged()
if player.GameplayPaused then
-- 显示自定义 GUI
else
-- 隐藏自定义 GUI
end
end
player:GetPropertyChangedSignal("GameplayPaused"):Connect(onPauseStateChanged)

模型细节等级

启用传输时,当前传输区域外的 Models 将默认不可见。然而,您可以通过每个模型的 LevelOfDetail 属性指示引擎渲染较低分辨率的“捣乱者”网格给不存在于客户端的模型。

LevelOfDetail property indicated for Model instance
A globe model displays in its actual level of detail.
实际模型
The same globe model displays as a low resolution imposter mesh with rough edges that obscure the globe's details.
低分辨率“捣乱者”网格
模型设置流媒体行为
流媒体网格 激活捣乱者网格异步生成,以便在模型不存在于客户端时显示。
已禁用 / 自动 当模型脱离传输范围时,模型将消失。

使用捣乱者网格时,请注意以关注中/正在关注内容:

  • 捣乱者网格设计为在 距离相机1024格或更远处被看到 。如果您将直播目标半径降低到像 256 这样的更小值,那么恶意篡改者网格可能不会对模型替换后的视觉效果接受。
  • 如果模型 其子模型都设置为 流媒体网格 ,只有顶级祖先模型被渲染为捣乱者网格,包括祖先和子模型下的所有几何体。为了获得更好的性能,建议您使用 禁用 对子模型。
  • 不支持纹理;恶意者网格被渲染为光滑网格。
  • 虽然 Model 尚未完全传输,但捣乱者网格被渲染而不是模型的个别部分。一旦所有单个部分都传送进来,它们将渲染并忽略捣乱者网格。
  • 捣乱者网格没有物理意义,它们在射线投射、碰撞检测和物理模拟中行为为不存在。
  • 在工作室编辑模型,例如添加/删除/重新定位子部件或重置颜色,将自动更新代表网格。