Restricting rotation by clamping of relative angles error

Godot Version

v4.2.1.stable.official [b09f793f5]


I’m having problems keeping a rotation correctly clamped, relative to another node’s rotation.

I am setting up a player controller, consisting of two sprites, a body and a head. Depending on the direction the body faces, the head’s rotation should be limited between two limits (an anti-clockwise bound and a clockwise bound) centred on the body’s rotation. Within this range, the head tries to look towards the cursor’s location on the screen, restricted by the edges of the bound.

I’ve used clamping to limit the angles either side of the body’s direction, but it fails when the body is facing backwards (close to -PI or +PI radians). I’m unsure how to keep the head consistently bounded across this part of the body’s rotation.

extends Area2D

@onready var body : Sprite2D = $Body
@onready var head : Sprite2D = $Head
@onready var camera : Camera2D = $Camera
var max_speed := 200.0
var rotational_speed := 2.0

func _process(delta : float) → void:
var input_direction := Vector2(0,0)
var velocity := Vector2(0,0)
var cursor_angle := (get_global_mouse_position() - global_position).angle() # Auto-wraps from Pi to -Pi
var cw_bound := 0.0
var anti_cw_bound := 0.0
input_direction.x = Input.get_axis(“move_left”,“move_right”)
input_direction.y = Input.get_axis(“move_up”,“move_down”)

if input_direction.length() > 1.0:
	input_direction = input_direction.normalized()

body.rotation = wrapf(body.rotation,-PI,PI) # Keeps body rotation within radian limit

if Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT): # 'Aiming' ability, to look wherever
	head.rotation = cursor_angle
	anti_cw_bound = wrapf(body.rotation - PI/4,-PI,PI) # Vision restricted to a cone facing the direction of the body
	cw_bound = wrapf(body.rotation + PI/4,-PI,PI)
	print("Cursor angle : ", cursor_angle, " . Anti-CW bound : ", anti_cw_bound, " . CW bound : ", cw_bound, " . Body angle : ", body.rotation)
	head.rotation = clampf(cursor_angle,anti_cw_bound,cw_bound)
	# ERROR here. When body angle is close to 180 degrees (-PI or +PI radians), in -ve or +ve direction, the clamping fails
	#print(body.rotation, ": Body rotation.", head.rotation, ": Head rotation.", cursor_angle, ": Cursor angle.")

velocity = input_direction * delta * max_speed

if input_direction.length() > 0.0:
	body.rotation += wrapf(velocity.angle() - body.rotation,-PI,PI) * delta * rotational_speed

position += velocity

camera.global_position = (get_global_mouse_position() + global_position)/2