Flipping Node/Sprite - scale.x = -1 flipping every frame?

Godot Version

4.2.1

Question

I’m making a 2D side scrolling RPG, and the Player character node has a Weapon node and animation player as children.

I’m trying to use scale.x = 1 or scale.x = -1 to flip the character and all child nodes based on direction, yet it seems to be flipping every frame when setting scale.x = -1. I’m trying to do this so I don’t have to recreate the animations the other side in the animation player.

My function below for getting the input and direction is called from within the _physics_process function.
The below is my function for getting the input and setting the direction:

func get_input_and_direction() → void:
# Get the input direction: -1, 0, 1
var direction: float = Input.get_axis(“move_left”, “move_right”)

if direction:
	velocity.x = direction * SPEED
else:
	velocity.x = move_toward(velocity.x, 0, 7.0)
	
print(scale.x)     **# Prints 1 every loop**

# Flip the sprite based on input
if direction > 0:
	scale.x = 1
elif direction < 0:
	if scale.x != -1:
		print("Before set " + str(scale.x))    **# Prints 1**
		scale.x = -1
		print("After set " + str(scale.x))   **# Prints -1**

I’d greatly appreciate if someone shine some light on to why this is happening, I can’t figure it out.

I’m also open if you’ve got suggestions on how I can write my code better or handle flipping the Player node.

Hi, I ran your code and the scale.x doesn’t revert back to 1 every frame for me, are you setting the scale somewhere else in the code?

3 Likes

Thank you for your reply.

I’m definitely not setting scale.x outside of the function I posted above.
I’m at a loss as to why it’s resetting.

I don’t want to post my entire player character script here as it’s a bit messy.
What’s the best way to show you my code?

you could post it on github? it’s fine if you post it here too, i won’t judge your code for being messy

This is not a bug, see the documentation

But they aren’t doing any decomposing? IDK what decomposing is anyway

It does when assigning because of how transforms are done, it’s complicated mathematics but it’s put simply that there’s no way to distinguish negative x scale from other things because of how 2D transforms are computed, it has to be combined with the transform or parents etc.

1 Like

How come I can’t replicate it?

The specific behaviour might not be directly replicable, but the fact remains that negative x scale isn’t supported and will behave unreliably

Probably the exact behaviour depends on setup

Okay then I guess OP should just use sprite flip_h property then

1 Like

@athousandships what if you need to flip the player by it’s root node, where you cant flip by flip_h because it doesn’t flip collisions. Scale.x -1 is used for flipping an object, collisions and all. and if that doesnt work, how to flip by root node?

You need to flip the individual parts, using what suits the situation, moving or rotating, you can have a shape that doesn’t need to be flipped for example, or switch shape being active when facing different directions, it all depends on your situation

1 Like

Sorry to necro this Topic, but I saw multiple of these without (most of them) mentioning how to solve this with Transforms, so I made a guide on this very thing!

Tl;dr, you can achieve stable flipping in the x direction with something like the following on the root node:

if velocity.x > 0:
    transform.x = Vector2(1.0, 0.0)
elif velocity.x < 0:
    transform.x = Vector2(-1.0, 0.0)

This way, all children should also flip correctly :clockwise_vertical_arrows:

2 Likes

Hey, the reason your scale.x keeps resetting is likely because setting just one component of the scale vector (scale.x = -1) sometimes doesn’t persist properly in Godot. Instead, you should assign the whole scale vector at once, like this:

scale = Vector2(-1, 1)

Also, check if your AnimationPlayer is animating the scale property — if it is, it will override your code every frame and reset scale.x back to 1. Removing or disabling those scale keyframes should fix the issue.

Alternatively, if you are using a Sprite2D or AnimatedSprite2D node for the character’s visuals, flipping it using the flip_h property is usually cleaner:

$Sprite2D.flip_h = direction < 0
This way, you avoid messing with the whole node’s scale and potential side effects.

Finally, for a cleaner code, you can do:

if direction != 0:
    scale = Vector2(sign(direction), 1)

Hope this helps!

1 Like

I just tested what you said @esp8266 , and it seems like (at least in my case) that the node still gets “confused” when doing:

scale = Vector2(-1, 1)

In my case it would flip the image upside down sporadically.

I think using the “helper properties” scale, rotation, and skew to indirectly set the transform isn’t ideal in a few edge cases (such as negative scale.x). But setting the transform directly seems to always behave in a stable manner.

Hey all - I just wanted to share my solution in case it helps someone…

My solution was to put all of the visual (sprite) nodes into their own Node2D and update the scale.x of the new visuals-only parent node when the character flips from left to right.

Here’s what I did:

First I created a Node2D called “Visuals” as a child of my root node (fyi it’s a CharacterBody2D)

Next, I moved all of the Sprite2D nodes into the new “Visuals” node

CharacterBody2D
  |--- BodyCollider
  |--- PlayerAnimation
  |--- ...
  |--- Visuals (Node2D)                        <---- new node
         |--- BodyBaseSprite
         |--- BodyArmsSprite
         |--- ...

Finally, I updated my player controller script to set the scale appropriately based on the character body’s velocity as follows:

# ...
@onready var _visuals: Node2D = $Visuals

func _physics_process(_delta: float) -> void:
    # ...

    if velocity.x > 0:
        _visuals.scale.x = 1
    elif velocity.x < 0:
        _visuals.scale.x = -1

    # ... other code ...

Doing this avoids messing with any physics calculations that a physics body node may be doing when velocity changes, and as a bonus the scene structure is a bit more organized.