How to fix spectate script with StreamingEnabled on?

StreamingEnabled ruins my spectate script but I need it for the game I’m making so it won’t be laggy. So is there any way to make the script work while StreamingEnabled is on?

ClientScript:

local toggle = script.Parent.DeathFrame.SpectateButton
local frame = script.Parent.Bar
local previous = frame.Previous
local nextbutton = frame.Next
local status = frame.Title
local camera = game.Workspace.CurrentCamera
local num = 1
local players = game:GetService("Players")
local plr = players.LocalPlayer
local char = plr.Character
local hum = char:WaitForChild("Humanoid")

local function SetCam(n)
	local p = workspace.AlivePlayers:GetChildren()[n]
	status.Text = p.Name
	workspace.CurrentCamera.CameraSubject = p.Value.Character.Humanoid
end

local function unlockMouse()
	local thing = Instance.new("TextButton")
	thing.Parent = script.Parent
	thing.Modal = true
	thing.Size = UDim2.new(0,0,0,0)
	thing.Name = "Unlocker"
	thing.Text = ""
end

hum.Died:connect(function()
	print("died")
	script.Parent.Enabled = true
	unlockMouse()
end)


status.Text = game.Players.LocalPlayer.Name

toggle.MouseButton1Click:Connect(function()
	frame.Visible = not frame.Visible
	script.Parent.DeathFrame.Visible = false
end)

previous.MouseButton1Click:Connect(function()
	if num > 1 then
		num = num - 1
	else
		num = #workspace.AlivePlayers:GetChildren()
	end
	SetCam(num)
end)

nextbutton.MouseButton1Click:Connect(function()
	if num < #workspace.AlivePlayers:GetChildren() then
		num = num + 1
	else
		num = 1
	end
	SetCam(num)
end)

1 Like

Which part of the script breaks? Is there an error outputted?

No like the spectate works, its just while spectating the unloaded objects stay unloaded and never loads. It’s because of the StreamingEnabled.

1 Like

With StreamingEnabled, you need to explicitly request certain parts of the game to load, particularly the character models and their components.

local toggle = script.Parent.DeathFrame.SpectateButton
local frame = script.Parent.Bar
local previous = frame.Previous
local nextbutton = frame.Next
local status = frame.Title
local camera = game.Workspace.CurrentCamera
local num = 1
local players = game:GetService("Players")
local plr = players.LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()
local hum = char:WaitForChild("Humanoid")

local function SetCam(n)
    local alivePlayers = workspace:WaitForChild("AlivePlayers"):GetChildren()
    if #alivePlayers > 0 then
        local p = alivePlayers[n]
        status.Text = p.Name

        local character = p:WaitForChild("Character", 10)
        if character then
            -- Request to stream around the target player
            p:RequestStreamAroundAsync()
            -- Wait for the character and humanoid to be fully loaded
            character:WaitForChild("Humanoid", 10)
            -- Set the camera subject to the target player's humanoid
            workspace.CurrentCamera.CameraSubject = character.Humanoid
        else
            warn("Character not found for player " .. p.Name)
        end
    else
        warn("No alive players found")
    end
end

local function unlockMouse()
    local thing = Instance.new("TextButton")
    thing.Parent = script.Parent
    thing.Modal = true
    thing.Size = UDim2.new(0,0,0,0)
    thing.Name = "Unlocker"
    thing.Text = ""
end

hum.Died:Connect(function()
    print("died")
    script.Parent.Enabled = true
    unlockMouse()
end)

status.Text = game.Players.LocalPlayer.Name

toggle.MouseButton1Click:Connect(function()
    frame.Visible = not frame.Visible
    script.Parent.DeathFrame.Visible = false
end)

previous.MouseButton1Click:Connect(function()
    local alivePlayers = workspace:WaitForChild("AlivePlayers"):GetChildren()
    if #alivePlayers > 0 then
        if num > 1 then
            num = num - 1
        else
            num = #alivePlayers
        end
        SetCam(num)
    else
        warn("No alive players to spectate")
    end
end)

nextbutton.MouseButton1Click:Connect(function()
    local alivePlayers = workspace:WaitForChild("AlivePlayers"):GetChildren()
    if #alivePlayers > 0 then
        if num < #alivePlayers then
            num = num + 1
        else
            num = 1
        end
        SetCam(num)
    else
        warn("No alive players to spectate")
    end
end)

That is not true 100% of the time. Streaming is dynamic and it happens automatically. One of the exceptions for this would be if you used the Player:RequestStreamAroundAsync() method with a specific Vector3, which can be called manually to try to stream in a specific area of the game (although this is not necessary for streaming to function at all times).

*Important to note that :RequestStreamAroundAsync() doesn’t work unless you specify a Vector3.


You can set the ModelStreamingMode to Persistent for each player’s Character model when it spawns in. This will ensure that from the perspective of each player, all other Character models will always be streamed in / visible in the Workspace, no matter how far away they are.

As a result, when the spectating system tries to update the CameraSubject to another player’s Character, it’ll be able to do so, and then it’ll stream in the area around them if it wasn’t loaded already.


In order to do this, listen for the CharacterAdded event to fire for each player in a singular server-sided script:

Example

-- Code for a Server Script in the ServerScriptService
local Players = game:GetService("Players")

local function onPlayerJoin(player)
    if player.Character then
        player.Character.ModelStreamingMode = Enum.ModelStreamingMode.Persistent
    end

    player.CharacterAdded:Connect(function(Character)
        Character.ModelStreamingMode = Enum.ModelStreamingMode.Persistent
    end)
end

for _, player in Players:GetPlayers() do
    task.spawn(onPlayerJoin, player)
end

Players.PlayerAdded:Connect(onPlayerJoin)

(Optional LocalScript change)

In addition to this, the spectating system code in the LocalScript could be slightly modified to make use of Player:RequestStreamAroundAsync(), so that when the camera is about to go to the next player’s Character, the game can begin to stream in that area of the map so it takes less time for the map to appear from the perspective of the spectating player:

-- Optional changes to the "SetCam" function in the LocalScript from original post
local function SetCam(n)
    local alivePlayers = workspace.AlivePlayers:GetChildren()
    local otherPlayer = alivePlayers[n]

    if not otherPlayer then return end

    local otherCharacter = otherPlayer.Character or otherPlayer.CharacterAdded:Wait()
    local otherHumanoid = otherCharacter:FindFirstChildOfClass("Humanoid")

    if otherHumanoid then
        status.Text = otherPlayer.Name

        local newCFrame = otherCharacter:GetPivot()
        local newPosition = newCFrame.Position

        plr:RequestStreamAroundAsync(newPosition)
        workspace.CurrentCamera.CameraSubject = otherHumanoid
    end
end
1 Like

Hello, I had the first ever support topic covering this issue, the easiest solution is what bigeman suggested,
you can just add a script on starterplayer → startercharacterscripts like so:

local character = script.Parent
character.ModelStreamingMode = Enum.ModelStreamingMode.Persistent

--[[
this solution is enough for it to work again,
but consider upgrading it by using a remote
to set the .ReplicationFocus to the target,
as well as :RequestStreamAroundAsync()
]]

alternatively, you can use PersistentPerPlayer for a more customizable solution (control which specific players have that model streamed in, instead of having everyone streaming in everyone)

you can read more about it in the topic:

1 Like

I now have a different spectate system script but still how can I do it with streaming enabled?

local toggle = script.Parent.DeathScreen.Spectate
local frame = script.Parent.SpectateFrame
local previous = frame.Prev
local next = frame.Next
local status = frame.Status.CurrentPlayer
local camera = game.Workspace.CurrentCamera
local num = 1

status.Text = game.Players.LocalPlayer.Name

toggle.MouseButton1Click:Connect(function()
	frame.Visible = not frame.Visible
	script.Parent.DeathScreen.Visible = false
end)

previous.MouseButton1Click:Connect(function()
	local players = game.Players:GetChildren()
	local max = #players
	num = num - 1
	if num < 1 then
		num = max
	end
	local player = players[num]
	camera.CameraSubject = player.Character.Humanoid
	status.Text = player.Name
end)

next.MouseButton1Click:Connect(function()
	local players = game.Players:GetChildren()
	local max = #players
	num = num + 1
	if num > max then
		num = 1
	end
	local player = players[num]
	camera.CameraSubject = player.Character.Humanoid
	status.Text = player.Name
end)

frame.Changed:Connect(function()
	if not frame.Visible then
		camera.CameraSubject = game.Players.LocalPlayer.Character.Humanoid
		status.Text = game.Players.LocalPlayer.Name
	end
end)