NPC Head Rotation Code
- donovanpassantino
- Mar 22
- 2 min read
Updated: Apr 21
func rotate_head_to_target(delta : float, rotate_speed : float):
{ if !m_rotate_head: return # Get temp var for target pos var target_pos : Vector3 # The forward direction of the NPC var npc_forward = global_basis.z.normalized() # The Transform of the bone in global space.
Need to do global Transform * Bone Spacevar bone_pose: Transform3D = m_skeleton.global_transform *
m_skeleton.get_bone_global_pose(m_head_bone) # The rotation of the global bone var bone_pose_rotation = bone_pose.basis.get_euler() # The look_at rotation of the NPC to target var head_rotation : Vector3 # If there is a target do math to rotate head towards them if m_look_at_target:
{ # Need to add UP vector because global pos is on ground target_pos = m_look_at_target.global_position + Vector3.UP
# The distance between the npc and the target //Opitmized version var dist = global_position.distance_squared_to(target_pos) # The direction from the npc to the target var npc_dir_to_target = (target_pos - global_position).normalized() # The dot product between the NPC and the direction to the target var dot = npc_dir_to_target.dot(npc_forward) # The threshold of the view_width var threshold : float = cos(deg_to_rad(m_view_width)) # (radius * radius) because dist is squared m_view_dist_squared = m_view_dist * m_view_dist # Fov check if within view_width and if the target is within view_distvar within_view : bool = dot > threshold and (dist <= m_view_dist_squared) if m_debug: DebugDraw3D.draw_sphere(global_position, m_view_dist, Color.DARK_RED) draw_debug_dot_cone(npc_forward, m_view_dist, within_view) if (within_view): # Get the look_at rotation from the NPC to target
I think this is because of -Z forward
head_rotation = bone_pose.looking_at(target_pos, Vector3.UP,
true).basis.get_euler() + -bone_pose_rotation
}
# head rotation will be 0 deg if there is no target
# Convert rotation to degrees for clampinghead_rotation =
Vector3(rad_to_deg(head_rotation.x),rad_to_deg(head_rotation.y),rad_to_deg(head_rotation.z)) # Clamp the angles based on specific values var head_rotation_degrees = head_rotation head_rotation_degrees.x = clamp(head_rotation_degrees.x,
-m_vertical_angle, m_vertical_angle) # Angle lerp to get the fastest rotation m_lerpValue.x = deg_to_rad(head_rotation_degrees.x) m_lerpValue.y = lerp_angle(m_lerpValue.y, deg_to_rad(head_rotation_degrees.y), (rotate_speed * delta)) # Set the new rotation and set it to the rotation of the head bone var new_rotation = Quaternion.from_euler(Vector3(m_lerpValue.x, m_lerpValue.y, deg_to_rad(head_rotation_degrees.z))) m_skeleton.set_bone_pose_rotation(m_head_bone, new_rotation)
}# Debug draw dot product conefunc draw_debug_dot_cone(dir : Vector3, radius : float, condition : bool):
{ var color : Color = Color.DARK_RED if condition: color = Color.DARK_GREEN else: color = Color.DARK_RED var origin : Vector3 = (global_position + Vector3.UP) var forward_dir : Vector3 = dir * radius DebugDraw3D.draw_arrow(origin, origin + forward_dir, color, 0.1 ) var left_dir : Vector3 = origin + forward_dir.rotated(Vector3.UP, deg_to_rad(m_view_width)) DebugDraw3D.draw_arrow(origin, left_dir, color, 0.1 ) var right_dir : Vector3 = origin + forward_dir.rotated(Vector3.UP, -deg_to_rad(m_view_width)) DebugDraw3D.draw_arrow(origin, right_dir, color, 0.1 )
}Note:
DebugDraw3D draw functions is a Free Godot Plugin

Comments