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?

1 Like

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

2 Likes

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)

1 Like

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
2 Likes

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:

2 Likes

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)
1 Like

I know its been a while but I got my solution here.

1 Like

Because this topic was based around the code in the original post, I’d recommend changing the solution back to what was previously marked as the solution.

Even though the posts here and in the other topic you linked provided the exact same solution of using Player:RequestStreamAroundAsync(), it’d be a bit misleading for developers who read through this topic in the future to see that the marked solution is for a completely different spectating system from 2 months after the initial post, when the posts here provided code revisions that were specific to the codeblock within the original post.

1 Like

there wasnt a solution before tho its been unsolved.

1 Like

Oh, I must have misremembered then; I thought one of the posts was marked as a solution and that you just didn’t reply to any of them afterwards. Especially since it went a long time without any new posts (I also never got a notification for the update 2 months later), after a while I presumed that the issue outlined in the initial post as it pertains to the original codeblock was eventually resolved.


So that means that you had tried to implement the suggestions in the following two posts and neither ended up working for the original code?

  • If that was the case, do you remember if any errors appeared in the Output, or if there was any unexpected behavior when testing out those changes, as well as what led you to decide to create a new spectating system, instead?

    That may be useful to know for other developers who are planning on creating a spectating system in a game that has StreamingEnabled turned on, especially for understanding what made the difference between the original spectating system and the newer one, given that Player:RequestStreamAroundAsync() was recommended in both topics.

Referenced posts

1 Like

so what ur saying is that i should make this the solution?

Whichever reply most accurately answered the initial question (based on what was included in the original post, since that is what the topic was created around) should be marked as the solution, which is up to you.

But if none of the posts in this topic ended up being a solution for the original code, which is what you mentioned earlier, then please refer to the questions I asked at the end of my last post:

Um you could also change the ReplicationFocus to the players RootPart that you are spectating. Just add a remote through which the player will fire to the server to change the ReplicationFocus. If you want to set the ReplicationFocus back to the your player, just set it as nil.

1 Like