I have an npc that looks at target, but for some reason it does not look Up or Down. Only the sides.
I dont understand what am i doing wrong, please help. this is the script inside the npc.
self = script.Parent
local Neck = self.Head:FindFirstChild("Neck", true)
local YOffset = Neck.C0
while task.wait() do
local cameraDirection = (CFrame.new(self.HumanoidRootPart.Position, workspace.target.Position) * self.HumanoidRootPart.CFrame:Inverse()).lookVector
local horizontalAngle = math.atan2(-cameraDirection.x, -cameraDirection.z)
local verticalAngle = -math.asin(cameraDirection.Y)
local goal = YOffset * CFrame.Angles(verticalAngle, 0, 0) * CFrame.Angles(0, horizontalAngle, 0)
Neck.C0 = Neck.C0:Lerp(goal, 0.1)
end
1 Like
You’re using two CFrame.Angles()
and combining them in a way that might not be correctly updating the vertical angle for the neck. When applying the angles to the neck’s C0, you’re using both vertical and horizontal rotations, but the order of transformations may be incorrect.
You have to simplify the calculations by directly using the LookVector to calculate the angles needed for both horizontal and vertical rotation.
I wrote this, test it out!
self = script.Parent
local Neck = self.Head:FindFirstChild("Neck", true)
local YOffset = Neck.C0
while task.wait() do
-- Get the direction vector towards the target
local targetDirection = (workspace.target.Position - self.HumanoidRootPart.Position).unit
-- Calculate the horizontal angle (yaw) using only the X and Z components
local horizontalAngle = math.atan2(-targetDirection.X, -targetDirection.Z)
-- Calculate the vertical angle (pitch) using the Y component
local verticalAngle = math.asin(targetDirection.Y)
-- Create the new goal CFrame for the neck, combining both rotations
local goal = YOffset * CFrame.Angles(verticalAngle, horizontalAngle, 0)
-- Smoothly transition the neck's C0 towards the new goal
Neck.C0 = Neck.C0:Lerp(goal, 0.1)
end
so basically what I did is:
-
Direction Vector: Instead of creating a
lookVector
and manually applying inverse CFrames, we directly calculate the direction from the NPC’s position to the target’s position using workspace.target.Position - self.HumanoidRootPart.Position
. The .unit
makes sure we get a normalized direction vector.
-
Angles: We now directly calculate the horizontal (yaw) and vertical (pitch) angles using the
atan2
function for horizontal rotation and asin
for vertical rotation based on the direction vector.
-
CFrame.Angles: We combine both angles directly into the
CFrame.Angles()
function, applying the vertical and horizontal rotations in the correct order.
this will mostly do work because
-
Horizontal rotation is handled by the yaw angle, which controls the NPC’s left-right movement.
-
Vertical rotation is handled by the pitch angle, which allows the NPC to look up and down at the target.
- The
Lerp
function ensures smooth interpolation between the current and target neck positions.
1 Like
The code you provided makes the head rotate sidewards(Roll axis) instead of Pitch when the head looks 90 degrees left or right. when the target is getting higher or lower

1 Like
we should avoid combining pitch and yaw directly in CFrame.Angles()
because that can misalign the neck’s orientation when yaw approaches ±90°.
try this once again:
self = script.Parent
local Neck = self.Head:FindFirstChild("Neck", true)
local YOffset = Neck.C0
while task.wait() do
local targetPos = workspace.target.Position
local headPos = self.Head.Position
-- Use CFrame.lookAt to create a rotation towards the target
local lookCFrame = CFrame.lookAt(headPos, targetPos)
-- Convert world space target CFrame into relative space of the HumanoidRootPart
local relativeCFrame = self.HumanoidRootPart.CFrame:ToObjectSpace(lookCFrame)
-- Apply the new relative rotation to the neck offset
local goal = YOffset * CFrame.new(Vector3.new(0, 0, 0)) * relativeCFrame.Rotation
-- Lerp smoothly for natural movement
Neck.C0 = Neck.C0:Lerp(goal, 0.1)
end
1 Like
There is no way this is not AI generated
Does not work with animations. the animations affect the head rotation
try this:
local self = script.Parent
local neck = self:WaitForChild("Head"):FindFirstChild("Neck", true)
local humanoidRoot = self:WaitForChild("HumanoidRootPart")
local target = workspace:WaitForChild("target")
local defaultC0 = neck.C0
while task.wait() do
local toTarget = (target.Position - neck.Part0.Position)
local lookCFrame = CFrame.lookAt(Vector3.zero, neck.Part0.CFrame:VectorToObjectSpace(toTarget))
-- Break apart into pitch (X) and yaw (Y), no roll
local _, yaw, _ = lookCFrame:ToEulerAnglesYXZ()
local pitch, _, _ = lookCFrame:ToEulerAnglesXYZ()
local goalC0 = defaultC0 * CFrame.Angles(pitch, yaw, 0)
neck.C0 = neck.C0:Lerp(goalC0, 0.1)
end
no bro i’m doing it, please dont judge my code

Does not work, the thing is i have an animation. This is my animation:

And i want the NPC head to look at the red part while it has animation but i get this result:

The head should face the red line and not the black line, I want the head to be able to aim the part regardless of what animation does to the head
Can you help on that?. i cant seem to figure it out