godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
87.08k stars 19.56k forks source link

RigidBody and RigidBody2D ignore scaling on runtime #5734

Open akien-mga opened 8 years ago

akien-mga commented 8 years ago

Operating system or device - Godot version: Mageia 6 x64, Godot 2.0.4.1 and master HEAD.

Issue description (what happened, and what was expected): RigidBody2D does not respect its scale parameter, or that of its parents. When scaling a RigidBody2D (directly or by scaling its parent), it shows properly scaled in the editor, but on runtime the scale is ignored.

Steps to reproduce:

  1. Add a RigidBody2D, with a Sprite to actually see its size
  2. Scale it down or up
  3. Run the project

Link to minimal example project (optional but very welcome): 3.x MRP (works in master too): RigidBody2D_scaling_bug.zip

(Old 2.1 project: RigidBody2D_scaling_bug.zip)

The project has a root Node2D scaled by (0.2, 0.2), and RigidBody2D, StaticBody2D and KinematicBody2D children. When running the scene, you'll see that only the RigidBody2D does not respect its parent's scale.

reduz commented 8 years ago

This is intended, because physics bodies manage their own transform which overwrites the ones you set. Some kind of workaround could be done eventually, but not until 3.0

ivanskodje commented 7 years ago

Posting this here in case someone else also needs a "hack" to forcefully make scaling work on RigidBody2D.

You can use integrate forces to set the scale. For example,

func _integrate_forces(state):
    set_scale(Vector2(-1, 1))
JarLowrey commented 7 years ago

Also if you set the mode to "Kinematic" it will scale https://github.com/godotengine/godot/issues/7375 . _integrate_forces wasn't working for me.

Edit: got _integrate_forces working, think I copied something wrong. That's def better than changing your body type.

reduz commented 6 years ago

may do a workaround for 3.1 I am still not convinced it will work, though

raffomania commented 6 years ago

Is there a way to scale rigidbodies before instantiation?

reduz commented 6 years ago

there should be a tracker with all these things, so i can work and remember everything after 3.0

aaronfranke commented 6 years ago

@reduz The tracker for items on the 3.1 milestone is https://github.com/godotengine/godot/milestone/7

reduz commented 5 years ago

Sorry, out of time for 3.1 to create a workaround, kicking again.

Houkime commented 5 years ago

Probably it needs to be at least documented in RigidBody2D class description while workaround is in progress? It took me a little while to decide that it is this issue that is screwing everything up.

t-karcher commented 5 years ago

I fully agree with @Houkime : A hint in the class description would have saved me almost an hour of tearing my hair out trying to figure out what's wrong with my code.

nathanfranke commented 4 years ago

This issue is very prevalent with HeightMapShape ... Since HeightMapShape does not have any options for cell size and scaling shapes is unsupported, it is impossible to have a heightmap that isn't 1x1 cells. I hope this can be fixed in 3.2 or at least high priority in 4.0...

bluenote10 commented 4 years ago

I'd love to see this included in 3.2 as well (a major showstopper for the project I'm working on...). Is there actually any possible work-around in 3.1?

ThakeeNathees commented 4 years ago

I was trying to fix this bug, but when the scale is not (1, 1) after a physics frame the scale is changing by a small value ( 0.00001 ) because of the physics process which cause the body to flicker.

and the transform was orthonormalized intentionally which cause the scale to (1, 1). https://github.com/godotengine/godot/blob/bd61281a5f515065b05be008dd3d6b73a03f5a7c/servers/physics_2d/body_2d_sw.cpp#L293 I think the scale must be (1, 1) to make the physics work properly and we have to prevent the user from scaling it. rigid-body-fix

hmans commented 4 years ago

Any new insights on this? I'm seeing the comments on this "simply not being possible", but it pretty much makes it impossible for me to build the game I want to build with Godot (while it's not a problem in pretty much any other engine, including one I hacked together myself using Box2D).

If performance is a concern (since this would probably involve combine parent transforms and then rebuilding the physics shapes), only allowing this in _ready() but not allowing it afterwards would be a great middle ground.

Are there any recommended workarounds (other than creating/adapting the collision polygon programmatically, which I suppose would work)?

nathanfranke commented 4 years ago

Someone could make a collision shape scale function for 3.2. I'm not sure what the situation is for 4.0 right now

olson-sean-k commented 3 years ago

Just wanted to bump this issue. I encountered this in my very first learning project and was confused about the transform (the scale) of a Node2D not being applied to its children (in this case, a RigidBody2D node). This was especially baffling, because the editor rendered what I expected but playing the scene did not. I see now that this is mentioned in the documentation of RigidBody2D, but I missed this.

Are there any thoughts, designs, or advice about how to create instanced scalable nodes that contain a RigidBody2D? Is it possible for the scene topology below to be scaled, or is this considered an antipattern? If this topology is not ideal, what is a more ideal topology?

aaronfranke commented 3 years ago

Are there any thoughts, designs, or advice about how to create instanced scalable nodes that contain a RigidBody2D?

The only supported option is to scale the child nodes.

olson-sean-k commented 3 years ago

The only supported option is to scale the child nodes.

Gotcha. Thanks for the help.

35614 should help alleviate this, but current quirks prevent merging it.

Awesome, that looks very helpful. 😄

I can see how applying (arbitrary) parent transforms to physics bodies could be tricky. Hopefully an intuitive solution can be found.

Okxa commented 2 years ago

Using 3D, and I am not sure if this is correct, but it seems that it mostly works when scaling uniformly (the RigidBody:ies are instanced with MeshInstance and CollisionShape as children):

image

The other rigidbody is scaled [0.2, 0.2, 0.2] while the longer is [0.2, 0.4, 0.2]. Of course if it's not scaled uniformly, it results in a weird change of scale when the rigidbody rotates. (It seems to work OK if the scale is uniform.)

In any case I think this is something godot needs. For example this would be usefull when working with instanced models, but scale each instance separately.

Calinou commented 2 years ago

https://github.com/godotengine/godot/pull/50637 may allow for using non-uniformly scaled PhysicsBodies, but right now, only uniform scaling is supported.

Okxa commented 2 years ago

50637 may allow for using non-uniformly scaled PhysicsBodies, but right now, only uniform scaling is supported.

I see, atleast uniform scaling is working.

If it is truly supported (uniformly), should the warning message be adjusted (if it's not already) to reflect that? Or is the scaling still somewhat unsupported feature?

hmans commented 2 years ago

I'm playing around with the current 4.0 master, and was disappointed to find out that it's still not possible to scale a RigidBody2D (I haven't tried its 3D counterpart):

image

As I've raised before, this makes certain use cases (like spawning asteroids of slightly randomized sizes) cumbersome to implement, as detailed in this tweet of mine and the thread following it.

I've been wondering about this issue for a long time, feeling that I must be missing something obvious, but so far I have not managed to find a solution, or at least an answer to why this is even a thing (consider that no other game engine I've used has had issues like this.)

Why does the physics engine even want to scale the body at run-time?

pouleyKetchoupp commented 2 years ago

This is definitely one of the topics I'd like to improve for 4.0, it's just nobody had time to work on it yet.

What's needed is to check the current state for different cases, make some changes in the engine to handle more of them and adjust the warnings to be more accurate for the remaining cases.

I'll try to spend some time on this topic once the general API changes and major bug fixing are done for 4.0, but any contributor is welcome to give it a try.

hmans commented 2 years ago

FWIW, and I'm saying this as someone who knows absolutely zero about Godot's internals, I think what might also work fine is to leave RigidBody and RigidBody2D as is, but allow us to have an intermediary node between it and the collision shapes, and allow that intermediary node to apply scaling to its contained nodes (collision shapes included). (Related: @aaronfranke's proposal here: https://github.com/godotengine/godot-proposals/issues/535)

pouleyKetchoupp commented 2 years ago

Good point! It could be a way to solve the problem in case there's any technical limitation to allow scaling the rigid body itself.

akien-mga commented 2 years ago

Updated MRP for 3.x (works in master too): RigidBody2D_scaling_bug.zip

akien-mga commented 2 years ago

Copying a comment by @pouleyKetchoupp on the current state of RigidBody scaling (from #35614), at least as of Feb 2021:

The current state with scaling physics objects is: Non-uniform scale: This is not supported for either bodies or collision shapes and there's no plan to support it in the future. Uniform scale: This is supported for collision shapes, not for bodies of any type. This limitation will be lifted in the future.

Parent node scaling: This is also not supported for bodies. Actually, it would be best to test global transforms rather than local ones in order to catch parent node scaling. Cases where the rigid body scale cancels the parent scale seem to work fine, so testing global transforms should be enough. edit: For collision shapes, non-uniform scale would be tested on the local transform.

BaddRadish commented 2 years ago

apparently this problem still exists in 3d in version 4a3?? is this on the to do list??

EDIT: you CAN resize STATIC bodies and adjust size, so this wont effect levels comprised of static colliders.

for DYNAMIC rigid bodies you have to manually pair up meshes with hitboxes and resize them both every single time. this makes reusing dynamic rigid bodies limited to one size. i /think/ you can bypass this limitation with scripts?

i dont know if this isnt already planned, but this IS a problem. i know yall have your work cut out for you, but please consider it for the todo list. EDIT i understand this may be outside of your control. i'll try not to complain to much about it. ;)

Calinou commented 2 years ago

apparently this problem still exists in 3d in version 4a3?? is this on the to do list??

pouleyKetchoupp is no longer available to work on the physics engine, so I can't guarantee this limitation will be lifted for 4.0. We have very few contributors who are knowledgeable with physics and have time to submit contributions to the existing physics engine implementations.

Okxa commented 2 years ago

One update on this issue; it seems that on godot 3.4.2-stable the uniform scaling (like I posted on this thread earlier) works when using bullet, but not on godot physics.

(That came up when I tried to switch to godot physics for #29392 / #34596, because https://github.com/godotengine/godot/pull/56801 has not been merged.)

hmans commented 1 year ago

Just a quick ping to find out if anything has happened in the meantime. In 4.0, apparently Bullet is no longer available, only GodotPhysics3D. Scaling a scene that contains a RigidBody3D (and probably also its 2D equivalent) still yields warnings & scaling resets at scene start.

I understand this is an Open Source project and I could just take the source and fix this myself, but this is outside of my abilities. I also understand that features are usually added/fixed by people who need them, so please don't misunderstand my comment here as me demanding anything. I just can't imagine that I'm the only person on the planet who'd like to take a scene prefab and spawn smaller and larger versions of it?

(As a reminder, I am very specifically not looking for being able to change the scale at runtime; just at spawn time. And uniform scale only would be perfectly fine.)

akien-mga commented 1 year ago

@hmans You can work it around fairly easily by changing the size of the collision shape, instead of scaling the RigidBody.

Here's a quick example in 2D: image

You'd scale the parent node, whose script takes care of setting the size of the collision shape and propagating the scale to the sprite. The RigidBody itself is not scaled and works fine.

hmans commented 1 year ago

Thanks @akien-mga, but in that example the RigidBody node will now simulate (and move) independently from its parent node, correct? So this isn't of much use as a reusable prefab scene.

I remember adding code to a Rigidbody node (the last time I actually tried building a physics game with Godot :b) that would apply the Rigidbody's scale to its children like you're doing, and then reset the Rigidbody's own scale back to 1/1/1.

Also consider that this can be a little more complex than just the children's scale. Colliders might be offset (with their origin not at 0/0/0); this would also need to be scaled accordingly.

(It would be immensely useful here if colliders didn't need to be parented directly to their rigidbodies. Then we could have an intermediate node between the two and just scale that one.)

I'm hoping for a future version where hacks like these are no longer required.

(Don't get me wrong, I fully understand that these engines also just rescale collision radii/polygons/etc. — I'm just making an argument for Godot abstracting this away so you can have a small ball and a large ball from the same prefab and not go hunting for hacks like the one above.)

I'll check again next year :-)

hmans commented 1 year ago

Woohoo:

https://user-images.githubusercontent.com/1061/218258432-299d1bd6-5d8d-4165-8c4b-80fc4135098f.mov

As a workaround for this issue, I've created a new class called ScalingRigidBody3D in Godot 4.0 (feedback welcome):

@tool
class_name ScalingRigidBody3D
extends RigidBody3D

var _size := 1.0

@export var size : float:
    get: return _size

    set(v):
        _size = v
        _apply_size()

func _ready():
    _apply_size()

func _apply_size():
    for child in get_children():
        if child is CollisionShape3D:
            child.scale.x = size
            child.scale.y = size
            child.scale.z = size

This can probably be improved further.

My personal wish for Godot's (3D) physics would be to

Baby steps, but we'll get there :)

Update!

Improved version of the above class — doesn't use a new property, but just applies its own scale to its children as described in the notes above:

class_name ScalingRigidBody3D
extends RigidBody3D

func _ready():
    _apply_scale()

func _apply_scale():
    # apply my own scale to my children
    for child in get_children():
        if child is Node3D:
            child.scale *= scale

    # reset my own scale
    scale = Vector3.ONE

Update 2023-03-07:

I've polished the code, gave it some upgrades, and packaged it into a Godot Plugin. Please see this comment for details.

koekhaos commented 1 year ago

@hmans New to Godot and going through the tutorials wanted to try to make the 2d balls smaller and found this issue. Would your script work somehow for 2d too if just changing a few calls inside and changing to vector2? Thanks!

hmans commented 1 year ago

@hmans New to Godot and going through the tutorials wanted to try to make the 2d balls smaller and found this issue. Would your script work somehow for 2d too if just changing a few calls inside and changing to vector2? Thanks!

Yup, that should work!

If there's interest, I could wrap this into a plugin and publish it in the Asset Library (but probably for 4.0+ only.)

koekhaos commented 1 year ago

That could be super useful as a lot of people seem to want this but doesn't seem to be in the works anytime soon as it looks like the milestone is set to 4.1 now to fix/implement.

hmans commented 1 year ago

Hi and good morning to everyone following this issue! I've packaged my glue code into a Godot 4.0 Plugin that you can add to your project to work around this issue:

Godot RigidBody Auto Scaler 🎉

It's "fire and forget"; just add it to your project and it'll fix things up automatically. I've also given it a couple of upgrades: it now respects parent scale, also fixes child positions, and works for both 2D and 3D.

While the Asset Library listing submission is still pending, you can find it here:

https://github.com/hmans/Godot-RigidBody-Auto-Scaler

Feedback welcome! (But preferably outside of this issue thread. The plugin README is listing a bunch of ways you can get in touch with me!)

Okxa commented 1 year ago

Another problematic thing when scaling rigidbodies with the workaround (eg. RigidBody is at scale 1 and CollisionShape & mesh are at your desired scale) is that automatically calculated inertia does not seem to scale.

If you use PhysicsServer3D.body_get_direct_state(get_rid()).inverse_inertia.inverse() to check inertia, it is the same regardless of the child objects scale. This makes objects fall somewhat weirdly looking.

If I set the scale manually to something like calculated_inertia * scale it looks slightly better. (For example if calculated inertia is about (15000,7000,15000) on each xyz axis, and scale is 0.2, it's about (3000,1400,3000) respectively)

(However if I set the RigidBody3D mass to 1000, and the inertia in the editor for each axis, the model flies downwards through the floor, as if it had massive gravity. If I only set to x & z axis, it does not go through floor. When using the above function to check inertia, it reports the value set in editor for the x/z axis, and for z axis it's the autocalculated value.)

EDIT: As a workaround, it is possible to scale the inertia with script (at runtime), where ever you just have access to PhysicsDirectBodyState3D, like _integrate_forces() (or by getting it with PhysicsServer3D.body_get_direct_state(get_node(".").get_rid())). Using _integrate_forces() or _physics_process() is usefull as inverse_inertia is not set (inf) at _start():

var has_set_inertia = false

func _integrate_forces(state):
    if !has_set_inertia:
        var inertia_actual = state.inverse_inertia.inverse()
        if !is_inf(inertia_actual.x):
            # get scale from child collider node, assuming you have already applied scale with the workaround
            self.inertia = inertia_actual * collider.scale
            has_set_inertia = true
hmans commented 1 year ago

Another problematic thing when scaling rigidbodies with the workaround (eg. RigidBody is at scale 1 and CollisionShape & mesh are at your desired scale) is that automatically calculated inertia does not seem to scale.

If you use PhysicsServer3D.body_get_direct_state(get_rid()).inverse_inertia.inverse() to check inertia, it is the same regardless of the child objects scale. This makes objects fall somewhat weirdly looking.

Good point! I'll look into this. Thanks!

Okxa commented 9 months ago

Update: Using Godot Jolt physics engine, rigidbody scaling gets slightly better. (Godot Jolt is consired as physics engine, see: https://github.com/godotengine/godot-proposals/issues/7308)

The prop_physics are sized the same, but with this difference:

Also tested with godot physics and expectedly above function printed different inertia for the rigidbodies.

jitspoe commented 2 weeks ago

Just wanted to add to this, as I think it's the same/similar issue: It seems the rigid bodies will attempt to invert the scaling when re-parented to something of a different scale.

When I was working on the Godot plushy for KOOK, I wanted to have the arms and legs flop around, so I had joints and rigid bodies for them. When I pick rigid bodies up, they would get reparented to a bone on the player. The first person player is actually much smaller than everything else to avoid clipping through walls, so the objects picked up got scaled down (and there's an additional scale factor as well, just to fudge things so they look right, like 0.8x). The pinned rigid bodies, which are a child of the plushie would actually get scaled UP when everything else got scaled down, so they'd be sitting at > 1.0. When I dropped the object and changed the parent back to the world, they'd still be scaled larger (possibly because it was a non-uniform scale, as stuff in first person is actually squished as well).

Here's a clip from the dev stream where this happened. The white cube is a mesh that's a child of the RigidBody3D, for visual feedback: https://clips.twitch.tv/InspiringFrozenDotterelUWot-XoUI1ECrtU8-isdX

hmans commented 2 weeks ago

Just wanted to add that current GodotJolt stable seems to support this out of the box. It even seems to support non-uniform scaling. That's pretty cool!

Godot 4.2 is still giving me the little exclamation mark warning that I'm doing something that I'm not supposed to be doing, but it looks like it can be safely ignored.

Okxa commented 1 week ago

Just wanted to add that current GodotJolt stable seems to support this out of the box. It even seems to support non-uniform scaling. That's pretty cool!

Can confirm, here is a clip using Godot 4.2.2 and godot-jolt stable release 0.12. Each RigidBody is scaled just by their transform. IIRC this was not working on 0.11 (which was version I previously used), but not sure.

https://github.com/user-attachments/assets/6d547802-5c70-486d-98c1-b7a05915718a

The inertia also seems to be still scaled correctly, as was when using previous godot jolt version.