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)
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.
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
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)
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.
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.