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 Space
var 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_dist
var 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 clamping
head_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 cone
func 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