Why is the NPC not looking at the player?

Hi there, I’m making a script that if a player comes close in a 30 studs radius. The head will look at the player, if the head gets at a certain angle, the body will rotate/move itself with the head. So it stays naturally and the head doesn’t spin 360 degrees. However, I can’t seem to get why this isn’t working. It just doesn’t work, and nothing shows up in the output window.

The script is located in the NPC, it has a humanoid and humanoidrootpart, it’s an R15 NPC and the script is a server sided script.

Thank you.

local PlayerService = game:GetService("Players")
local npc = script.Parent


local Range = 30 
local angleThreshold = 30 
local hasSeenPlayer = false 


local function getAngleBetweenVectors(vec1, vec2)
	return math.deg(math.acos(vec1:Dot(vec2) / (vec1.magnitude * vec2.magnitude)))
end

local function CheckLineOfSight(Start, End)
	local RaycastParams = RaycastParams.new()
	RaycastParams.FilterType = Enum.RaycastFilterType.Exclude
	RaycastParams.FilterDescendantsInstances = {npc, unpack(PlayerService:GetPlayers())}

	local RaycastResult = workspace:Raycast(Start, End - Start, RaycastParams)

	return RaycastResult == nil
end

local function updateVisibilityFlag()
	local canSeePlayer = false

	for _, b in pairs(PlayerService:GetPlayers()) do
		local Character = b.Character

		if Character then
			local NPCToCharacter = (Character.Head.Position - npc:WaitForChild("Head").Position).Unit
			local NPCLookVector = npc:WaitForChild("Head").CFrame.LookVector

			local DotProduct = NPCToCharacter:Dot(NPCLookVector)
			local Angle = math.deg(math.acos(DotProduct))

			local Distance = (npc.HumanoidRootPart.Position - Character.HumanoidRootPart.Position).Magnitude

			if Angle <= angleThreshold and Distance <= Range and CheckLineOfSight(npc.Head.Position, Character.Head.Position) then
				canSeePlayer = true
			end
		end
	end

	hasSeenPlayer = canSeePlayer
end

local function rotateHeadTowardsPlayer(player)
	if player and hasSeenPlayer then
		local playerCharacter = player.Character
		if playerCharacter then
			local playerHead = playerCharacter:FindFirstChild("Head")
			if playerHead then
				local direction = (playerHead.Position - npc.Head.Position).unit
				local currentLookVector = npc.Head.CFrame.LookVector
				local angle = getAngleBetweenVectors(currentLookVector, direction)

				if angle > angleThreshold then
					local lookAtCFrame = CFrame.lookAt(npc.Head.Position, playerHead.Position, Vector3.new(0, 1, 0))
					npc.Head.CFrame = lookAtCFrame
				end
			end
		end
	end
end

local function checkPlayerVisibility()
	while true do
		wait(1) 

		updateVisibilityFlag()
	end
end

local function checkPlayerProximity()
	while true do
		wait(0.5) 

		local players = game.Players:GetPlayers()
		for _, player in pairs(players) do
			local distance = (npc.Head.Position - player.Character.HumanoidRootPart.Position).magnitude
			if distance < 30 then 
				rotateHeadTowardsPlayer(player)
			end
		end
	end
end

spawn(checkPlayerVisibility)
spawn(checkPlayerProximity)

3 Likes

Use IKControls.

local studsToTrigger = 30

local ik = Instance.new("IKControl")
ik.Name = "HeadTurn"
ik.Type = "LookAt"
ik.EndEffector = script.Parent.Head
ik.ChainRoot = script.Parent.Head
ik.Parent = script.Parent.Humanoid

local function getNearestPlayer()
    local player
    local distance = 0
    for _, v in game.Players:GetPlayers() do
        local mag = (script.Parent.HumanoidRootPart.CFrame - player.Character.HumanoidRootPart.CFrame).Magnitude
        if (mag > distance) or mag > studsToTrigger then continue end
        distance = mag
        player = v
    end
    return player
end

game:GetService("RunService").Heartbeat:Connect(function()
    local player = getNearestPlayer()
    if not player then return end
    ik.Target = player.Character.Head
end)
2 Likes

With some adjustments, that seemed to work. However, I get this really terrifying thing when I’m standing behind him.

Is there a way to fix this? Now it looks creepy, haha.

2 Likes

You can use raycasting, to detect if the player is behind the character

1 Like

I guess rotate the npc 180 degrees?

1 Like

The npc is walking around. If the player catches the NPC’s attention. I want to have the NPC look at the player. If the NPC is for example look at a painting on the wall. And the player catches the NPC’s attention. I want the NPC to turn around and look at the player. It doesn’t have to be 180 degrees. It could be 110 degrees too.

1 Like

Try this:

local studsToTrigger = 30

local ik = Instance.new("IKControl")
ik.Name = "HeadTurn"
ik.Type = "LookAt"
ik.EndEffector = script.Parent.Head
ik.ChainRoot = script.Parent.UpperTorso
ik.Parent = script.Parent.Humanoid

local function getNearestPlayer()
    local player
    local distance = 0
    for _, v in game.Players:GetPlayers() do
        local mag = (script.Parent.HumanoidRootPart.CFrame - player.Character.HumanoidRootPart.CFrame).Magnitude
        if (mag > distance) or mag > studsToTrigger or (CFrame.new(player.Character.HumanoidRootPart.CFrame.Orientation) - CFrame.new(script.Parent.HumanoidRootPart.CFrame.Orientation)).Magnitude > 90 then continue end
        distance = mag
        player = v
    end
    return player
end

game:GetService("RunService").Heartbeat:Connect(function()
    local player = getNearestPlayer()
    if not player then return end
    ik.Target = player.Character.Head
end)
1 Like

That works with some adjustments, very nice! Thank you, you just made one of my game’s main aspects working. I guess I need to learn how to do IKControl because this looks great!

1 Like

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