My 2D topdown character doesn't flip sometimes, please, fix it

Godot Version

4.6.1.stable

Question

My 2D topdown character doesn’t flip sometimes, please, fix it, I wish the flip of the character would work every time by pressing left or right button.

extends CharacterBody2D

@export var speed: float = 500.0
@export var body_part: Array[Node]
@export var bullet_prefab: PackedScene
@export var shoot_delay: float = 0.05  

@export var gun_rotation_offset: float = 0.0

@export var mouse_sensitivity: float = 0.03
@export var invert_mouse: bool = false
@export var rotation_smooth: float = 0.2

@onready var anim: AnimationPlayer = %anim
@onready var gun: Node2D = %Pistol
@onready var bullet_start_pos: Node2D = %bullet_start_pos

var _anim_i: Array[int] = [0, 0]
var _device: int = 0

var save_system: Node
var SFX: Node
var game_ui: Node
var fire_timer: float = 0.0

var mouse_delta: Vector2 = Vector2.ZERO
var is_facing_right: bool = false

func _init() -> void:
	add_to_group("player")
	add_to_group("player" + str(_device))

func _ready() -> void:
	save_system = Global.find_node_in_group("save_system")
	SFX = Global.find_node_in_group("SFX")
	game_ui = Global.find_node_in_group("game_ui")
	load_cutstom()

func load_cutstom() -> void:
	if save_system:
		save_system.load_gslot()
		_anim_i[0] = save_system.gslot["idle"]
		_anim_i[1] = save_system.gslot["walk"]
		load_all_body()

func load_all_body() -> void:
	for i: int in body_part.size():
		var saved_body_part: int = save_system.gslot["body_part"][i]
		replace_body_part(body_part[i], saved_body_part)
		load_color(i)

func replace_body_part(_n: Sprite2D, saved: int) -> void:
	saved = clamp(saved, 0, 2)
	var cat_id: int = saved + 1
	var part_name: String = _n.name.to_lower()
	var path: String = "res://Image/Game/Player/Cat_%d/Cat_%d_%s.png" % [cat_id, cat_id, part_name]
	var tex: Texture = load(path)
	if tex:
		_n.texture = tex

func load_color(_i: int) -> void:
	var v: String = save_system.gslot["body_color"][_i]
	var _color: Color = Color(v)
	body_part[_i].self_modulate = _color

func _input(event: InputEvent) -> void:
	if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
		mouse_delta += event.relative

func _physics_process(delta: float) -> void:
	var input_dir: Vector2 = Vector2.ZERO
	input_dir.x = Input.get_action_strength("right" + str(_device)) - Input.get_action_strength("left" + str(_device))
	input_dir.y = Input.get_action_strength("down" + str(_device)) - Input.get_action_strength("up" + str(_device))
	input_dir = input_dir.normalized()

	velocity = input_dir * speed
	move_and_slide()

	handle_animation(input_dir)
	handle_flip(input_dir)
	handle_aim(delta)
	handle_shoot(delta)

func handle_animation(input_dir: Vector2) -> void:
	if input_dir.length() > 0.0:
		var anim_name: String = "walk" + str(_anim_i[1])
		if anim.current_animation != anim_name:
			anim.play(anim_name)
	else:
		var anim_name: String = "idle" + str(_anim_i[0])
		if anim.current_animation != anim_name:
			anim.play(anim_name)

func handle_flip(input_dir: Vector2) -> void:
	if input_dir.x > 0.0:
		if not is_facing_right:
			scale.x = -1
			is_facing_right = true
	else:
		if is_facing_right:
			scale.x = 1
			is_facing_right = false

func handle_aim(_delta: float) -> void:
	var joy_x: float = Input.get_joy_axis(_device, JOY_AXIS_RIGHT_X)
	var joy_y: float = Input.get_joy_axis(_device, JOY_AXIS_RIGHT_Y)
	var stick: Vector2 = Vector2(joy_x, joy_y)

	if stick.length() > 0.2:
		var aim_dir: Vector2 = stick.normalized()
		var target_angle: float = aim_dir.angle()
		gun.rotation = lerp_angle(gun.rotation, target_angle, rotation_smooth)

	else:
		if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
			var invert: float = (1.0 if invert_mouse else -1.0)
			gun.rotation += mouse_delta.x * mouse_sensitivity * invert
			mouse_delta = Vector2.ZERO
		else:
			var aim_pos: Vector2 = get_global_mouse_position()
			gun.rotation = (aim_pos - gun.global_position).angle()


func handle_shoot(delta: float) -> void:
	fire_timer -= delta

	if fire_timer <= 0.0:
		shoot()
		fire_timer = shoot_delay

func shoot() -> void:
	if not bullet_prefab:
		return

	var bullet: Node = bullet_prefab.instantiate()
	get_parent().add_child(bullet)

	bullet.global_position = bullet_start_pos.global_position
	bullet.game_ui = game_ui

	var dir: Vector2 = Vector2.LEFT.rotated(gun.rotation)
	if is_facing_right:
		dir.x *= -1

	bullet.direction = dir.normalized()
	bullet.rotation = dir.angle()
	bullet.shooter = self

func damage() -> void:
	if game_ui:
		game_ui.damage()

You’re not dealing with no input and input_dir being zero.

Change that line to this:

if input_dir.x >= 0.0:

Should solve your problem.

2 Likes

I finally fixed, it didn’t work out cuz of AnimationPlayer & CharacterBody, cuz I was moving entire character body, now I do like that:

var last_flip: int = 1

func handle_flip() -> void:
	if Input.is_action_pressed("right" + str(_device)):
		last_flip = 1
	elif Input.is_action_pressed("left" + str(_device)):
		last_flip = -1
	all_sprites.scale.x = -last_flip

@onready var all_sprites: Node2D = %all_sprites
1 Like