Extremely frustrating issue with rotations flipping from 180 to -180 degrees

4.1.1

So I am trying to rotate my player gun so that its rotation faces the direction of my mouse position in global coordinates. I have this all working already. However, despite converting the rotation from how it’s currently represented in global rotation property (180 degrees to -180 degrees as semicircles), I try to plug in the converted value using if gun_angle < 0: gun_angle += 360. Nevertheless, Godot continues to convert even this converted value back into the 180 to -180 degrees representation. This is ridiculous since I need the 360 version for my if statements. And there’s no function/property to help with this. I’ve searched Google high and low but to no avail, I couldn’t find a single answer to this problem.

extends CharacterBody2D

@onready var animation_player: AnimationPlayer = $AnimationPlayer
@onready var camera: Camera2D = $Camera2D

@onready var player_sprite: Sprite2D = $Player

@onready var bullet = preload("res://scenes/bullet.tscn")
@onready var gun: Sprite2D = $Gun
@onready var bullet_spawn_point: Node2D = gun.get_node("BulletSpawnPoint")

@onready var summons = $Summons
@onready var summons_group_one = summons.get_child(0)
@onready var summons_group_two = summons.get_child(1)
@onready var summons_group_three = summons.get_child(2)

@export var SPEED: int = 750
@export var HEALTH: int = 5

@onready var bullet_count: int = 1

@onready var sprint_speed = SPEED * 3
@onready var camera_zoom_speed = Vector2(0.2, 0.2)
@onready var max_zoom = Vector2(5, 5)
@onready var min_zoom = Vector2(0.2, 0.2)

@onready var last_direction: Vector2 = Vector2.ZERO

func _ready():
	animation_player.play("run_right")
	#print(get_viewport_rect().position)
	#print(get_viewport_rect().end)
	#print(get_viewport_rect().size)
	#print(bullet)
	#print(bullet.can_instantiate())
	pass
	
func _process(delta):
	pass
	
func _physics_process(delta):
	#print(gun.global_position)
	summons_group_one.rotate(deg_to_rad(1.25))
	summons_group_two.rotate(deg_to_rad(1.25))
	summons_group_three.rotate(deg_to_rad(1.25))
	#print(SPEED, " " + str(HEALTH))
	check_gun(delta, get_global_mouse_position())
	check_movement(delta)
	check_zoom(delta)
	
	if Input.is_action_just_pressed("fire"):
		fire_bullet(1, get_global_mouse_position())
	
func check_movement(delta):
	var direction: Vector2 = Input.get_vector("left", "right", "up", "down")
	if direction:
		if Input.is_action_pressed("sprint"): velocity = direction * sprint_speed * (delta * 20)
		else: velocity = direction * SPEED * (delta * 20)
		
		#player_sprite.scale.x = -1
		#print(direction.x)
		if direction.x > 0: player_sprite.scale.x = 1
		elif direction.x < 0: player_sprite.scale.x = -1
		animation_player.play("run_right")
	else:
		velocity = Vector2.ZERO
		#print("IDLE")
		animation_player.play("idle_right")
	
	move_and_slide()
	
func check_zoom(delta):
	if Input.is_action_just_pressed("zoom_in"):
		camera.zoom += camera_zoom_speed
	elif Input.is_action_just_pressed("zoom_out"):
		camera.zoom -= camera_zoom_speed
	camera.zoom = camera.zoom.clamp(min_zoom, max_zoom)

func check_gun(delta, global_mouse_position: Vector2):
	#gun.global_rotation_degrees = -fmod(rad_to_deg(gun.global_position.angle_to_point(global_mouse_position)), 360)
	#gun.look_at(get_global_mouse_position())
	var gun_angle = rad_to_deg(global_position.angle_to_point(global_mouse_position))
	if gun_angle < 0: gun_angle += 360
	print(gun_angle)
	#var df =
	gun.set_global_rotation_degrees(gun_angle)
	#if gun_angle < 0: gun_angle += gun_angle + (2 * PI)
	#gun.global_rotation = gun_angle
	#print(gun_angle)
	#print(rad_to_deg(gun_angle))
	
	#print(gun.global_rotation_degrees)
	#print(rad_to_deg(gun.global_position.angle_to_point(get_global_mouse_position())))
	if gun.rotation_degrees < 90 or gun.rotation_degrees > 270:
		gun.scale.y = 1
	else:
		gun.scale.y = -1
		
func fire_bullet(count: int, target: Vector2):
	var new_bullet = bullet.instantiate()
	get_tree().root.add_child(new_bullet)
	new_bullet.global_position = bullet_spawn_point.global_position
	new_bullet.direction = (target - gun.global_position).normalized()
	#print("FIRE")
	#print(new_bullet.direction)
	#print((target - gun.global_position).normalized())
	#print(new_bullet.direction)
	#print(new_bullet)
	#print("fire")
	```
var gun_angle = rad_to_deg(global_position.angle_to_point(global_mouse_position))

Does it matter you are not referencing gun.global_position?

gun.flip_v = absf(gun.rotation_degrees) > 90

I figured out the problem wasn’t with rotation, even though it’s an annoying representation not having 0-360 degrees. It was that my gun was on the wrong side of my character, starting at -90, since I changed the pivot point. So I flipped it to the other side so it started at 0 degrees, and that solved the problem.

The main reason why angles are represented this way isn’t necessarily a Godot thing, but has to do with how trigonometric functions dealing with angles conventionally work.

The good news is that, in any case, you can ensure that an angle is in the range 0-360 using fposmod(angle_degrees, 360).

The functions rotate_toward and angle_difference are also essential in dealing with rotation values, in a way that fposmod often is not needed thanks to them, so it may pay off to learn about those functions as well.

…Though it’s important to note that these functions deal with rotation units of radians rather than degrees. (You can use the functions deg_to_rad and rad_to_deg to convert, but if you’re getting into making games, it’ll be very valuable to get used to working with radians directly. Again, not a Godot thing, just broader conventions in arithmetic that do actually exist for pretty good reasons.)

1 Like