Hello devs, I’m maintaining the codebase for a somewhat popular obby game, and recently we’ve decided to make the switch to using Streaming Enabled, and haven’t had much problem refactoring all the client code.
However, I’ve been unable to come up with a reliable solution for a spectate system, and this is really important as some devproducts are tied to it.
I’ve got to mention that I did come up with a solution, but it has some specific problems that I’m hoping I can get feedback on.
It is a combined implementation based on these threads: (only ones existing about the topic)
The gist of how it works is that the the GUI picks a Target (player) and fires a remote to the server, then attempts to set the CameraSubject
to that target every renderstep.
Afterwards, a separate, serversided script detects when this target has changed, and does two things:
-
RequestStreamAroundAsync
the area around the Target -
Sets the spectator’s
ReplicationFocus
to the Target (HumanoidRootPart)
Heres the scripts:
--[[SPECTATE GUI]]
... --//inside RenderStepped
local playerSpectating = playerlist[current] or player :: Player
if playerSpectating == lastplayer then
SetSubject(lastplayer) --camera.CameraSubject
return
end
lastplayer = playerSpectating
frame.Spectate.Current.Text = playerSpectating.DisplayName
game.ReplicatedStorage.TargetPick:FireServer(playerSpectating.Name)
--[[SERVER SCRIPT]]
function StreamAroundTarget(plr:Player)
local char = plr.Character or plr.CharacterAdded:Wait()
local part = char.PrimaryPart or char:FindFirstChildOfClass('BasePart')
plr:RequestStreamAroundAsync(part.Position,1.5)
return part
end
game:GetService('Players').PlayerAdded:Connect(function(plr)
local target:ObjectValue = plr:WaitForChild('Target',20)
local lastcnc :RBXScriptSignal|nil = nil
target.Changed:Connect(function(value: Instance)
if not value or not value:IsA('Player') then return end
if lastcnc then lastcnc:Disconnect(); lastcnc=nil end
local focus = nil
if value ~= plr then
focus = StreamAroundTarget(value)
if target.Value ~= value then return end
lastcnc = value.CharacterAdded:Connect(function()
print(`[{plr.Name}] refresh focus {value.Name}`)
plr.ReplicationFocus = StreamAroundTarget(value)
end)
end
plr.ReplicationFocus = focus
end)
end)
The problem I seem to be having with this approach is that the CameraSubject
isn’t changing to the target unless the target respawns, or is close enough so that they were already streamed in. Ocassionally, it seems to work as intended but severly delayed (10+ secs).
I am not entirely sure what precisely is causing the issue as I’m fairly new to using streaming enabled.
Finally, I’m greatful for any suggestions or help offered, and I hope this can also serve useful for people coming across this. (as spectating is fairly common in games)
I will also be posting a video shortly to further illustrate the problem.