How do I keep my characters collision box on them when they flip?

Here is the code for my character
Screenshot 2024-05-23 105242
Screenshot 2024-05-23 105250

extends CharacterBody2D

const SPEED = 700.0
const JUMP_VELOCITY = -900.0
@onready var sprite_2d = $Sprite2D

var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")

func _physics_process(delta):
	if (velocity.x > 1 || velocity.x < -1):
		sprite_2d.animation = "running"
	else:
		sprite_2d.animation = "default"
	# Add the gravity.
	if not is_on_floor():
		velocity.y += gravity * delta
		sprite_2d.animation = "jumping"

	if Input.is_action_just_pressed("jump") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var direction = Input.get_axis("left", "right")
	if direction:
		velocity.x = direction * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, 100)

	move_and_slide()

	var isLeft = velocity.x < 0
	sprite_2d.flip_h = isLeft

Easy solution: Fix the sprite, make it so the character is always centered (add an extra transparent region on one side). That’s a rule number zero for flippable sprites.

Workaround for some designs: Add another collider and enable the correct one when changing direction. This is common when the character has different states (like crouching).

2 Likes

There are normaly 2 aproachs for this:

Fliping the sprite, having areas for both left and right, and enabling and disabling them if necesary based of char direction/velocity.

Or, which I prefer, doing everything for just one side (left, for instance) and scaling the entire object.

Something like:

var flipped := false

func flip():
	if direction < 0 and !flipped:
		flipped = true
		scale.x *= -1
	elif direction > 0 and flipped:
		flipped = false
		scale.x *= -1

This approach have some down sides. You may not want to flip under the character everithing always. But there are tweaks to avoid this kind of problems. I like this approach the most.

Like you said, if there are several states with different sprite sizing, having all colliders x2 is a bit ugly if you ask me.

1 Like

I have absolutely no clue how to do either (I’m new), I’m sorry but could you help me a bit?

Your code adapted to flip the character based on it velocity:

extends CharacterBody2D

const SPEED = 700.0
const JUMP_VELOCITY = -900.0

var flipped := false

@onready var sprite_2d = $Sprite2D

var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")

func _physics_process(delta):
	if (velocity.x > 1 || velocity.x < -1):
		sprite_2d.animation = "running"
	else:
		sprite_2d.animation = "default"
	# Add the gravity.
	if not is_on_floor():
		velocity.y += gravity * delta
		sprite_2d.animation = "jumping"

	if Input.is_action_just_pressed("jump") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var direction = Input.get_axis("left", "right")
	if direction:
		velocity.x = direction * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, 100)

    flip()
	move_and_slide()

func flip():
	if velocity.x < 0 and !flipped:
		flipped = true
		scale.x *= -1
	elif velocity.x > 0 and flipped:
		flipped = false
		scale.x *= -1

Okay, the hitbox is perfect and I love the fact that when they turn left it stays left now, but I have found something shameful, I have connected an AnimatedSprite2D to the character but have it called Sprite2D by accident, and when I turn, it’s teleporting my character like a mile away each time they turn left and right, is there something I should do that could change this? anything to do with offsetting? I’m very sorry for the confusion

Never do this. It will cause bugs on physics calculations. It works for a time, sure. But when starting complex things, it will bug a lot. Never scale collisions nodes.
The best solution is to center the sprite, so you will only flip the image.

I centered my sprite already, but when she ran to the left, she got out of the collision box.

Send a print of the node structure. Is the collision and sprite children of characterbody?

I have a few collision shapes that are not centered on my player. For those I do this whenever I flip the sprite:

collision_shape.position.x = collision_shape.position.x * -1
1 Like

i would not flip the whole character scene, just the visuals.
this can be done by putting all of the character’s visuals in a Node2D child named Visuals and flipping only that Visuals node.