-
-
Notifications
You must be signed in to change notification settings - Fork 21.3k
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
3D KinematicBody slides with move_and_collide #18433
Comments
This is also happening in 2D, the kinematic body slides down the slope when using I made a minimum project with this behaviour, just make sure |
I've run into this problem while trying to create a character controller that also handles stair stepping. Using move_and_collide() as a sort of trace and down-stepper. Only now they slide down slopes at speeds determined through the safe margin :( |
Same here. Any workarounds? I'm using 3.0.5 |
Setting the kinematic body safe margin to a very very small value should mitigate the sliding. |
Sorry for bumping an old issue, but I can still reproduce this even with margin set to |
Making the "move the object outside of the solid world" step optional might go a long way towards making this less of a problem. The "pop outside of anything you're touching" step should only happen once a frame, but people using move_and_collide might want to use it multiple times per frame. If you're trying to write your own movement solver (which is absolutely imperative for some genres!) then you're going to be using move_and_collide until you're nearly touching things multiple times a frame, inside the safety margin, but that doesn't mean you want to pop out of them yet. Popping out of the world can also sometimes be done better by the movement solver, at least in simple cases like "hey, I'm standing on a slope, I only want to pop out of it in a straight vertical line". |
It would be nice if, instead of moving to a point where the collision needs to push the KinematicBody out, move_and_collide() moved all the way up to, and stopped at, the point where a collision would happen. Like, move_and_collide(), not move_and_penetrate_then_push_away_from_the_normal(). :) |
Workaround:
IMHO, move_and_collide() should be doing it this way already (and is what I would expect from the description in the docs). Otherwise why also have move_and_slide()? (; |
I couldn't get this workaround to work. The travel is a vector3, so multiplying it by the move direction just got crazy results and teleported my character into walls and such. I tried adding the pre-move transform and travel together, but that just had the same result as not using the workaround (still had the slow slide down hills). I also tried to get the length of the travel and multiply that by the move direction, but that ended up making the character fall through the ground. Looking at the source, it seems the travel combines the movement and recover motion, so I don't think using this value can result in anything much different from the initial behavior. I tried messing around with the remainder, since that doesn't take the depenetration into account, but that resulted in the character slowly sinking into the ground. |
After a few hours digging around in the physics code, I eventually decided to do an ugly hack: If the horizontal velocity is 0, and the ground touched has a walkable slope, I set the horizontal component of the position to the position prior to moving. This specifically fixes the case of not moving while on a slope causing the character to slide down, but does not fix the fundamental issue of the margin nudging things around to unexpected locations (instead of the move_and_collide() just stopping at the desired collision point). The bulllet code is broken into 3 phases (see SpaceBullet::test_body_motion()):
I tried disabling both of the depenetrations and actually got much better results for the most part. Unexpected sliding and nudging were gone, and the normals seemed to be correct (most notably on non-uniformly scaled collision, which can get really wonky, especially with small margins). Unfortunately, this resulted in the character sticking frequently when moving parallel to surfaces (such as walking on the ground). Fixing this would likely require nudging the kinematic body slightly, bringing us back to square 1. I wonder how other engines handle this. |
Other engines are basically just very careful about exactly when they do depenetration, and sometimes "how" as well. move_and_collide would make a lot more sense if there were some way to control when and how depenetration happens, like only doing it once per frame even if you call move_and_collide multiple times per frame, or making it try to go straight up/down before falling back to using collision normals. |
Thanks @raincomplex for the solution! It was enough to get me the rest of the way there, which I accomplished by rounding off the starting position and movement vector to a fairly precise grid via Vector2.snapped(Vector2(0.000001, 0.000001)). |
it seems to me that the issue is SpaceBullet::recover_from_penetration godot/modules/bullet/space_bullet.cpp Line 1284 in c4daac2
With that, when hitting a collider with an angle, when the recovering happens it basically is reflected from the collision object, moving back along the collision normal. |
recover_from_penetration is used not just after tracing but also before tracing, intended purpose being pushing the object outside of anything it's already overlapping (depenetration). The code you're pointing out is correct in that context (depenetration). |
I tried rewriting the test_body_motion function to not use the margin or recover from penetration and have it PRETTY CLOSE to working the way I would expect it to, but there are still a few oddities/edge cases that I need to work around. This is what I have so far:
The old code was kind of sketchy in that it allowed the movement to penetrate a set amount during the sweep, then it recovered from the penetration, rather than simply sweeping and colliding:
m_allowedCcdPenetration was 0.04, the default margin value. I was actually using a different margin value than that. Not sure if that makes things better or worse. Things I've noticed with my changes: Issues with bad normals are MOSTLY fixed. For whatever reason, I still get some bad normal when moving quickly. Not sure why that would make any difference, but... shrug. Also some precision issues that seem worse than I would expect (ex: start colliding, back up, then move back down more than I moved up, but don't collide). Bullet doesn't always report a collision if you start inside of something. Sometimes catch the edge of two objects that are flush next to each other. This was something I was hoping to fix, but instead made worse. :| Easiest way to repro bad normals is to take a box with collision and scale it a bunch non-uniformly (like make it super long). With the old logic, you'll get normals as though you're running on bumpy ground. With this code it's closer to what it should be. Still need to iterate on this a bit more. Might be a while before I get back to it. Feel free to take my WIP code and tinker with it. |
Bullet's convexSweepTest (or more specifically the btConvexCast::calcTimeOfImpact functions that it eventually depends on, see here) works by having distance support functions, which return a conservative approximation of distance, and moving closer to the target body until the distance support function is smaller than a certain value (0.0001 by default in 32-bit bullet) or it loops too many iterations (32 in 32-bit bullet). So you can't rely on it to give particularly precise, or stable, results, especially with wildly different speeds. Also, cylinders and meshes are more likely to give bad normals than other shapes in bullet. |
Oof. What's a guy have to do to get a nice, clean sweep of a shape that just does some exact math for a convex intersection point? I'm mostly doing box to box collision tests. Is that too much to ask for? I don't want these mushy physics for my platformer. :( |
Just tried it in GodotPhysics and Bullet. They both seem to do the slide thing. Just wanted to point that out. (edited) |
I was responding to consistency concerns in jitspoe's post. |
CC @madmiraal |
Still valid in 3.2.4 beta4 |
Can be still reproduced in 3.2.4 beta 5, but occurs only when using Bullet Physics (default settings). Switching to Godot Physics 3D fixes the issue. I've opened #45004 for the 2D version of this bug from #18433 (comment) to track it separately. |
me too, but my case is whatever physics engine you chosed that it still slides on MacOS |
@snougo Could you please share a minimal project for your case? |
@snougo Thanks! Switching back to non-Bullet specific issue since it can be reproduced in Godot Physics with the MRP from #18433 (comment). |
Not sure if my input is still helpful but a proper way to avoid sliding using gdscript would be
|
Godot version:
3.0.2
OS/device including version:
Windows 10
Issue description:
KinematicBody slides slowly along a slope when applying constant downwards movement using move_and_collide(). Expected it to come to a complete stop after first collision with the slope.
This issue was not reproducible with KinematicBody2D. Safe Margin is set to 0.001.
Steps to reproduce:
Create a slope using StaticBody with a CollisionShape.
Minimal reproduction project:
move_and_collide_sliding.zip
The text was updated successfully, but these errors were encountered: