Player Spectate Breaking

FOR STARTERS
I’m aware that this sort of spectate isnt very efficient for games, this is just to do some testing and explore in scripting.

SCRIPTS

LOCAL SCRIPT

--Getting the player. Simple enough right?
local player = game.Players.LocalPlayer
local char = player.CharacterAdded:Wait()
char.Archivable =true

--Getting a random screen. Handled by the server so the taken ones aren't overlapped
local screen = game.ReplicatedStorage.GetScreen:InvokeServer()

--[[This links the gui (which is on a part) to a part. Links by number
which I've already sorted out.]]
local PGUI = player.PlayerGui['S' ..string.sub(screen.Name,5,5)]
print(PGUI.Name)
PGUI.Adornee = screen
game.Workspace.Model:Clone().Parent = PGUI.ViewportFrame

--this makes a camera to link to the player, tracking movement
local cam = Instance.new("Camera",workspace)
PGUI.ViewportFrame.CurrentCamera = cam
print(cam)

--[[I know it seems weird that firing the server will fire all clients, but
it's so that everyone gets this information. If there's a more viable way 
feel free to let me know]]
game.ReplicatedStorage.UpdateAllScreens:FireServer(player,screen,PGUI,cam)
game.ReplicatedStorage.UpdateAllScreens.OnClientEvent:Connect(function(plr,screen,gui,cam)
	plr.PlayerGui[gui.Name].Adornee = screen
	plr.PlayerGui[gui.Name].ViewportFrame.CurrentCamera = cam
	
end)

--This is pretty straight forward, it sticks the camera to the player
game:GetService("RunService").RenderStepped:Connect(function()
	cam.CFrame = char.HumanoidRootPart.CFrame
end)

This runs in the StarterPlayer

SERVER SCRIPT

--[[A table to hold screens. It's more comforting to have it like this
I don't know why.]]

local screens = {}
local pick = game.Workspace.Screens:GetChildren()

--[[Transfers all the screens into a seperate table, before sorting them by name]]
for i, v in pairs(pick) do
	table.insert(screens,pick[i])
end

--[[A created function to get the screen number in the table so I can remove it once 
	it's taken]]
local function GetScreenNum(screen)
	for i,v in pairs(screens) do
		if v == screen then
			return i
		end
	end
end

--Putting them in order...
table.sort(screens,function(a,b) return a.Name < b.Name end)

--[[This is the return for the invoke from the LocalScript. Gives a random screen
then removes it from the pickable screens]]
game.ReplicatedStorage.GetScreen.OnServerInvoke = function(plr)
	print('Request recieved from',plr)
	local screen = screens[math.random(1,#screens)]
	
	local num = GetScreenNum(screen)
	table.remove(screens,num)
	
	return screen
end

--[[Once it gets the fire, it spreads the information out to all the other
	clients, where they will update the viewport information for themselves
--]]
game.ReplicatedStorage.UpdateAllScreens.OnServerEvent:Connect(function(player,plr,screen,Gui,cam)
	print(plr, screen, Gui)
	game.ReplicatedStorage.UpdateAllScreens:FireAllClients(plr,screen,Gui,cam)
end)

This is in the Server, and is supposed to work with the local script.

PROBLEM

The problem I have is that the viewport doesn’t render. I have a feeling this has something to do with the camera belonging to another client or something, I don’t know but here’s what happens when I play it through:

Could I get some clarification as to what I’m doing wrong in these 2 scripts? Thanks!

1 Like

Okay I see a minor issue which may or may not be the cause of your issue. Have a go with this!

game:GetService("RunService").RenderStepped:Connect(function()
   cam.CameraType = Enum.CameraType.Scriptable
   cam.CameraSubject = camsubj

Don’t forget to define camsubj or set CameraSubject to a different object or variable!

Hope this helps!

The problem still occurs, thanks though.

Viewport Frames do not work on Surface Guis. Use them in a Screen Gui, as they are in Beta and do not currently work on Surface Guis.


What you could do is make a similar Screen Gui pop up and display the multiple viewport frames.

If you have any more questions, feel free to ask them.

They do work on surface guis, they have to be placed into the startergui and the gui and the adornee has to be the part.



this works, but it wont track the rendering for other players if they join

Looks like Roblox updated viewport frames. That’s great!


Now, to address your problem. You are sending the clients camera to the server, which is local to that client only (FE barrier prevents the clients objects from replicating to the server). This value will be nil, so the other clients won’t receive the camera. What you should do is take the cframe and send it across to the server, which sends it to the clients. Then have the clients create the camera for themselves.

game.ReplicatedStorage.UpdateAllScreens:FireServer(player,screen,PGUI,cam)
--Change To:
game.ReplicatedStorage.UpdateAllScreens:FireServer(player,screen,PGUI,cam.CFrame)

I’m not sure if CFrames are userdata, but if they are then you will have to convert the cframe to a array of numbers. Best of luck, good luck.

If you have any more questions, feel free to ask them.

But I’m trying to set a camera for all the clients. Doesn’t that mean that if I sent the CFrame, it would be one camera constantly changing all the CFrame, and just constantly changing? I’m guessing that I would have to make all the cameras on the server then? Then update their positions replicated to the clients?

How to solve the problem

Make the cameras on each individual client. Send the CFrame over to the server, which the server sends to the clients to turn into a camera. Then use this local camera.

You would need a individual camera for each frame.


Why this happens

The user’s camera is local to them and them only. In a filtering enabled environment, anything from the client will not replicate to the server. Therefore, the server doesnt see this camera object. Instead, pass the CFrame (a value, that the server can send) to the server, and have them pass that to the clients. Then, the clients create their own camera locally, solving the problem of replication.

This post was edited for clarity. If you have any further questions, feel free to ask them

1 Like