Well, in the case of a typical FPS camera there isn’t really such a thing as a mouse cursor, so pointing “towards” that doesn’t make a lot of sense.
Instead, the movements of the mouse are used to rotate the camera, but not “towards” anything in particular.
You can get the mouse movement like this:
local InputS = game:GetService("UserInputService")
InputS.MouseBehavior = Enum.MouseBehavior.LockCenter
do --This part is necessary because Roblox is just broken like that
local c
c = InputS:GetPropertyChangedSignal("MouseBehavior"):Connect(function()
InputS.MouseBehavior = Enum.MouseBehavior.LockCenter
c:Disconnect()
end)
end
InputS.InputChanged:Connect(function(inputObject)
if inputObject.UserInputType == Enum.UserInputType.MouseMovement then
print(inputObject.Delta) --How much the user moved their mouse since last frame
end
end)
You can turn that into a super basic 1st person camera controller like this:
local InputS = game:GetService("UserInputService")
local camera = game.Workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable
InputS.MouseBehavior = Enum.MouseBehavior.LockCenter
do
local c
c = InputS:GetPropertyChangedSignal("MouseBehavior"):Connect(function()
InputS.MouseBehavior = Enum.MouseBehavior.LockCenter
c:Disconnect()
end)
end
function rotateCamera(pitch, yaw)
camera.CFrame *= CFrame.Angles(pitch, 0, 0)
camera.CFrame *= CFrame.fromAxisAngle(camera.CFrame:VectorToObjectSpace(Vector3.new(0, 1, 0)), yaw)
end
InputS.InputChanged:Connect(function(inputObject)
if inputObject.UserInputType == Enum.UserInputType.MouseMovement then
local delta = inputObject.Delta
rotateCamera(-delta.Y * 0.01, -delta.X * 0.01)
end
end)
You can prevent the camera from pitching too much like this
local CAM_MAX_PITCH = math.rad(90)
local CAM_MIN_PITCH = math.rad(-90)
local CAM_PITCH_STEP_MAX = math.min(math.rad(180) - CAM_MAX_PITCH, math.abs(math.rad(-180) - CAM_MIN_PITCH))
function constrainCamera()
local lookVector = camera.CFrame.LookVector
local forwardVector = -camera.CFrame.RightVector:Cross(Vector3.new(0, 1, 0))
local pitch = math.acos(forwardVector:Dot(lookVector)) * math.sign(lookVector.Y)
if pitch > CAM_MAX_PITCH then
rotateCamera(CAM_MAX_PITCH - pitch, 0)
end
if pitch < CAM_MIN_PITCH then
rotateCamera(CAM_MIN_PITCH - pitch, 0)
end
end
InputS.InputChanged:Connect(function(inputObject)
if inputObject.UserInputType == Enum.UserInputType.MouseMovement then
local delta = inputObject.Delta
rotateCamera(-delta.Y * 0.01, -delta.X * 0.01)
constrainCamera()
end
end)
You can prevent the camera from flipping when the user pitches more than 90 degrees in a single frame like this:
The complete script
local InputS = game:GetService("UserInputService")
local CAM_MAX_PITCH = math.rad(90)
local CAM_MIN_PITCH = math.rad(-90)
local CAM_PITCH_STEP_MAX = math.min(math.rad(180) - CAM_MAX_PITCH, math.abs(math.rad(-180) - CAM_MIN_PITCH))
local camera = game.Workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable
camera.CFrame = CFrame.new(0, 10, 0)
InputS.MouseBehavior = Enum.MouseBehavior.LockCenter
do
local c
c = InputS:GetPropertyChangedSignal("MouseBehavior"):Connect(function()
InputS.MouseBehavior = Enum.MouseBehavior.LockCenter
c:Disconnect()
end)
end
function rotateCameraUnconstrained(pitch, yaw)
camera.CFrame *= CFrame.fromAxisAngle(camera.CFrame:VectorToObjectSpace(Vector3.new(0, 1, 0)), yaw)
camera.CFrame *= CFrame.Angles(pitch, 0, 0)
end
function rotateCameraConstrained(pitch, yaw)
camera.CFrame *= CFrame.fromAxisAngle(camera.CFrame:VectorToObjectSpace(Vector3.new(0, 1, 0)), yaw)
if math.abs(pitch) > CAM_PITCH_STEP_MAX then
--Pitch as much as allowed
local step = CAM_PITCH_STEP_MAX * math.sign(pitch) * 0.5
rotateCameraConstrained(step, 0)
--Recursively pitch however much is "remaining" (may be over limit again, so potentially gets called many times)
rotateCameraConstrained(pitch - step, 0)
else
camera.CFrame *= CFrame.Angles(pitch, 0, 0)
constrainCamera()
end
end
function constrainCamera()
local lookVector = camera.CFrame.LookVector
local forwardVector = -camera.CFrame.RightVector:Cross(Vector3.new(0, 1, 0))
local pitch = math.acos(forwardVector:Dot(lookVector)) * math.sign(lookVector.Y)
if pitch > CAM_MAX_PITCH then
rotateCameraUnconstrained(CAM_MAX_PITCH - pitch, 0)
end
if pitch < CAM_MIN_PITCH then
rotateCameraUnconstrained(CAM_MIN_PITCH - pitch, 0)
end
end
InputS.InputChanged:Connect(function(inputObject)
if inputObject.UserInputType == Enum.UserInputType.MouseMovement then
local delta = inputObject.Delta
rotateCameraConstrained(-delta.Y * 0.005, -delta.X * 0.005)
end
end)
EDIT:
You can make the camera follow the character, and the character rotate with the camera, like this:
local InputS = game:GetService("UserInputService")
local RunS = game:GetService("RunService")
local CAM_MAX_PITCH = math.rad(90)
local CAM_MIN_PITCH = math.rad(-90)
local CAM_PITCH_STEP_MAX = math.min(math.rad(180) - CAM_MAX_PITCH, math.abs(math.rad(-180) - CAM_MIN_PITCH))
local camera = game.Workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable
camera.CFrame = CFrame.new(0, 10, 0)
InputS.MouseBehavior = Enum.MouseBehavior.LockCenter
do
local c
c = InputS:GetPropertyChangedSignal("MouseBehavior"):Connect(function()
InputS.MouseBehavior = Enum.MouseBehavior.LockCenter
c:Disconnect()
end)
end
function rotateCameraUnconstrained(pitch, yaw)
camera.CFrame *= CFrame.fromAxisAngle(camera.CFrame:VectorToObjectSpace(Vector3.new(0, 1, 0)), yaw)
camera.CFrame *= CFrame.Angles(pitch, 0, 0)
end
function rotateCameraConstrained(pitch, yaw)
camera.CFrame *= CFrame.fromAxisAngle(camera.CFrame:VectorToObjectSpace(Vector3.new(0, 1, 0)), yaw)
if math.abs(pitch) > CAM_PITCH_STEP_MAX then
--Pitch as much as allowed
local step = CAM_PITCH_STEP_MAX * math.sign(pitch) * 0.5
rotateCameraConstrained(step, 0)
--Recursively pitch however much is "remaining" (may be over limit again, so potentially gets called many times)
rotateCameraConstrained(pitch - step, 0)
else
camera.CFrame *= CFrame.Angles(pitch, 0, 0)
constrainCamera()
end
end
function constrainCamera()
local lookVector = camera.CFrame.LookVector
local forwardVector = cameraForwardVector()
local pitch = math.acos(forwardVector:Dot(lookVector)) * math.sign(lookVector.Y)
if pitch > CAM_MAX_PITCH then
rotateCameraUnconstrained(CAM_MAX_PITCH - pitch, 0)
end
if pitch < CAM_MIN_PITCH then
rotateCameraUnconstrained(CAM_MIN_PITCH - pitch, 0)
end
end
function cameraForwardVector()
local lookVector = camera.CFrame.LookVector
local forwardVector = -camera.CFrame.RightVector:Cross(Vector3.new(0, 1, 0))
return forwardVector
end
InputS.InputChanged:Connect(function(inputObject)
if inputObject.UserInputType == Enum.UserInputType.MouseMovement then
local delta = inputObject.Delta
rotateCameraConstrained(-delta.Y * 0.005, -delta.X * 0.005)
end
end)
RunS.RenderStepped:Connect(function()
local character = game.Players.LocalPlayer.Character
local head = character and character:FindFirstChild("Head")
local rootPart = character and character.PrimaryPart
if head and rootPart then
-- Move camera to head
camera.CFrame += head.Position - camera.CFrame.Position
-- Rotate character to face same direction as camera
rootPart.CFrame = CFrame.new(rootPart.Position, rootPart.Position + cameraForwardVector())
end
end)
CameraScript.lua (2.7 KB)