Snappy Aim Look

I have a module that handles weapons in my game, there’s a function connected to RunService.RenderStepped() that rotates player’s HumanoidRootPart in order for it to look at the mouse, however it causes some issues since it uses Mouse.Hit.Position and because of that it “snaps” a lot. Honestly I have no clue how to fix that, maybe raycasts but won’t that lag the clients a lot?
Video:


Code:

local function faceAim()
	if not (hum.Sit or lp.DistanceFromCharacter(lp, workspace.CurrentCamera.CFrame.Position) < 1) then
		root.CFrame = CFrame.new(root.CFrame.Position, Vector3.new(mouse.Hit.Position.X, root.CFrame.Position.Y, mouse.Hit.Position.Z))
	end	
end

This problem exists when the cursor moves over objects because the rotation is tied to Mouse.Hit.Position, which can be affected by objects in the way.

If you insist on using Mouse.Hit.Position you can smooth out the rotation using :Lerp(). Whilst this does help dampen the jarring “snapping” effect, as you can see the results are still not entirely perfect.

Without Lerp

Medal_C8QDEal5a4

With Lerp

Medal_1QUAu8ljUV

If you want to completely eliminate any abnormal behaviour caused by objects, you can use ScreenPointToRay. This method ensures that the rotation is influenced only by the position of the cursor on the screen, rather than being affected by objects in the world.

Medal_oG6aQCLJbB

As you can see, these results are much smoother.

Here is my code from replicating your conditions in my own environment.

local Players = game:GetService("Players")
local player = Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local root = character:WaitForChild("HumanoidRootPart")
local hum = character:FindFirstChildOfClass("Humanoid")
local mouse = player:GetMouse()

local function faceAim()
	if not hum or hum.Sit then return end 
	
    -- Get the mouse's direction from the screen
	local camera = workspace.CurrentCamera
	local mouseRay = camera:ScreenPointToRay(mouse.X, mouse.Y) 
	
	local targetPosition = mouseRay.Origin + mouseRay.Direction * 100
	local targetCFrame = CFrame.new(root.Position, Vector3.new(targetPosition.X, root.Position.Y, targetPosition.Z))
	root.CFrame = root.CFrame:Lerp(targetCFrame, 0.15)
end

game:GetService("RunService").RenderStepped:Connect(faceAim)

Hope this helps :slight_smile:

1 Like

Very helpful. I think I’d like to use ScreenPointToRay but when I point my mouse behind the player in 3rd person they won’t turn around, is there a way to fix that or will I have to stick to :Lerp-ing mouse hit?

Also when walking in both right/left and forward/back direction ScreenPointToRay method starts snapping and failing to rotate. I assume :Lerp is more fitting for my use-case

EDIT: actually that happens to both methods :frowning:

After much trial and error I found a solution that truly worked, and tested it thoroughly myself to ensure this was the case.

While Lerp() did smooth out the rotation, it was causing minor misalignment during movement. So, I replaced it with CFrame.lookAt() which now directly aligns the character every frame for perfect accuracy.

Medal_iPgbec18XY
Medal_qdAp5KbZWS

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local root = character:WaitForChild("HumanoidRootPart")
local hum = character:WaitForChild("Humanoid")
local mouse = player:GetMouse()

-- Function to calculate the true world-space mouse position
local function getMouseWorldPosition()
	local camera = workspace.CurrentCamera
	local mouseRay = camera:ScreenPointToRay(mouse.X, mouse.Y) 

	-- Project onto a plane at the player's height
	local planeY = root.Position.Y
	local rayOrigin = mouseRay.Origin
	local rayDirection = mouseRay.Direction

	-- Ensure ray always hits the ground by slightly adjusting downward
	if rayDirection.Y > -0.02 then
		rayDirection = Vector3.new(rayDirection.X, -0.1, rayDirection.Z).unit
	end

	-- Solve for intersection with the plane
	local t = (planeY - rayOrigin.Y) / rayDirection.Y
	local hitPosition = rayOrigin + rayDirection * t

	hitPosition = Vector3.new(hitPosition.X, root.Position.Y, hitPosition.Z)

	local minDistance, maxDistance = 5, 100
	local distance = (hitPosition - root.Position).Magnitude
	if distance < minDistance then
		hitPosition = root.Position + (hitPosition - root.Position).unit * minDistance
	elseif distance > maxDistance then
		hitPosition = root.Position + (hitPosition - root.Position).unit * maxDistance
	end

	return hitPosition
end

local function faceAim()
	if not hum or hum.Sit then return end 

	local targetPosition = getMouseWorldPosition()

	root.CFrame = CFrame.lookAt(root.Position, targetPosition)
end

RunService.RenderStepped:Connect(faceAim)

Hope this helps :slight_smile:

2 Likes

Amazing, thank you so much for this!

1 Like

is there way to have this aim DIRECTLY at the mouse (like involving z axis?)

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.