Can a KinematicBody2D have a negative scale and still move_and_slide properly?

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

EDIT - Never Mind - I didn’t google enough the first time. Apparently this is a known behavior, but I’d hesitate to call it expected or correct.

I have a KinematicBody2D scene with an AnimatedSprite child. Previously, in order to make the character face the opposite direction, I changed the scale of the AnimatedSprite to -1 if the character was facing that direction. However, I recently added a another child node with a RayCast2D and some Particles2D that always face forward, so I decided to change the Scale of the whole KinematicBody2D instead of the AnimatedSprite so that the Sprite and the RayCast2D/ Particles2D would be facing the correct direction.

This system works the same so far when the character is facing right (positive x scale) but exhibits strange behavior when the character is facing left (negative x scale). The whole character (Sprite and Particles) appears to flip back and forth horizontally each frame. Upon further inspection, I have narrowed the issue to when I call move_and_slide_with_snap and the end of my _phyisics_process method.

Immediately before I call move_and_slide_with_snap on the first loop facing left, my body’s scale is (-1,1) as I expect. However, when I return from that method call, both signs have flipped, and the scale is (1,-1). At the start of the next loop, I change just the x component of the scale to -1 (because the character’s state is facing left) and immediately before the next move_and_slide_with_snap call, the scale is (-1, -1) (I have never touched the y component of the scale.) Immediately after the move_and_slide_with_snap call, the scale is (1,1) and the cycle continues.

Should move_and_slide_with_snap have a side effect on the body’s scale? Is setting the body’s x scale negative not an appropriate way to flip the whole node and children horizontally? Does anyone have any insight here?

:bust_in_silhouette: Reply From: RikKargones

Yeach, you can do this. With separate boolean variable. I use what solution:

var  changed_scale = false

func _physics_process(delta):
    if Input.is_action_pressed("player_left"):
        if (!changed_scale):
            scale.x=-1.0
            changed_scale=true
    elif Input.is_action_pressed("player_right"):
        if (changed_scale):
            scale.x=-1.0
            changed_scale=false

Why variable, not checking “scale”? Because “scale” isn’t properly represents real scale of object for some reason. And transform.get_scale().x does the same thing, I’m checked it recently.

Physics work properly, by the way.

Looks like in engine code, variable multiplie old value by new. I find something (116 line) in engine code, what maybe causes what, but I’m not sure.

UPD: I find a bit better solution. For better understanding read this tutorial from documentation.

var h_scale = true

func _physics_process(delta):
    if <condition to change scale to left> && !h_scale:
        <Node2D>.transform.x *= -1.0
        h_scale = true
    elif <condition to change scale to right> && h_scale:
        <Node2D>.transform.x *= -1.0
        h_scale = false
                
    if str(<Node2D>.transform.x.x) == "-0": <Node2D>.transform.x.x=0
    if str(<Node2D>.transform.x.y) == "-0": <Node2D>.transform.x.y=0
    if str(<Node2D>.transform.y.x) == "-0": <Node2D>.transform.y.x=0
    if str(<Node2D>.transform.y.y) == "-0": <Node2D>.transform.y.y=0

#<Node2D> - it's any node what you need to change scale

Why it may help you: here

RikKargones | 2021-05-07 00:45

Wow, thanks. This was driving me crazy.

Pdcl | 2021-08-28 06:35

That worked for me!!! Thank you very much!!

Andrew2022 | 2022-12-03 03:27

Thx so much!!!

Dendy | 2023-01-24 19:41