Head Tracking Cursor

Hello! I am currently working on a start menu screen for my new game, and I need help coding the humanoid NPC’s head to track/watch the player’s cursor.

Please note that I want the head to watch the 2D cursor position on your screen/camera. NOT the mouse.hit.p or the object in 3D space in which the mouse is pointing at.

I know it sounds complicated, but somehow I figured out a tolerable method of achieving this. As shown in the example below, I have managed to successfully write a code which will watch the player’s cursor correctly. However it will only work for anchored parts. In other words, I can’t use the head’s CFrame or the NPC will break.

LocalScript inside of StarterPlayerScripts:

local rs = game:GetService("RunService")
local player = game:GetService("Players").LocalPlayer
local mouse = player:GetMouse()
local camera = workspace.CurrentCamera
local object = workspace:WaitForChild("WatchPart") -- BasePart that will watch the cursor. Anchored.

rs.RenderStepped:Connect(function()
	local cursorPosition = camera.CFrame.Position + camera:ViewportPointToRay(mouse.X, mouse.Y).Direction * 10
	object.CFrame = CFrame.new(object.Position, cursorPosition)
end)

Let me know if you come up with any methods of watching the player’s 2D cursor position through the head of a humanoid NPC. I’ve been stuck on this one for quite some time, and I would really appreciate some help. I can also provide further information if needed.

Here is a picture of the NPC and its contents:

2 Likes

Here you go! I found a topic fairly easily concerning this goal.

1 Like

I used the technique and attempted to convert it to R6, but I’m bad at CFrames so I’m having a little trouble making it come out correctly.

Here is the code I’m using:

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

local rig = workspace.Rig1
rig.Torso.Neck.MaxVelocity = 1/3

local NeckOriginC0 = rig.Torso.Neck.C0

rs.RenderStepped:Connect(function()
	local head = rig:FindFirstChild("Head")
	local neck = rig:FindFirstChild("Torso"):FindFirstChild("Neck")
	local root = rig:FindFirstChild("HumanoidRootPart")
	if head and neck and root then
		local torso = rig:FindFirstChild("Torso")
		local TorsoLookVector = torso.CFrame.LookVector
		local HeadPosition = head.CFrame.p
		local Point = mouse.Hit.p
		local Distance = (head.CFrame.p - Point).Magnitude
		local Difference = head.CFrame.Y - Point.Y
		neck.C0 = neck.C0:Lerp(NeckOriginC0 * CFrame.Angles(-(math.atan(Difference / Distance) / 2), (((HeadPosition - Point).Unit):Cross(TorsoLookVector)).Y * 0.5, 0), 0.5 / 2)
	end
end)

It resulted in the NPC heads turning in unusual directions, as shown in the picture below:

Terribly sorry for this late reply. I actually solved this two days ago but then forgot to respond. Anyways, here’s the code; it works for both R15 and R6. You may need to adjust the Roll value or just set it to zero if you don’t like it.

Code:

local RunService = game:GetService("RunService")

local Player = game:GetService("Players").LocalPlayer
local PlayerMouse = Player:GetMouse()
local Camera = workspace.CurrentCamera
local Character = Player.Character or Player.CharacterAdded:Wait()

local Humanoid = Character:WaitForChild("Humanoid")
local IsR6 = (Humanoid.RigType == Enum.HumanoidRigType.R6)

local Head = Character:WaitForChild("Head")
local Torso = if IsR6 then Character:WaitForChild("Torso") else Character:WaitForChild("UpperTorso")

local Neck = if IsR6 then Torso:WaitForChild("Neck") else Head:WaitForChild("Neck")
local Waist = if IsR6 then nil else Torso:WaitForChild("Waist")

local NeckOriginC0 = Neck.C0
local WaistOriginC0 = if Waist then Waist.C0 else nil

Neck.MaxVelocity = 1/3

local AllowedStates = {Enum.HumanoidStateType.Running, Enum.HumanoidStateType.Climbing, Enum.HumanoidStateType.Swimming, Enum.HumanoidStateType.Freefall, Enum.HumanoidStateType.Seated}
local IsAllowedState = (table.find(AllowedStates, Humanoid:GetState()) ~= nil)

local find = table.find
local atan = math.atan
local atan2 = math.atan2

Humanoid.StateChanged:Connect(function(_, new)
	IsAllowedState = (find(AllowedStates, new) ~= nil)
end)

local updatesPerSecond = 10
RunService.RenderStepped:Connect(function(deltaTime: number)
	if IsAllowedState then
		local HeadPosition = Head.Position

		if Neck then
			local MousePos = PlayerMouse.Hit.Position

			local TranslationVector = (HeadPosition - MousePos).Unit
			
			local Pitch = atan(TranslationVector.Y)
			local Yaw = TranslationVector:Cross(Torso.CFrame.LookVector).Y
			local Roll = atan(Yaw)
			
			local NeckCFrame
			if IsR6 then
				NeckCFrame = CFrame.Angles(Pitch, Roll, Yaw)
			else
				NeckCFrame = CFrame.Angles(-Pitch * 0.5, Yaw, -Roll * 0.5)
				
				local waistCFrame = CFrame.Angles(-Pitch * 0.5, Yaw * 0.5, 0)
				Waist.C0 = Waist.C0:Lerp(WaistOriginC0 * waistCFrame, updatesPerSecond * deltaTime)
			end
			
			Neck.C0 = Neck.C0:Lerp(NeckOriginC0 * NeckCFrame, updatesPerSecond * deltaTime)
		end
	end	
end)
2 Likes

It worked! The code successfully allowed the R6 character to track my cursor. Thank you so much for taking the time to write this code. I really appreciate your help! :slight_smile:

Successful

Edit:
The usage of Mouse.Hit.Position for my menu screen resulted in the head tracking awkwardly, from a distance in depth away from the player’s camera. So after a little experimenting, I was actually able to implement my original line of code, and replace the Mouse.Hit.Position with camera.CFrame.Position - Vector3.new(0,1,0) + camera:ViewportPointToRay(mouse.X, mouse.Y).Direction * 14 and it worked PERFECTLY. I’m very happy with the results!

For resourceful purposes, here is the simplified version of the code I’m running:

local rs = game:GetService("RunService")
local ps = game:GetService("Players")
local player = ps.LocalPlayer
local mouse = player:GetMouse()
local camera = workspace.CurrentCamera
local rig = workspace:WaitForChild("Rig")
local humanoid = rig:FindFirstChildOfClass("Humanoid")
local head = rig:FindFirstChild("Head")
local torso = rig:FindFirstChild("Torso")
if humanoid and head and torso then
	local neck = torso:FindFirstChild("Neck")
	local NeckOriginC0 = neck.C0
	rs.RenderStepped:Connect(function()
		local position = camera.CFrame.Position - Vector3.new(0,1,0) + camera:ViewportPointToRay(mouse.X, mouse.Y).Direction*14
		local TranslationVector = (head.Position - position).Unit
		local Pitch = math.atan(TranslationVector.Y)
		local Yaw = TranslationVector:Cross(torso.CFrame.LookVector).Y
		local NeckCFrame = CFrame.Angles(Pitch, 0, Yaw)
		neck.C0 = neck.C0:Lerp(NeckOriginC0 * NeckCFrame, 0.15)
	end)
end

I was wondering if the Npc could also smoothly turn its HumRootPart while looking to the 2D mouse cursor. Is there a method to do this. (Sorry for the late reply)