Camera Script does not work after resetting

I am currently learning how to manipulate the camera. So far, I tried creating my own camera based on the code from this article. It works beautifully but after resetting the character the camera gets stuck.

local Players = game:GetService("Players")
local ContextActionService = game:GetService("ContextActionService")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
 
local camera = workspace.CurrentCamera
local cameraOffset = Vector3.new(2, 1.7, 5)
local player = Players.LocalPlayer
 
player.CharacterAdded:Connect(function(character)
	local humanoid = character:WaitForChild("Humanoid")
	local rootPart = character:WaitForChild("HumanoidRootPart")
 
	local cameraAngleX = 0
	local cameraAngleY = 0
 
	local function playerInput(actionName, inputState, inputObject)
		-- Calculate camera/player rotation on input change
		if inputState == Enum.UserInputState.Change then
			cameraAngleX = cameraAngleX - inputObject.Delta.X*.4
			-- Reduce vertical mouse/touch sensitivity and clamp vertical axis
			cameraAngleY = math.clamp(cameraAngleY-inputObject.Delta.Y*0.4, -75, 75)
		end
	end
	ContextActionService:BindAction("PlayerInput", playerInput, false, Enum.UserInputType.MouseMovement, Enum.UserInputType.Touch)
 
	RunService.RenderStepped:Connect(function()
		if camera.CameraType ~= Enum.CameraType.Scriptable then
			camera.CameraType = Enum.CameraType.Scriptable
		end
		local startCFrame = CFrame.new((rootPart.CFrame.Position)) * CFrame.Angles(0, math.rad(cameraAngleX), 0) * CFrame.Angles(math.rad(cameraAngleY), 0, 0)
		local cameraCFrame = startCFrame:ToWorldSpace(CFrame.new(cameraOffset.X, cameraOffset.Y, cameraOffset.Z))
		local cameraFocus = startCFrame:ToWorldSpace(CFrame.new(cameraOffset.X, cameraOffset.Y, -10000))
		camera.CFrame = CFrame.new(cameraCFrame.Position, cameraFocus.Position)
	end)
end)
 
local function focusControl(actionName, inputState, inputObject)
	-- Lock and hide mouse icon on input began
	if inputState == Enum.UserInputState.Begin then
		UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
		UserInputService.MouseIconEnabled = false
		ContextActionService:UnbindAction("FocusControl", focusControl, false, Enum.UserInputType.MouseButton1, Enum.UserInputType.Touch, Enum.UserInputType.Focus)
	end
end
ContextActionService:BindAction("FocusControl", focusControl, false, Enum.UserInputType.MouseButton1, Enum.UserInputType.Touch, Enum.UserInputType.Focus)
1 Like

Have you tried moving the function outside of CharacterAdded.
My guess is that the function get’s destroyed when resetting because it’s an instance only inside the scope of CharacterAdded.

1 Like

Can you please share the errors you are getting? Additionally, where is this script located in your game?

1 Like

Here’s the problem:
robloxapp-20200406-1743221.wmv (1.2 MB)

The local script is located in StarterPlayerScripts. I am not getting errors unfortunately.

1 Like

What do you mean by this? Please explain further.

1 Like

Unfortunately I can’t view the video on mobile, but based on what you’ve told me this sounds like an issue within the CharacterAdded event. Within the CharacterAdded event try setting the camera variable again. Additionally, make sure you disconnect old RenderStepped events and make sure you’re binding keys as many times as you want to. The issue may that you’re binding over top of a bind, and the old one takes priority. Try moving your angle variables outside of CharacterAdded as well as your action bind. In fact, I don’t even think you really need much code in CharacterAdded other than changing the character variable

2 Likes

I tried setting the camera variable inside CharacterAdded. Sadly, nothing has occurred.

Unfortunately I can’t view the video on mobile

robloxapp-20200406-1743221

Additionally, make sure you disconnect old RenderStepped events and make sure you’re binding keys as many times as you want to.

What do you mean by “disconnect old RenderStepped events”? How do I perform that?

1 Like

instead of:

player.CharacterAdded:Connect(function(character)

You could try to create a function:

function FUNCTIONNAME()
end

And then activating that function when the player is added

1 Like

Using :Disconnect().

local connection

--later on when you get to the RenderStepped
connection = RunService.RenderStepped:Connect(function()
	if camera.CameraType ~= Enum.CameraType.Scriptable then
		camera.CameraType = Enum.CameraType.Scriptable
	end
	local startCFrame = CFrame.new((rootPart.CFrame.Position)) * CFrame.Angles(0, math.rad(cameraAngleX), 0) * CFrame.Angles(math.rad(cameraAngleY), 0, 0)
	local cameraCFrame = startCFrame:ToWorldSpace(CFrame.new(cameraOffset.X, cameraOffset.Y, cameraOffset.Z))
	local cameraFocus = startCFrame:ToWorldSpace(CFrame.new(cameraOffset.X, cameraOffset.Y, -10000))
	camera.CFrame = CFrame.new(cameraCFrame.Position, cameraFocus.Position)
end)

character.Died:Connect(function() --when character dies
     connection:Disconnect() --disconnect that old event
end)

Or actually, since you’re using CharacterAdded anyways

local connection

player.CharacterAdded:Connect(function(character)
    if connection then --if there was a connection before char respawned
       connection:Disconnect() --remove that connection
    end

	local humanoid = character:WaitForChild("Humanoid")
	local rootPart = character:WaitForChild("HumanoidRootPart")
 
	local cameraAngleX = 0
	local cameraAngleY = 0
 
	local function playerInput(actionName, inputState, inputObject)
		-- Calculate camera/player rotation on input change
		if inputState == Enum.UserInputState.Change then
			cameraAngleX = cameraAngleX - inputObject.Delta.X*.4
			-- Reduce vertical mouse/touch sensitivity and clamp vertical axis
			cameraAngleY = math.clamp(cameraAngleY-inputObject.Delta.Y*0.4, -75, 75)
		end
	end
	ContextActionService:BindAction("PlayerInput", playerInput, false, Enum.UserInputType.MouseMovement, Enum.UserInputType.Touch)
 
	connection = RunService.RenderStepped:Connect(function()
		if camera.CameraType ~= Enum.CameraType.Scriptable then
			camera.CameraType = Enum.CameraType.Scriptable
		end
		local startCFrame = CFrame.new((rootPart.CFrame.Position)) * CFrame.Angles(0, math.rad(cameraAngleX), 0) * CFrame.Angles(math.rad(cameraAngleY), 0, 0)
		local cameraCFrame = startCFrame:ToWorldSpace(CFrame.new(cameraOffset.X, cameraOffset.Y, cameraOffset.Z))
		local cameraFocus = startCFrame:ToWorldSpace(CFrame.new(cameraOffset.X, cameraOffset.Y, -10000))
		camera.CFrame = CFrame.new(cameraCFrame.Position, cameraFocus.Position)
	end)
end)
5 Likes

That is just another way of writing the code. It would just yield the same results since the :Connect() method takes a function as an argument.

I wouldn’t mind @Hexcede getting the solution! Because he’s the one that took time to diagnose and actually discover the problem with your code.

Thank you for the solution! I have a follow up question though. Why is there a need to call :Disconnect()?

If you were to break down what’s going on, :Connect()ing an event, is like plugging a cable. If you :Connect() an event, that event will always listen for certain things to happen in order to fire until you :Disconnect() it so it stops waiting for stuff to happen. Just like a cable, the cable would always function until you unplug it.

What’s happening in your situation is, when the character spawns for the first time, you Connected the RenderStepped event once, what happens if he spawns in a second time? Welp the CharacterAdded event will fire again, making another RenderStepped connection! We have two now. And so on. Since previous RenderStepped connections aren’t removed (the cables aren’t unplugged!), they will keep on firing even after the character dies, which is why you need to disconnect the current connection, to leave a place for a new one that’s added when he respawns.

5 Likes

Cool! Again, thank you very much @starmaq and @Hexcede! I spent 3 days browsing the forum just to post this hahahahahahahahaha

1 Like

No, the difference is that the functions would be declared in different scopes.

In that case you would still be defining the function in the same scope. A function call won’t create a new scope (it doesn’t need to) since it’s just accepting values rather than executing a block of code. Within an anonymous function you will have another scope, however, it works the same if the function is defined in a variable beforehand. Generally different scopes will not yield any issues unless you have defined local variables after you have defined your function. This is because the scope of your function has been declared before the variable has been declared, thus the variable is only “available” to scopes after.

Example:

local someValue
print(function()
    someValue = true
end)

-- Is the same (in code functionality, technically things do behave differently in terms of GC) as
local someValue
local someFunction = function()
    someValue = true
end
print(someFunction)

-- Is not the same as (function scope declared before someValue declared)
local someFunction = function()
    someValue = true
end
local someValue
print(someFunction)

So in summary, the scope itself is not different, however, you are probably referring to the placement of the function’s scope, which would have an effect depending on the placement of upvalues. In this context, the placement of the function isn’t important to the issue since all of the function’s upvalues were declared above the function declaration.

1 Like