Camera Manipulation not Stopping for some players

So basically, in a serverscript, it waits until a player is added then fires a remote event to the frame of the starting gui, and a remote event that starts the camera manipulation. When the player chooses a player by clicking one of the three gui button, it fires a remote event to stop the camera manipulation. The problem is it doesn’t stop for some players. (Player1: Doesn’t work Player2: Works Player3: Doesn’t work.)

-- Camera manipulation local script in frame of starting gui
game.ReplicatedStorage.Other2.OnClientEvent:Connect(function()
local CamTween = true
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
 
local target = workspace:FindFirstChild("CamPart")  -- The object to rotate around
local camera = workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable
local rotationAngle = Instance.new("NumberValue")
local tweenComplete = false
 
local cameraOffset = Vector3.new(0, 10, 12)
local rotationTime = 15  -- Time in seconds
local rotationDegrees = 360
local rotationRepeatCount = -1  -- Use -1 for infinite repeats
local lookAtTarget = true  -- Whether the camera tilts to point directly at the target
 
local function updateCamera()
	if not target then return end
	camera.Focus = target.CFrame
	local rotatedCFrame = CFrame.Angles(0, math.rad(rotationAngle.Value), 0)
	rotatedCFrame = CFrame.new(target.Position) * rotatedCFrame
	camera.CFrame = rotatedCFrame:ToWorldSpace(CFrame.new(cameraOffset))
	if lookAtTarget == true then
		camera.CFrame = CFrame.new(camera.CFrame.Position, target.Position)
	end
end
 
-- Set up and start rotation tween
local tweenInfo = TweenInfo.new(rotationTime, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, rotationRepeatCount)
local tween = TweenService:Create(rotationAngle, tweenInfo, {Value=rotationDegrees})
tween.Completed:Connect(function()
	tweenComplete = true
end)
tween:Play()
game.ReplicatedStorage.StopCameraManip.Event:Connect(function()
   if CamTween == true then
      CamTween = false
      tween:Cancel()
   end
end)
 
-- Update camera position while tween runs
RunService.RenderStepped:Connect(function()
	if tweenComplete == false then
		updateCamera()
	end
end)
end)

If you don’t understand please feel free to ask questions

Could anyone please help me out here? Thanks!

First of all, I would maybe recommend not having two separate events specifically for camera commands from the server - the server shouldn’t really care what the client is doing with its camera.

Just have the client always start off manipulating its own camera, and then when it clicks a button and gets an OK response from the server to move on to the next step or whatever, it can just stop it itself.

Anyways, I would recommend structuring your code more like this if you really wanna do it this way – store the event itself and connect/disconnect it at will:

local connection = nil;

function updateCamera()
    --- etc.
end

game.ReplicatedStorage.StartCameraManip.Event:Connect(function()
    if (not connection) then
        connection = RunService.RenderStepped:Connect(updateCamera)
    end
end)

game.ReplicatedStorage.StopCameraManip.Event:Connect(function()
    connection:Disconnect()
    connection = nil
end)

edit: Oh also since this isn’t code review – to fix your current code, try just moving the StopCameraManip outside of the Other2 block (so they’re “siblings”). Also move the updateCamera function and the tweenComplete variable one level up as well.

So like this?

local RunService = game:GetService("RunService")
local connection = nil;
game.ReplicatedStorage.Other2.OnClientEvent:Connect(function()

function UpdateCamera()
   local CamTween = true
local TweenService = game:GetService("TweenService")
 
local target = workspace:FindFirstChild("CamPart")  -- The object to rotate around
local camera = workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable
local rotationAngle = Instance.new("NumberValue")
local tweenComplete = false
 
local cameraOffset = Vector3.new(0, 10, 12)
local rotationTime = 15  -- Time in seconds
local rotationDegrees = 360
local rotationRepeatCount = -1  -- Use -1 for infinite repeats
local lookAtTarget = true  -- Whether the camera tilts to point directly at the target
 
local function updateCamera()
	if not target then return end
	camera.Focus = target.CFrame
	local rotatedCFrame = CFrame.Angles(0, math.rad(rotationAngle.Value), 0)
	rotatedCFrame = CFrame.new(target.Position) * rotatedCFrame
	camera.CFrame = rotatedCFrame:ToWorldSpace(CFrame.new(cameraOffset))
	if lookAtTarget == true then
		camera.CFrame = CFrame.new(camera.CFrame.Position, target.Position)
	end
end
 
-- Set up and start rotation tween
local tweenInfo = TweenInfo.new(rotationTime, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, rotationRepeatCount)
local tween = TweenService:Create(rotationAngle, tweenInfo, {Value=rotationDegrees})
tween.Completed:Connect(function()
	tweenComplete = true
end)
tween:Play() 
-- Update camera position while tween runs
RunService.RenderStepped:Connect(function()
	if tweenComplete == false then
		updateCamera()
	end
end)
end
end)

game.ReplicatedStorage.Other2.OnClientEvent:Connect(function()
    if (not connection) then
        connection = RunService.RenderStepped:Connect(UpdateCamera)
    end
end)

game.ReplicatedStorage.StopCameraManip.Event:Connect(function()
    connection:Disconnect()
    connection = nil
end)

EDIT: Never mind it doesn’t work :confused:

Kinda close, try something like this. I simplified some things and fixed others. I didn’t test this, so there might be typos.

local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")

local cameraOffset = Vector3.new(0, 10, 12)
local rotationTime = 15 -- Time in seconds

local target = workspace:FindFirstChild("CamPart") -- The object to rotate around

local connection = nil
local camera = workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable

-- set up tween (to reuse)
local rotationAngle = Instance.new("NumberValue")
local tweenInfo = TweenInfo.new(rotationTime, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, -1)
local tween = TweenService:Create(rotationAngle, tweenInfo, {Value = 360})

local function updateCamera()
    -- simplified this
    camera.CFrame = target.CFrame * CFrame.Angles(0, rotationAngle.Value, 0) * CFrame.new(cameraOffset)
end

game.ReplicatedStorage.StartCameraManip.Event:Connect(function()
    if (not connection) then
        rotationAngle.Value = 0
        tween:Play()
        connection = RunService.RenderStepped:Connect(updateCamera)
    end
end)

game.ReplicatedStorage.Other2.Event:Connect(function()
    if (connection) then
        tween:Pause()
        connection:Disconnect()
        connection = nil
    end
end)

Also – do you need the tween? You could do this a little easier if you just change the angle each frame yourself.

Here’s the same thing without using TweenService:

local RunService = game:GetService("RunService")

local cameraOffset = Vector3.new(0, 10, 12)
local rotationTime = 15 -- Time in seconds

local target = workspace:FindFirstChild("CamPart") -- The object to rotate around

local connection = nil
local camera = workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable

-- set up tween (to reuse)
local rotationAngle = 0
local startTime = 0

local function updateCamera()
    local angle = math.rad(360) * (tick() - startTime) / rotationTime
    camera.CFrame = target.CFrame * CFrame.Angles(0, angle, 0) * CFrame.new(cameraOffset)
end

game.ReplicatedStorage.StartCameraManip.Event:Connect(function()
    if (not connection) then
        startTime = tick()
        connection = RunService.RenderStepped:Connect(updateCamera)
    end
end)

game.ReplicatedStorage.Other2.Event:Connect(function()
    if (connection) then
        connection:Disconnect()
        connection = nil
    end
end)

Its works but every time a player joins everyone sees the camera manip again

-- serverscript
game.Players.PlayerAdded:Connect(function()
    game.ReplicatedStorage.Other:FireAllClients()
    game.ReplicatedStorage.Other2:FireAllClients()
    game.ReplicatedStorage.ShowGui:FireAllClients()
end)
-- camera manip local script
local RunService = game:GetService("RunService")

local cameraOffset = Vector3.new(0, 10, 12)
local rotationTime = 15 -- Time in seconds

local target = workspace:FindFirstChild("CamPart") -- The object to rotate around

local connection = nil
local camera = workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable

-- set up tween (to reuse)
local rotationAngle = 0
local startTime = 0

local function updateCamera()
    local angle = math.rad(360) * (tick() - startTime) / rotationTime
    camera.CFrame = target.CFrame * CFrame.Angles(0, angle, 0) * CFrame.new(cameraOffset)
end

game.ReplicatedStorage.Other2.OnClientEvent:Connect(function()
    if (not connection) then
        startTime = tick()
        connection = RunService.RenderStepped:Connect(updateCamera)
    end
end)

game.ReplicatedStorage.StopCameraManip.Event:Connect(function()
    if (connection) then
        connection:Disconnect()
        connection = nil
    end
end)

Also could you explain to me how your camera manip code works?

Well of course everyone sees it, you’re calling FireAllClients :slight_smile:

Replace the top part with something like:

game.Players.PlayerAdded:Connect(function(player)
    game.ReplicatedStorage.Other:FireClient(player)
    game.ReplicatedStorage.Other2:FireClient(player)
    game.ReplicatedStorage.ShowGui:FireClient(player)
end)

Again though, I don’t think it’s really necessary to have this stuff go through the RemoteEvent. Why not just have the Player start these things in a LocalScript as soon as it joins? It doesn’t need the server to tell it “Hey you just joined a game”.

And sure, here I’ve commented it:

-- camera manip local script
local RunService = game:GetService("RunService")

local cameraOffset = Vector3.new(0, 10, 12)
local rotationTime = 15 -- Time in seconds

local target = workspace:FindFirstChild("CamPart") -- The object to rotate around

-- keep track of the Connection object for RenderStepped
local connection = nil

-- set up the camera
local camera = workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable

-- keep track of the camera's current angle
local rotationAngle = 0

-- keep track of when we started the rotation (in seconds).
-- update this whenever Other2 is invoked
local startTime = 0

-- called every frame
local function updateCamera()
    -- calculate the angle by doing the following:
    -- 1. figure out how much time has passed since we started (tick() - startTime)
    -- 2. multiply by 360 degrees - convert to radians bc that's what roblox uses
    --        (math.rad(360))
    -- 3. divide by rotationTime to slow it down
    local angle = math.rad(360) * (tick() - startTime) / rotationTime

    -- set the camera cframe:
    -- 1. set to the target CFrame
    -- 2. rotate it about the Y axis
    -- 3. back it up by the cameraOffset
    camera.CFrame = target.CFrame * CFrame.Angles(0, angle, 0) * CFrame.new(cameraOffset)
end

-- called when the server tells us to animate
game.ReplicatedStorage.Other2.OnClientEvent:Connect(function()
    if (not connection) then -- confirm we have a connection object
        -- set the time for the start of the animation
        -- tick() returns the number of seconds since 1/1/1970
        -- (see https://devforum.roblox.com/t/tick-how-do-i-use-it/422677)
        startTime = tick()

        -- call updateCamera each frame - save the connection object for later
        connection = RunService.RenderStepped:Connect(updateCamera)
    end
end)

game.ReplicatedStorage.StopCameraManip.Event:Connect(function()
    if (connection) then
        -- kill the connection (stop updating)
        connection:Disconnect()
        connection = nil
    end
end)
1 Like