Godot: Issue with flipping 2D characters (non uniform scaling)

Created on 23 Oct 2017  Â·  31Comments  Â·  Source: godotengine/godot

Hello, so I wanted to try out 3.0 and began porting a game prototype I had in godot 2.1.4 and got a ridiculous "bug?"
somethingwrong2

This was working fine in godot 2.1.4. Flipping the main node of the character (AKA KinematicBody2D).

but why like that? so all the stuff that are child of the character will flip with the character too, like weapons scenes with its own hitbox and script, particle effects, more sprites, etc. In Godot 2.1.4 actually worked really well without any physics problem as long as you don't use weird numbers.

the way I did it was changing the node scale of the main node (KinematicBody2D) from (1, 1) facing right to (-1, 1) facing left.

Feature Request: this is not crazy needed but maybe a Flip H and Flip V checkbox in the physicsbodies like in the Sprite node. that way you don't have to flip every single child on the character and you don't mess with the scale numbers. at be able of doing it of code or fixing the bug I'm having

I know you guys will say you're not supposed to change the scale of a physicsbody but to me it makes
more sense to flip the main node of the character instead of the sprite node or any other node.

having all the weapons and stuff as a child of the sprite node doesn't feel right and at the same time even if thats the way of doing it, you have to tell new people that that's the way of doing it.

Issue description:
the characters goes crazy and flips H and V very fast when flipping the character (that didn't happen in 2.1.4)

Steps to reproduce:
when moving to the left flip the main node with set_scale((-1,1) in script then ingame move to the left and it happens

Link to minimal example project:
platformTEST.zip
godot 3.0 build 2017-10-22

discussion physics

Most helpful comment

This could be done for 3.1, I am considering rewritting all the collision code anyway to speed it up, and supporting flipped axies may be doable

All 31 comments

A non-uniform scale like (-1,1) isn't supposed to work with godot's physics code (or bullet).
If it did work at some point, it's was due to an accident or bug.

You could workaround this by flipping only the "visual" nodes of your character.

Yes except characters aren't always symmetrical. You might get away with it for a person, but not a centaur. Or a person holding a gun as in @aaronmzn's example

One way of doing this currently is saving four separate transforms for each child (normal, hflipped, vflipped, hflipped+vflipped) and picking between them in code on the fly. It's a bit messy and would fall over for non-symmetric children like a custom drawn ConvexPolygonShape2D though. Perhaps somebody knows a better way? An out of the box solution or a plugin would be great.

I did change your set_facing code to

var old_facing
func set_facing( dir ):
    facing = dir
    if old_facing != facing:
        set_scale( Vector2( facing , 1 ) )
        old_facing = facing

and it fixed the twitchy behavior (not for vertical, not sure why your char goes down)

anyway, as @Zylann said, just put the kinematic body in a node2d (container) and flip that

but i mean .. i can't really fault you on why. it does seem logical to just flip the kinematic node..

however, godot is special, we must deal with it

anyway, as @Zylann said, just put the kinematic body in a node2d (container) and flip that

That produces very odd results, at least in 2.1.4. Flipping (via scale) a Node2D containing a RigidBody2D will work for a fraction of a second before it kicks itself back to the original orientation.
flipping_hell.tar.gz

Be sure to turn on visible collision shapes when running this.

OHHH. @DanielKinsman. Oops, I was using KinematicBody! My bad sir, you are correct. RigidBody does have that effect... ¯\_(ツ)_/¯

@Dillybob92 @DanielKinsman I never said to put KinematicBody under a Node2D^^" I proposed to flip visuals instead, so basically not flipping the root node and only do that to Sprites (which can be put under Node2D as long as nothing physics-related is child of it), as the root node must very often be the physics body.

How are you supposed to flip the collision shapes of a KinematicBody without scalling it to (-1,1)?
What @Zylann said works in general, but you can't put a collision shape in a Node2D.

by the way if you delete the code to make it flip and you edit the scaling in the editor to (-1,1) or set it on _ready() function it works fine without going crazy but ofcourse stays like that.
maybe its because is in physics_process. If someone can do it so it runs the script once when you press left or right (like with signals maybe) and see what happens, because I'm not skilled enough in coding

and for putting it as a child of a Node2D it works kind of but when you walk you keep getting way from the origin position of the node2d and when you go to the other way you teleport to the negative x of the current position like fliping a page of a book.

well I hope this gets fixed, this is a must for fighting games for example. even for top-down games too.

zangief_sfa_various_hit_boxes_display

Oh I see. So a quick workaround would be duplicating the KinematicBody, setting the scale to (-1,1) for one of them and then switching visibility depending on which way the characters looks at.

@rtsketo If your collision shapes are not symmetrical, then no, there is no way to flip them with scaling (unless the physics devs implement such a feature officially). An alternative would be to toggle, rotate or offset them instead. Or, simplest: not make them assymetrical in the first place, there are alternatives. And again, I don't know what's wrong with Node2D but when I say "flipping visuals" I am referring to such node structure:

  • KinematicBody (root)

    • CollisionShape1 <-- do something else with these

    • CollisionShape2

    • visuals (Node2D) <-- flip only this



      • Sprite


      • Particles



I've just done exactly that in a generic way, here it is for everyone who wants to use it: https://github.com/DanielKinsman/godot-flippable-physics

Demo project in https://github.com/DanielKinsman/godot-tricks

@DanielKinsman for some reason it doesn't work for me, it shows an error on the line 32,13 if not (n extends Node2D):
_expected ')' in expression_ on FlipHelper.gd

your proyect didn't had the godot.proyect file so I had to do a new proyect and move the documents inside (sorry if I did something wrong I'm new on this git stuff)
FlippablePhysics2D.zip

Issues with my code should be discussed in my repo, not this one. I am using godot 2.x so you may have to adapt it for 3.x. I will probably update it myself once 3.x is released.

Setting the size and position values for every child to make it flip seems unpractical and not every game needs to flip only the visuals to make it look like it changed direction.

I waited for godot 3.0 beta and the bug is still remaining, but guys, I noticed something that may help fix this.

I only know this only happens on KinematicBody2D:

When you are not using move_and_collide() or move_and_slide() in the script this method _(setting the scale to (-1,1))_ works fine, but when you use either of them then it happens. But only when setting the size.X value to a negative value. Doing it with Y works fine and I think I know what is causing this.

When using move_and_collide() or move_and_slide() and you set the size.x to a negative value like -1, the bug happens, and it sets the rotation_degree to -180 only when X is negative.

This may seem like something that is not required to get fixed but if you stop and think about it, this is needed for big bosses where you need to put multiple collision shapes, or in a fighting game like I said before.

Please, if any godot developer can fix this in base of what I said it would be great. I suggest to put an option to fliph and flipv like in the sprite node, that would be handy.

godot 3.0 beta1
fliptest.zip

It isn't tagged as a bug, so I think it won't get fixed. Correct?

I guess, even that it isn't considered a bug, then it can be considered a feature request. If I knew how to do it myself I would fixed it or add the feature to flip horizontally and vertically with a pull request but I don't have that knowledge but I do believe someone will need this when they reach the point I am.

The main request is to be able to flip the main node so every child node flips, but without affecting how the code works. Don't want to be too pushy but if the final godot 3.0 release still has this issue some people will encounter the same problem that I had when they try to do the same thing.

For now I found the way of fixing it anyway, set rotation_degree to 0 after setting scale.x to -1, this is in case someone got the same issue. Now I can start porting a project I'm working on 2.1.4 to 3.0 😄

This could be done for 3.1, I am considering rewritting all the collision code anyway to speed it up, and supporting flipped axies may be doable

To visual whats going on I created a test scene in godot2 and godot3.

Godot2

godot2_kinematicbody2d_transformation

  • the global_transformation.get_scale() don't change and stays at 1,1
  • when move(Vector2(0,0)) is used KinematicBody2D.get_scale() stays at 1,1 but visually it changes
  • no -0 value in transformation

Godot3

godot3_kinematicbody2d_transformation

  • -0 value in transformation
  • global_transformation.get_rotation() does not remain 0
  • move_and_slide(Vector2(0,0)) or move_and_collide(Vector2(0,0)) change global_transformation
  • object.get_scale() and global_transformation.get_scale() are not the same

QuickFix

to flip a KinematicBody2D in Godot3 you need to set the global_transformation inside the _physics_process()

  • scale(1,1) = set_global_transform(Transform2D(Vector2(1,0),Vector2(0,1),Vector2(position.x,position.y)))
  • scale(-1,1) = set_global_transform(Transform2D(Vector2(-1,0),Vector2(0,1),Vector2(position.x,position.y)))

godot3_kinematicbody2d_flip

I'm not the right person to analyze the behavior in godot3 but maybe I was able to provide some valuable information about it.

pending issues

  • why is the global_transformation rotated
  • why there are -0 values in the global_transformation

I have a similar bug in the 3.1 beta but without a RigidBody2D, I used my own physics.

So if you animate a collisionShape2D, and you try to flip the KinematicBody2D node with scale.x, the Animation Player will make the characters goes crazy and flips H very fast while you move to the left. The only fix for this for me at this moment is to make all the animation to the left too. =/

Please please, fix this or at least make a flip_h or flip_y for the CollisionShape2D, cause scale it with -1 doesn't work the same way.

Remember fighting games or animations in general that needs to change the collider position needs this.

Thank you.

@Arkhano bodies do not accept scaling because the state transform and node transform are somehow linked and the physics engine cannot handle scales.
But you can scale the visual representation (sprite) and collision shape, it may be enough in most cases.

It will need a more elegant way to manage scales, though, because more complex things like mesh based figures may need a lot of corrections or special body setup to flip objects.

@eon-s The problem is if you do an animation to the right where the position of the sprite and collisionShape2D are for example x, x + 1 and x + 2 and you scale the collision and sprite to -1, Animation Player will continue doing the animation to the right and not doing the corresponding animation to the left, it flips the sprite but only reverse (not flip) the vertices of collisionShape2D.

The only solution I know for this right now is to make all the animations for the left side too.

One of the items in my TODO list for physics fixes for next Godot version is to ensure they work flipped/mirrored. This is not something that normally works in physics engines, but since we use a custom one I suppose it could be worked around.

For me, it worked with scale.x = scale.y * direction, where direction equals 1 if your character should face right and -1 otherwise.

** scale.x * direction, right?

Nope, it's scale.x = scale.y * direction. I'm using Godot 3.1.2.

** scale.x * direction, right?

Nope, it's scale.x = scale.y * direction. I'm using Godot 3.1.2.

It´s Work! Great!

If the initial scale of the KinematicBody2D node is not (1,1).

Expected code (produces flickering):

onready var initial_scale = scale
var velocity: Vector2

velocity = move_and_slide(velocity,Vector2.UP)

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

Fix:

if velocity.x > 0:
    scale.x = initial_scale.x * sign(scale.y)
elif velocity.x < 0:
    scale.x = -initial_scale.x * sign(scale.y)

Godot 3.2.1. I think is a bug.

I couldn't find an amiable solution to this, so I just set all directional collision to be children of my sprite node, which flips normally in the code. This feels like a cheap solution.

If anyone is still here, you can use flip_v and flip_h on the sprite.

If anyone is still here, you can use flip_v and flip_h on the sprite.

It's not about just the visuals, though, it's about the collisions, etc. too.

I use this and it works fine for me:

func flip_h(flip:bool):
    var x_axis = global_transform.x
    global_transform.x.x = (-1 if flip else 1) * abs(x_axis.x)
Was this page helpful?
0 / 5 - 0 ratings