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!