horizontal flip by using scale.x = -1 not work kinematicbody2d

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Timofey

I use this function to move the player, but the flip doesn’t work. when I try scale.x = -10 it seems to multiply by -1, thus constantly flipping in the opposite direction than a moment ago.

func _move() -> void:
    var horizontal := Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
    var vertical := Input.get_action_strength("move_down") - Input.get_action_strength("move_up")

    var vector := Vector2(horizontal, vertical)

    if vector == Vector2.ZERO:
	    if current_state == 'run':
		    _set_animation('idle')
else:
	if current_state == 'idle':
		if horizontal == 1:
			scale.x = 10
		elif horizontal == -1:
			scale.x = -10
		_set_animation('run')
	    move_and_slide(vector.normalized() * move_speed)

I solve the problem. most likely this bug is related only to this class, with other classes it works fine. You should either change the node type or replace scale.x with transform.x.x.

Timofey | 2021-10-02 17:20

Thanks a lot! I have the same Issue in Godot 3.4.3 and transfrom.x.x works, but I am interested in why does transform.x.x do the same?

Covenauta | 2022-03-15 00:54

:bust_in_silhouette: Reply From: Grizly

Why are you using operator “else” ? It not appropriate.

Try it:

if vector == Vector2.ZERO:
    if current_state == 'run':
        _set_animation('idle')

if current_state == 'idle':
    if vector.horizontal >= 1:
        scale.x = 10
    elif vector.horizontal =< -1:
        scale.x = -10
    _set_animation('run')
    move_and_slide(vector.normalized() * move_speed)
:bust_in_silhouette: Reply From: LordViperion

Inverting:
scale.x*=-1

:bust_in_silhouette: Reply From: Hyper Sonic

See my answer on https://forum.godotengine.org/92282/why-my-character-scale-keep-changing?show=146969#a146969

To sum up, when you go left, scale.x = -1 is converted into scale.y = -1 and rotation_degrees = 180, but scale.x remains 1 internally.

So if you keep going left, scale.x = -1 and it will keep flipping each frame, causing flickering.
While going right will never flip.

Thanks to Timofey, I added your workaround to my answer on the other question. The reason why transform.x.x works is that it modifies the base axes directly, so there is no matrix trick with negative scale.x and the rotation.

I figured out that

scale.x = -1

does the same thing as

scale.x *= -1

for some reason. As such, every time you try to change the scale to a negative number, the scale will flip regardless of whether it is already flipped. The solution is to only flip the scale once instead of every frame. You can probably achieve this multiple ways, but the way I figured out is by using a variable with a setter.

var flip: bool = false:
	set(value):
		if (flip != value):
			scale.x *= -1
			flip = value

func _move() -> void:

[...]

else:
	if current_state == 'idle':
		if horizontal == 1:
			flip = false
		elif horizontal == -1:
			flip = true
		_set_animation('run')
	    move_and_slide(vector.normalized() * move_speed)

This will cause the scale to flip horizontally only when the flip variable is changed and only once. You can set the initial scale to 10 in the editor or in code in the _ready function.