Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with flipping 2D characters (non uniform scaling) #12335

Open
Tracked by #45334
aaronmzn opened this issue Oct 22, 2017 · 41 comments
Open
Tracked by #45334

Issue with flipping 2D characters (non uniform scaling) #12335

aaronmzn opened this issue Oct 22, 2017 · 41 comments

Comments

@aaronmzn
Copy link

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

@ghost
Copy link

ghost commented Oct 22, 2017

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.

@Zylann
Copy link
Contributor

Zylann commented Oct 23, 2017

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

@DanielKinsman
Copy link
Contributor

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.

@ghost
Copy link

ghost commented Oct 23, 2017

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

@DanielKinsman
Copy link
Contributor

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.

@ghost
Copy link

ghost commented Oct 23, 2017

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

@Zylann
Copy link
Contributor

Zylann commented Oct 23, 2017

@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.

@rtsketo
Copy link

rtsketo commented Oct 23, 2017

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.

@aaronmzn
Copy link
Author

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

@rtsketo
Copy link

rtsketo commented Oct 24, 2017

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.

@Zylann
Copy link
Contributor

Zylann commented Oct 24, 2017

@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

@DanielKinsman
Copy link
Contributor

DanielKinsman commented Oct 24, 2017

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

@aaronmzn
Copy link
Author

aaronmzn commented Oct 28, 2017

@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

@DanielKinsman
Copy link
Contributor

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.

@akien-mga akien-mga changed the title [Feature Request/bug] flipping 2D characters in godot 3.0 Issue with flipping 2D characters (non uniform scaling) Nov 9, 2017
@aaronmzn
Copy link
Author

aaronmzn commented Dec 11, 2017

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

@rtsketo
Copy link

rtsketo commented Dec 12, 2017

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

@aaronmzn
Copy link
Author

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 😄

@reduz
Copy link
Member

reduz commented Dec 13, 2017

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

@puppetmaster-
Copy link

puppetmaster- commented Feb 11, 2018

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

@AttackButton
Copy link
Contributor

AttackButton commented Jan 22, 2019

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.

@eon-s
Copy link
Contributor

eon-s commented Jan 23, 2019

@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.

@AttackButton
Copy link
Contributor

@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.

@reduz
Copy link
Member

reduz commented Jan 23, 2019

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.

@arthurpaulino
Copy link
Contributor

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

@arthurpaulino
Copy link
Contributor

** scale.x * direction, right?

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

@giovanitrentin
Copy link

** scale.x * direction, right?

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

It´s Work! Great!

@luislodosm
Copy link

luislodosm commented Apr 9, 2020

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.

@KaizNike
Copy link

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.

@mageowl
Copy link

mageowl commented Jul 22, 2020

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

@kronok
Copy link

kronok commented Sep 3, 2020

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.

@rayxuln
Copy link

rayxuln commented Sep 4, 2020

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)

@edsulaiman
Copy link

After doing a simple test I concluded that.

  • when we change the value of scale x to -1, the value of scale x will become -1.
  • but if the node position changed, the scale x value will be reset to 1 again.

code:

func _physics_process(delta):
	if (Input.is_action_just_pressed("ui_left")):
		scale.x = -1
	if (Input.is_action_just_pressed("ui_right")):
		scale.x = 1

	print(scale.x)

	var xValue = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
	move_and_slide(Vector2(xValue, 0))

print result when node position is not changed:

1
1
-1 <--- When ui_left just pressed
-1
-1

print result when node position changed:

1
1
-1 <--- When ui_left just pressed
1 <--- Reset to 1 after node position changed
1

Additional information

  • Godot Engine v3.3.2 mono

@DogTorrent
Copy link

DogTorrent commented Jun 29, 2021

I am using RigidBody2D(mode is Character) and I have tried all the solution of above but none of them can solve the problem now...
the only way to flip it horizontally is to change scale.x of its children:

for child in get_children():
	if child.get("scale") != null:
		child.scale.x = -child.scale.x

NOT ELEGANT

I find #5734 (may be related to this issue?) but i think we can at least make a flip_h/flip_v function for RigidBody2D(or anything else with the same problem)

Version: 3.3.2 stable

@ghost
Copy link

ghost commented Aug 26, 2021

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)

this one works for me

@berarma
Copy link
Contributor

berarma commented Sep 20, 2021

If this isn't considered a bug but normal behavior, why isn't it documented anywhere?

The documentation leads you to believe you can animate any property on any node, and the editor let's you do it without any warning. I think this should be considered a bug in the documentation, editor or engine.

I'd like to be able to help fixing it but I'm new to Godot. These unexpected traps are the only negative in Godot.

@Calinou
Copy link
Member

Calinou commented Sep 20, 2021

I'm not sure where this should be documented exactly. Should we add a note to both RigidBody2D and KinematicBody2D? Does this also affect StaticBody2D?

cc @pouleyKetchoupp

@pouleyKetchoupp
Copy link
Contributor

I'm not sure where this should be documented exactly. Should we add a note to both RigidBody2D and KinematicBody2D? Does this also affect StaticBody2D?

Yeah, possibly a note in the documentation for RigidBody2D and KinematicBody2D until it is supported, although I'm not sure it would be easy to find. I don't think it affects StaticBody2D in any way.

Otherwise we could add an editor warning when the scale is set on KinematicBody2D (the same way it's already done for RigidBody2D), and maybe some runtime errors on local transform changed as well.

@eon-s
Copy link
Contributor

eon-s commented Sep 20, 2021

Some things are mentioned in the Physics tutorials
https://docs.godotengine.org/en/stable/tutorials/physics/physics_introduction.html#collision-shapes
https://docs.godotengine.org/en/stable/tutorials/physics/kinematic_character_2d.html#scene-setup

And the RigidBody2D reference
https://docs.godotengine.org/en/stable/classes/class_rigidbody2d.html#description

But maybe it need a separate entry in the physics tutorials that talk about all these "peculiarities" of the engine (shape/body scaling, collision reporting of mask-layers pairs on 3.x, limit with planes, segment shapes, etc.).


The OP issue may need better UX approach, maybe automagic resizes of shapes when trying to scale bodies, I don't know if something like this is planned for 4.

@berarma
Copy link
Contributor

berarma commented Sep 20, 2021

Adding a note in the editor tooltip would be helpful, I guess, and also in the AnimatedPlayer and "Introduction to animation" since flipping a game character is a very common use case.

The Physics tutorial would be the most logic place but since the editor allows changing the property without warning and it even seems to work, it's very misleading. The current note seems to imply that it's an issue only for collision shapes and not for all shapes, while I understand this is a broader issue rendering physics objects.

Thanks!

@DinkleDorph
Copy link

7 year old thread and this is still an issue 😬 . The solution posted by arthur does work though.

@zentaki
Copy link

zentaki commented May 5, 2024

I get completely the OP.
When we design a character for a 2D we think of one side and just flip it; The only problem here is that with non symmetrical shapes will cause clipping. So character and it's features should be done in such design that maintain symmetry so the visual and collision work together.
Lets say I have a character with non symmetrical shape, then if I am already colliding with the wall with the minor side then I flip the character, the bigger side will clip through the wall, probably making it stuck.
But that should not be a reason not to feature godot to flip the entire object.
yeah that clipping can occur with whatever you might add into the character for the same reason the other clipping does.

I guess this is why they did not put such a feature in godot, and must have other strategies which are not so obvious to deal with such things.
Maybe would be easier to have the feature and deal with the clipping later.

In the 3D we do have same problem, not for turning character around and clipping, but for clipping the weapons on walls. And to solve that is also not so simple at first. A character that has a gun, will shoot bullets at the tip of the gun. IF you collide the gun to the wall you solve the clipping problem but make the game collisions against wall looks weird, but if you leave as it is you would be able to shoot through walls which is mostly game breaking and undesirable, One way you break the immersion feels the other you break the mechanics. So it must be solved in a clever way, If I recall is solved with some kind of illusion that does not break the immersion feel but makes sure that the mechanic does not break either.

Anyway, it seems that we should also do some more work to make it happen nice with a 2D world. So flip the image as you would, but when you do that you also have to flip the other stuffs, guess it can be made using position changings, or using 2 objetos that represent 2 points and you change the point to be used depending on the side you are facing. I think overall trying to use scale flip will mostly break things apart.
For animation and collisions you can use animation player to do the work if I am not mistaken. I am sure is as simple as the animation itself to set up a changing collision according to the animation, and as a thumb of rule, is always good to have symmetrical shapes, otherwise things will get really hard to do.

But yes it does sucks not have a simple way like just flipping the whole object instead. I could make things simpler but in the other hand you would have to deal with the consequences later anyway

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests