Making an NPC look at your character

I find it depressing that I don’t know how to properly manipulate Motor6Ds at this point. I’m failing to make a darn NPC look at my character. Here’s my code and a screenshot of how it looks atm.

motor.C1 = CFrame.new(Vector3.new(0,-script.Parent.Size.Y/2,0),(script.Parent.Position - workspace.Kiansjet.Head.Position).Unit)

(The script is under the dummy’s head part (R15) and motor refers to the neck Motor6D)

12 Likes

Hey! Recently made a post of this on my twitter. Give me a bit so I can get on my pc and go over the code.

Manipulating the motors
When moving the motor6D, you should use Transform. The motor6Ds of characters are relative, meaning a blank CFrame would be pointing forwards in the same orientation as the humanoid root part.

As motors are relative, to make the motor point at something accurately we need to make the motor have a world rotation of (0, 0, 0). We can do this by:

-- Where char is the model of the NPC
local RotationOffset = (Char.PrimaryPart.CFrame-Char.PrimaryPart.CFrame.p):inverse()

We only need to calculate RotationOffset once. We can apply this to the character’s waist Motor6D, which is located in the UpperTorso - when you do that, you should end up with this:

-- Where char is the model of the NPC
local RotationOffset = (Char.PrimaryPart.CFrame-Char.PrimaryPart.CFrame.p):inverse()
-- Where waist is the motor located in the NPC's UpperTorso
Waist.Transform = RotationOffset
Desired

Pointing the waist motor
The transform of the motor accepts a CFrame, meaning you can make a CFrame and point it at the desired position:

-- Where pos is where we want to point, in this case our player's HumanoidRootPart's position
CFrame.new(Vector3.new(0, 0, 0), Pos)

To make this point properly and not at a weird angle, we need to apply the RotationOffset we made earlier.

Waist.Transform = RotationOffset * CFrame.new(Vector3.new(0, 0, 0), Pos)

The problem
This is functioning as intended. But there’s a problem.

The problem

Imgur: The magic of the Internet

The first character is at the origin of the world. Their rotation works perfectly, but other NPCs away from the origin of the world are messed up entirely.
I don’t know if this is the way you’re supposed to fix it, but I did it by doing this:

local RealPos = -(NPC.PrimaryPart.CFrame.p - Pos).unit * 5000

Applied to the rest of our code, it looks like this:

-- Where char is the model of the NPC
local RotationOffset = (Char.PrimaryPart.CFrame-Char.PrimaryPart.CFrame.p):inverse()

-- Where pos is where we want to point, in this case our player's HumanoidRootPart's position
local RealPos = -(NPC.PrimaryPart.CFrame.p - Pos).unit * 5000
-- Where waist is the motor located in the NPC's UpperTorso
Waist.Transform = RotationOffset * CFrame.new(Vector3.new(0, 0, 0), RealPos) -- Pos changed to RealPos
The result

Imgur: The magic of the Internet

Limiting the angles
This should work perfectly. But the NPC will point at us from any angle, and that’s quite freaky. The way I fixed it was by getting the euler angles of the CFrame, clamping it, and then making a new CFrame. This is probably not the best way of doing it, but it works.

function limitAngles(CF, X, Y)
	-- X: UP
	-- Y: SIDE-SIDE
	-- Z: PIVOT (DON'T USE)
	
	X = math.rad(X); Y = math.rad(Y)
	
	local x, y = CF:toEulerAnglesXYZ()
	
	return CFrame.new(CF.p) * CFrame.Angles(
		math.clamp(x, 0, X),
		math.clamp(y, -Y, Y),
		0
	)
end

This function takes the CFrame we’ve been constructing so far, and limits it in either direction on the axes we give in X and Y. X and Y are in degrees, for simplicity’s sake. The Z axes is defaulted to 0 degrees, which otherwise can cause problems.

I’d recommend also passing X as 0 because that can sometimes look a bit weird, but it’s your choice.

function limitAngles(CF, X, Y)
	-- X: UP
	-- Y: SIDE-SIDE
	-- Z: PIVOT (DON'T USE)
	
	X = math.rad(X); Y = math.rad(Y)
	
	local x, y = CF:toEulerAnglesXYZ()
	
	return CFrame.new(CF.p) * CFrame.Angles(
		math.clamp(x, 0, X),
		math.clamp(y, -Y, Y),
		0
	)
end

-- Where char is the model of the NPC
local RotationOffset = (Char.PrimaryPart.CFrame-Char.PrimaryPart.CFrame.p):inverse()

-- Where pos is where we want to point, in this case our player's HumanoidRootPart's position
local RealPos = -(NPC.PrimaryPart.CFrame.p - Pos).unit * 5000

local TargetCFrame = RotationOffset * CFrame.new(Vector3.new(0, 0, 0), RealPos)
Waist.Transform = limitAngles(TargetCFrame, 0, 15)
It works

Imgur: The magic of the Internet

Extra stuff
If you’re adding animations to the NPC, then call this update on RunService.Stepped, as this overrides the animations (RenderStepped doesn’t do this).

I really hoped this helped. This is my first guide, so please point out any errors / mistakes I’ve made.
Thank you! :smiley:

68 Likes

tutorial section pls

Mighty_Loopy’s amazingly detailed message was in response to a question asked by Kiansjet. It wouldn’t make sense to move it to the tutorial section has it isn’t a tutorial.

17 Likes