How to make first camera point towards mouse

(Sorry for my english i’m using a translator)

Hello, i’m working on a first camera that allows you to aim with guns, the aim and the first camera worked pretty well but when i move the mouse the camera doesn’t move with it, and i’m trying to fix this since yesterday but so far i don’t achieved

What i’m talking about:

What i want to achieve:

I know that roblox default camera script does that but as you can see in the video if i use the default roblox camera script i can’t aim properly with my gun, how can i re create this camera system in my camera script?

3 Likes
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local camera = workspace.CurrentCamera
local ts = game:GetService("TweenService")
local rs = game:GetService("RunService")

rs.RenderStepped:Connect(function()
	local tween = ts:Create(camera, TweenInfo.new(0.1), {["CFrame"] = mouse.Hit * CFrame.new(0, 10, 0)})
	tween:Play()
	tween.Completed:Wait()
end)
1 Like

I removed my aim system that sets camera CFrame to another CFrame and also removed the part of the script that sets the camera CFrame to Head.CFrame and made what you said but it still didn’t worked

1 Like

I don’t know if you’re doing this already but you should set the camera mode to first person:
game.Players.LocalPlayer.CameraMode = Enum.CameraMode.LockFirstPerson

2 Likes

I usually lock the mouse to the center of the screen (it’s a property in InputService, IIRC). When it’s locked like that, the Delta property of the InputObject that’s passed to InputChanged will be how much the player tried to move their mouse this frame, although obviously the mouse doesn’t actually move because it’s locked.

If you don’t lock the mouse and use its actual movement to rotate the camera, what happens when the mouse reaches the edge of the screen?

1 Like

This just works with default roblox camera script, yes i tried this but my camera type is set to scriptable so my script replaces the default

1 Like

The mouse in the video that i sent in my first answer to this topic wasn’t locked, the camera makes a lot of spins

1 Like

Wait you mean locking or unlocking the mouse?

1 Like

If you are setting the camera to be scriptable and you set its CFrame everytime it updates, you should write a custom camera system using UserInputService.

However, if it is simply that your mouse is free and not turning your camera unless hold down right mouse button, you should probably alter UserInputService.MouseBehavior (Which is an Enum) to lock your mouse to the middle.

F.Y.I mainstream shooters on Roblox generically write their own camera movement script.

1 Like

Yes my camera is scriptable and i’m setting the CFrame with renderstepped.

That’s ecxactly what i want to achieve but i don’t know how to do this.

1 Like

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)

2 Likes

Thanks for answering, but i already fixed that (sorry i forgot to mark this post as solved), i can see that you made something like what i made:

local RunService = game:GetService("RunService")
local Uis = game:GetService("UserInputService")

local RemoteEvent = game.ReplicatedStorage.ChangeRootCFrame
local Event = game.ReplicatedStorage.ChangeCameraCFrame

local Player = script.Parent.Parent
local Character = script.Parent.Parent.CharacterAdded:Wait()
local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart")
local Torso = Character:WaitForChild("Torso")
local Head = Character:WaitForChild("Head")
local RightArm = Character:WaitForChild("Right Arm")

local Mouse = Player:GetMouse()

local Camera = game.Workspace.CurrentCamera
Camera.CameraType = Enum.CameraType.Scriptable

local default_CameraInstance = Head
local default_CameraRotationX = 0
local default_CameraRotationY = 0
local default_CameraZoom = 1

local cameraInstance = default_CameraInstance
local cameraPosition
local cameraRotationX = default_CameraRotationX
local cameraRotationY = default_CameraRotationY
local cameraZoom = default_CameraZoom

local cameraMouseRotateSpeed = 0.25

-- Camera functions --

RunService.RenderStepped:Connect( function()
	
	for _, v in pairs(Character:GetChildren()) do -- Makes arms visible in first camera
		
		if v:IsA("Part") and v.Name ~= "Right Arm" and v.Name ~= "Left Arm" then
			
			v.LocalTransparencyModifier = 1
			
		elseif v.Name == "Right Arm" or v.Name == "Left Arm" then
			
			v.LocalTransparencyModifier = 0
		end
	end
	
	if Uis.MouseBehavior ~= Enum.MouseBehavior.LockCenter then -- Locks mouse
		
		Uis.MouseBehavior = Enum.MouseBehavior.LockCenter
	end
	
	cameraPosition = cameraInstance.Position
	
	local cameraRotationCFrame = CFrame.Angles(0, cameraRotationX, 0) * CFrame.Angles(cameraRotationY, 0, 0)
	Camera.CFrame = cameraRotationCFrame + cameraPosition + cameraRotationCFrame * Vector3.new(0, 0, cameraZoom)
end)

Event.Event:Connect( function(NeedChange, AimPart)
	
	if NeedChange then
		
		cameraInstance = AimPart
		cameraZoom = 0
		
	else
		
		cameraInstance = default_CameraInstance
		cameraZoom = default_CameraZoom
	end
end)

local function UpdateCamera()
	
	local Delta = Uis:GetMouseDelta()
	
	cameraRotationX = cameraRotationX - Delta.X * math.rad(cameraMouseRotateSpeed)
	
	if (cameraRotationY - Delta.Y * math.rad(cameraMouseRotateSpeed)) < 1.3 and (cameraRotationY - Delta.Y * math.rad(cameraMouseRotateSpeed)) > -1.5 then
		
		cameraRotationY = cameraRotationY - Delta.Y * math.rad(cameraMouseRotateSpeed)
	end
end

RunService:BindToRenderStep("MouseMovement", Enum.RenderPriority.Input.Value, UpdateCamera)
2 Likes