I have been making a first person camera script for a game I’ve been making. I needed to create a custom one because its not attached to the characters players actual player model, and you’re supposed to be sitting in the game. So i spent a while making it, and it turned out alright, and feels fine to use, but I’ve been experiencing small issues so far. More specifically one I would call ‘mouse jumps’ where when you’re moving your mouse in one direction, it will suddenly jumps a distance ahead in the direction it was already moving. This makes it feel super weird and inconsistent when using because it even triggers when im moving it the smallest amount. For example if I have my mouse still, and adjust it to the left a bit and it happens, it will jump forward a bunch. I’ve spent a while trying to figure out why this is happening, but haven’t been able to find a solution. I’m also not the best at math when it comes to this kinda stuff with mouse deltas so if it’s something obvious that I missed I apologize. One last thing, this is a module script, which is why it returns module at the end, i have it like this because I have multiple different camera scripts I need to alternate between. The camera module script gets called every render stepped by its local script parent, so all of that should be working properly. Any help would be greatly appreaciated.
local module = {}
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local camera = game.Workspace.CurrentCamera
local userinputservice = game:GetService("UserInputService")
local Player = game.Workspace.GameEnv.Players.Player
local Head = game.Workspace.GameEnv.Players.Player.Head
--Deltas/values
local maxRotationAngle = math.rad(30)
local Sens = 0.1
local Smoothness = 0.1
local Pos
local Delta
--angles
local Direction = Vector2.new()
local Angle = CFrame.new(0, 0, 0)
local TargetAngle = Vector2.new()
--Sine
local Y
local A = -0.05 -- Amplitude
local B = 1 -- Frequency multiplier
local C = math.pi / 2 -- Horizontal shift
local D = 1 -- Vertical shift
local x = 0
local step = 0.01
--Circle Limiter
local r = 65
local R = r * r
local X = 90
local Y = 0
function InRadius(x, y)
if (x - X) * (x - X) + (y - Y) * (y - Y) <= R then
return true
else
return false
end
end
local Lerp = function(a, b, t)
return a + (b - a) * t
end
function Transparent()
Head.LocalTransparencyModifier = 1
for _, v in Player:GetChildren() do
if v:IsA("Accessory") then
v.Handle.LocalTransparencyModifier = 1
end
end
end
function module.updateCameraRotation(deltaTime)
x += step
userinputservice.MouseBehavior = Enum.MouseBehavior.LockCenter
Transparent()
Delta = userinputservice:GetMouseDelta()
Direction -= Delta * Sens
--Clamp to circle
if not InRadius(Direction.X, Direction.Y) then
local m = (Direction.X - X) * (Direction.X - X) + (Direction.Y - Y) * (Direction.Y - Y)
Direction = Vector2.new(X, Y) + Vector2.new(Direction.X - X, Direction.Y - Y) * math.sqrt(R / m)
end
Direction = Vector2.new(Direction.X, Direction.Y)
-- HeadBob Sine
Y = (A * math.pow(math.sin(B * (x - C)) + D, 2)) + Head.CFrame.Position.Y
Pos = CFrame.new(Head.CFrame.Position.X, Y, Head.CFrame.Position.Z)
TargetAngle = CFrame.fromOrientation(math.rad(Direction.Y), math.rad(Direction.X), 0)
Angle = Angle:Lerp(TargetAngle, Smoothness)
if script.First.Value == true then
script.First.Value = false
Angle = CFrame.Angles(90, 0, 0)
end
camera.CFrame = Pos * Angle * CFrame.new(0, (0.5 + Direction.Y / 90 * 0.1) + 0.3, -0.3)
end
return module
I have it set up where it can only run one at once. There is an object value and the value will be one of the module scripts, and it will require whichever module script is set as the value and run that on render stepped. But other than that I don’t believe I have any other scripts that could be conflicting, unless you were talking about something else?
I just wanted to make sure that you didn’t accidentally copy/paste a version of one of the other scripts into another object and have them running at the same time.
Possibly put a quick print(script.Name) or print("first person camera script") (or whatever description of the script works for you) in each camera script that identifies exactly which script is running at any one time. If you get 2 scripts printing at the same time then you will know that they may be interfering with the camera movement.
I used this method as well as checked to make sure no other conflicting scripts existed, and it seemed to me that only the one script was running. Is there any other way you know that could fix it?
I’m not great with the math of camera movement so honestly there may be some simple fix for your script.
My troubleshooting style is to try to remove any variables (scripts, physics, etc.) that might be affecting an issue and see which one seems to cause the problem.
Not sure if this is the main cause of the issue but for the camera to not be dependent on FPS, multiply direction with deltaTime Direction -= Delta * Sens * deltaTime * 60
I also multiplied by 60 so you won’t have to fiddle with the sensitivity as I assume you based it on that FPS
This should play the same for players on different FPSs and shouldn’t be affected by FPS drops
So i tried this, and it with the * 60 and it was way to fast, so I removed it. To be more specific, the rotation just feels strange and inconsistent to use, like most of the time it’s alright but sometimes it just feels faster or jerky. Even with the delta time multiplication it felt the same. So is there anything else I can do?
Hmm it seems to work fine on my end, could the issue be outside of the module itself?
Make sure you’re calling module.updateCameraRotation(deltaTime) from Runservice.RenderStepped as that is when rendering updates happen/the next frame is rendered
Well, I just looked at my script that was calling it, and I just found out it was running on runservice.stepped. I am indescribably mad at myself rn. Well, thank you for the help
nvm, its still doing it, Ill try it in a seperate place with just the script being run directly, instead of it being called on a module script. Edit: after trying it on its own local script in a separate place it still is doing the same thing
this is the entire local script I used, it is in starter player scripts if it helps:
task.wait(1)
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local camera = game.Workspace.CurrentCamera
local userinputservice = game:GetService("UserInputService")
local Player = game.Workspace.PlayerObject
local Head = Player.Head
--Deltas/values
local maxRotationAngle = math.rad(30)
local Sens = 2
local Smoothness = 0.1
local Pos
local Delta
--angles
local Direction = Vector2.new()
local Angle = CFrame.new(0, 0, 0)
local TargetAngle = Vector2.new()
--Sine
local Y
local A = -0.05 -- Amplitude
local B = 1 -- Frequency multiplier
local C = math.pi / 2 -- Horizontal shift
local D = 1 -- Vertical shift
local x = 0
local step = 0.01
--Circle Limiter
local r = 65
local R = r * r
local X = 90
local Y = 0
function InRadius(x, y)
if (x - X) * (x - X) + (y - Y) * (y - Y) <= R then
return true
else
return false
end
end
local Lerp = function(a, b, t)
return a + (b - a) * t
end
function Transparent()
Head.LocalTransparencyModifier = 1
for _, v in Player:GetChildren() do
if v:IsA("Accessory") then
v.Handle.LocalTransparencyModifier = 1
end
end
end
game:GetService("RunService").RenderStepped:Connect(function(deltaTime)
x += step
userinputservice.MouseBehavior = Enum.MouseBehavior.LockCenter
Transparent()
Delta = userinputservice:GetMouseDelta()
Direction -= Delta * Sens * deltaTime
--Clamp to circle
if not InRadius(Direction.X, Direction.Y) then
local m = (Direction.X - X) * (Direction.X - X) + (Direction.Y - Y) * (Direction.Y - Y)
Direction = Vector2.new(X, Y) + Vector2.new(Direction.X - X, Direction.Y - Y) * math.sqrt(R / m)
end
Direction = Vector2.new(Direction.X, Direction.Y)
-- HeadBob Sine
Y = (A * math.pow(math.sin(B * (x - C)) + D, 2)) + Head.CFrame.Position.Y
Pos = CFrame.new(Head.CFrame.Position.X, Y, Head.CFrame.Position.Z)
TargetAngle = CFrame.fromOrientation(math.rad(Direction.Y), math.rad(Direction.X), 0)
Angle = Angle:Lerp(TargetAngle, Smoothness)
if script.First.Value == true then
script.First.Value = false
Angle = CFrame.Angles(90, 0, 0)
end
camera.CFrame = Pos * Angle * CFrame.new(0, (0.5 + Direction.Y / 90 * 0.1) + 0.3, -0.3)
end)
I can’t seem to replicate the issue but I did update some stuff that hopefully fixes it
Firstly I noticed that :GetMouseDelta() already takes deltaTime into account (kinda) so I changed “Direction -= Delta * Sens * deltaTime” back to “Direction -= Delta * Sens” whoops
I also thought the problem might’ve come from Lerp being heavily dependent on FPS and to take that into account you’d have to do something like:
But since it works fine on my end either way I can’t really see if those fixed it or not and I’m all out of any other ideas, sorry
Heres the current code I tested:
task.wait(1)
local player = game:GetService("Players").LocalPlayer
local mouse = player:GetMouse()
local camera = workspace.CurrentCamera
local userinputservice = game:GetService("UserInputService")
local Player = workspace.PlayerObject
local Head = Player.Head
--Deltas/values
local maxRotationAngle = math.rad(30)
local Sens = 0.1
local Smoothness = 0.1
local Pos
local Delta
--angles
local Direction = Vector2.new()
local Angle = CFrame.new(0, 0, 0)
local TargetAngle = CFrame.new()
--Sine
local Y
local A = -0.05 -- Amplitude
local B = 1 -- Frequency multiplier
local C = math.pi / 2 -- Horizontal shift
local D = 1 -- Vertical shift
local x = 0
local step = 0.01
--Circle Limiter
local r = 65
local R = r * r
local X = 90
local Y = 0
function InRadius(x, y)
if (x - X) * (x - X) + (y - Y) * (y - Y) <= R then
return true
else
return false
end
end
function Transparent()
Head.LocalTransparencyModifier = 1
for _, v in Player:GetChildren() do
if v:IsA("Accessory") then
v.Handle.LocalTransparencyModifier = 1
end
end
end
game:GetService("RunService").RenderStepped:Connect(function(deltaTime)
x += step * deltaTime * 60
userinputservice.MouseBehavior = Enum.MouseBehavior.LockCenter
Transparent()
Delta = userinputservice:GetMouseDelta()
Direction -= Delta * Sens
--Clamp to circle
if not InRadius(Direction.X, Direction.Y) then
local m = (Direction.X - X) * (Direction.X - X) + (Direction.Y - Y) * (Direction.Y - Y)
Direction = Vector2.new(X, Y) + Vector2.new(Direction.X - X, Direction.Y - Y) * math.sqrt(R / m)
end
-- HeadBob Sine
Y = (A * math.pow(math.sin(B * (x - C)) + D, 2)) + Head.CFrame.Position.Y
Pos = CFrame.new(Head.CFrame.Position.X, Y, Head.CFrame.Position.Z)
TargetAngle = CFrame.fromOrientation(math.rad(Direction.Y), math.rad(Direction.X), 0)
local correctedSmoothness = 1 - math.pow(Smoothness, deltaTime)
Angle = Angle:Lerp(TargetAngle, correctedSmoothness)
if script.First.Value == true then
script.First.Value = false
Angle = CFrame.Angles(90, 0, 0)
end
camera.CFrame = Pos * Angle * CFrame.new(0, (0.5 + Direction.Y / 90 * 0.1) + 0.3, -0.3)
end)
Ok, well thank you so much for all the help. Sadly this didn’t entirely work on my end, I have no idea what could be causing this, but whatever. I’ll probably just use another camera system, again thank you.