I know this is pretty much a request, which is not allowed on this site, but when it comes to FE I just really hate it
This is a local script which is put inside the StarterGui (it's a first person script), you don't need to change anything about the math, just change the things that are related to UserInputService because that service doesn't work in normal scripts
wait()
local InputService = game:GetService("UserInputService")
local Camera = game.Workspace.CurrentCamera
local Player = game.Players.LocalPlayer
local Character = Player.Character
local Head = Character.Head
local Torso = Character.Torso
local RootPart = Character.HumanoidRootPart
local RootJoint = RootPart.RootJoint
local Neck = Torso.Neck
Camera.FieldOfView = 70
Camera.CameraType = "Scriptable"
InputService.MouseBehavior = Enum.MouseBehavior.LockCenter
repeat
wait(0.5)
until Character and Head and Torso and RootPart and RootJoint and Neck
local v3 = Vector3.new
local cf = CFrame.new
local components = cf().components
local inverse = cf().inverse
local fromAxisAngle = CFrame.fromAxisAngle
local atan, atan2 = math.atan, math.atan2
local acos = math.acos
local function toAxisAngleFromVector(v)
local z = v.z
return z * z < 0.99999 and v3(v.y, -v.x, 0).unit * acos(-z) or v3()
end
local function AxisAngleLookOrientation(c, v, t)--CFrame,Vector,Tween
local c = c - c.p
local rv = (inverse(c) * v).unit
local rz = rv.z
return rz * rz < 0.99999 and c * fromAxisAngle(v3(rv.y, -rv.x, 0), acos(-rz) * (t or 1)) or c
end
local function AxisAngleLookNew(v, t)--CFrame,Vector,Tween
local rv = v.unit
local rz = rv.z
return rz * rz < 0.99999 and fromAxisAngle(v3(rv.y, -rv.x, 0), acos(-rz) * (t or 1)) or cf()
end
local function AxisAngleLook(c, v, t)--CFrame,Vector,Tween
local rv = (inverse(c) * v).unit
local rz = rv.z
return rz * rz < 0.99999 and c * fromAxisAngle(v3(rv.y, -rv.x, 0), acos(-rz) * (t or 1)) or c
end
local Sensitivity = 0.005
local CameraDirection = Vector3.new(0, 0, 1)
local function EulerAnglesYX(l)
local x, z = l.x, l.z
return atan(l.y / (x * x + z * z) ^ 0.5), -atan2(x, -z)
end
local function AnglesXY(l)
local z = l.z
return atan2(l.y, -z), -atan2(l.x, -z)
end
local function MouseMoved(Input)
if Input.UserInputType == Enum.UserInputType.MouseMovement then
local dx, dy = Input.Delta.x * Sensitivity, Input.Delta.y * Sensitivity
local m2 = dx * dx + dy * dy
if m2 > 0 then
CameraDirection = (AxisAngleLookOrientation(RootPart.CFrame, CameraDirection) * fromAxisAngle(v3(-dy, -dx, 0), m2 ^ 0.5)).lookVector
end
local RootOrientation = RootPart.CFrame - RootPart.Position
local RelativeDirection = RootOrientation:inverse() * CameraDirection
local AngX, AngY = AnglesXY(RelativeDirection)--RootOrientation:inverse()*
if AngX < -1.57 * 11 / 12 then
local y, z, c, s = RelativeDirection.y, RelativeDirection.z, math.cos(-1.57 * 11 / 12 - AngX), -math.sin(-1.57 * 11 / 12 - AngX)
z, y = z * c - y * s, z * s + y * c
CameraDirection = RootOrientation * v3(RelativeDirection.x < 0 and -(1 - y * y - z * z) ^ 0.5 or (1 - y * y - z * z) ^ 0.5, y, z)
elseif AngX > 1.57 * 11 / 12 then
local y, z, c, s = RelativeDirection.y, RelativeDirection.z, math.cos(1.57 * 11 / 12 - AngX), -math.sin(1.57 * 11 / 12 - AngX)
z, y = z * c - y * s, z * s + y * c
CameraDirection = RootOrientation * v3(RelativeDirection.x < 0 and -(1 - y * y - z * z) ^ 0.5 or (1 - y * y - z * z) ^ 0.5, y, z)
end
end
end
local Mouse = Player:GetMouse()
local Zoom = -0.7
Mouse.KeyDown:connect(function(k)
if k == "e" then
Zoom = -0.7
elseif k == "q" then
Zoom = -0.7
end
end)
InputService.InputChanged:connect(MouseMoved)
Neck.C1 = cf()
local _
local DirectionBound = 3.14159 / 3
local CurrentAngY = 0
local function CameraUpdate()
Camera.CameraType = "Scriptable"
local cx, cz = CameraDirection.x, CameraDirection.z
local rvx, rvz = RootPart.Velocity.x, RootPart.Velocity.z
if rvx * rvx + rvz * rvz > 4 and cx * rvx + cz * rvz < -0.5 * (cx * cx + cz * cz) ^ 0.5 * (rvx * rvx + rvz * rvz) ^ 0.5 then
DirectionBound = math.min(DirectionBound * 0.9, math.abs(CurrentAngY * 0.9))
else
DirectionBound = DirectionBound * 0.1 + 3.14159 / 3 * 0.9
end
local AngX, AngY = EulerAnglesYX((RootPart.CFrame - RootPart.Position):inverse() * CameraDirection)
if AngY > DirectionBound then
RootPart.CFrame = RootPart.CFrame * CFrame.Angles(0, AngY - DirectionBound, 0)
elseif AngY < -DirectionBound then
RootPart.CFrame = RootPart.CFrame * CFrame.Angles(0, AngY + DirectionBound, 0)
end
_, CurrentAngY = EulerAnglesYX((RootPart.CFrame - RootPart.Position):inverse() * CameraDirection)
local CameraOrientation = AxisAngleLookNew((RootPart.CFrame - RootPart.Position):inverse() * CameraDirection, 1)
Neck.C0 = CFrame.new(0, 1, 0) * CameraOrientation * CFrame.new(0, 0.5, 0) -------------------------------- HEAD POSITION -------------------------------
local PreCam = AxisAngleLook(RootPart.CFrame * cf(0, 1, 0), RootPart.CFrame * v3(0, 1, 0) + CameraDirection) * CFrame.new(0, 0.825, 0)
if Zoom == 8 then
local Part, Position = workspace:findPartOnRay(Ray.new(PreCam.p, PreCam.lookVector * -8), Character)
Camera.CoordinateFrame = PreCam * CFrame.new(0, 0, (Position - PreCam.p).magnitude)
else
Camera.CoordinateFrame = PreCam * CFrame.new(0, 0, Zoom + 1) --------------------------- CAM POSITION ------------------------------------------
end
end
game:GetService("RunService").RenderStepped:connect(CameraUpdate)

4 comments
OOF this is really long...
Suggest searching on YouTube on RemoteEvents, Bindables etc
its not really that hard to convert, all you need to do is change any parts that are related to UserInputService
If you hate FE quit developing. FE will overrun roblox anytime soon.