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
With Lerp
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.
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)
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
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.
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)