godotengine / godot

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

[Bullet] move_and_slide() causes jitter when colliding with corners between walls #32182

Open reptofrog opened 5 years ago

reptofrog commented 5 years ago

Godot version: 3.1.1.stable.official

OS/device including version: Linux / Fedora 30

Issue description: move_and_slide() can cause a lot of jitter when KinematicBody collides with corners, or anything actually. (sometimes even with simple 90° corners)

You can observe this in pretty much every single project that uses move_and_slide(), including the FPS Tutorial from Godot documentation.

Notes:

Steps to reproduce:

Minimal reproduction project: godot-fps-master.zip

CptPotato commented 5 years ago

I can confirm this. This is the reason I switched to rigid body physics instead.

kakoeimon commented 5 years ago

Yes, this is a problem of capsule collision shape and sphere shape. Never worked properly as far I remember.

For now you may consider using a cylinder or a box shape with a ray shape at the bottom with length of the maximum step height your character can climb.

reptofrog commented 5 years ago

I might be wrong, but I think jitter happens with a box shape too. (need to test this properly)

reptofrog commented 5 years ago

Yep, looks like it's happening with a box shape as well.

narcissawright commented 4 years ago

This is an issue for me as well. It is especially apparent in corners, but it also happens on walls. It doesn't seem to happen with simple convex shapes (like the grey rectangle in the attached gif).

problem

DenisBelmondo commented 4 years ago

Hello there, I can confirm that this still occurs in v3.2 Alpha 2. As a side note, calling move_and_slide*() on each separate axis can help mitigate the issue like so:

vel.x = move_and_slide_with_snap(Vector3(vel.x, 0.0, 0.0), snap, Vector3.UP, true, 1).x vel.y = move_and_slide_with_snap(Vector3(0.0, vel.y, 0.0), snap, Vector3.UP, true, 1).y vel.z = move_and_slide_with_snap(Vector3(0.0, 0.0, vel.z), snap, Vector3.UP, true, 1).z

As a side effect, the player unfortunately slides more slowly along walls and the jittering is only reduced when using Bullet instead of GodotPhysics using this method. It's also a pretty ugly hack.

As far as using move_and_slide() as it was intended went, I tested this with reasonably complex CSG constructs and level geometry I made in Blender and the jitter occurs only with these, further corroborating @narcissawright's claim that it never happens with convex shapes.

kinematicbody-plus-demo.zip

Here is a demonstration project that contains a simple CSG "garage" (the one immediately in front of the player in the scene). Moving into the corner produces jitter, but the “towers” behind the player, which are ordinary convex BoxShapes, do not produce jitter when moving into the corner.

madmiraal commented 4 years ago

Duplicate of #29392?

grevius5 commented 4 years ago

I confirm that this problem is still present in the 3.2rc1. @Calinou I think is a big problem, can we label it to be fixed in this milestone? Now if you have a simple 3d platform if you stay between 2 perfect aligned static platforms you will see this jittering and is frustrating to fix this issue.

Calinou commented 4 years ago

@grevius5 We need more information before we can look into fixing this. It'd be worth testing whether this issue occurs in Godot 3.0 (and to a lesser extent, 2.1). If the bug doesn't occur in those versions, you could then look into bisecting the commit where the bug started appearing. This requires building the engine from source many times, but it should be entirely feasible.

In the meantime, I wonder if lawnjelly's smoothing-addon can be used to make the bug less noticeable.

DenisBelmondo commented 4 years ago

@Calinou you may be aware of this already (apologies if you are) but in #34596, @madmiraal provided some greatly needed specificity and cited this PR as the point at which the jittering started to appear in GodotPhysics and Bullet respectively. Do you think it could lend sufficient insight to the team?

Poobslag commented 4 years ago

I had this problem in a 3D platformer I was making. When a character (a kinematic body) ran along a set of boxes (static objects) 1 unit high, my character's Y-coordinate would oscillate between 1.06 and 1.10 as they crossed a corner. For a character 1 unit high, this results in the camera jittering up and down by 4% everytime they crossed a boundary.

I worked around the issue by scaling every object in my game by a factor of 1,000%. The jitter is still about 0.04, but when the objects are 10x bigger it's harder to notice.

Poobslag commented 4 years ago

The small amount of jitter remaining after my previous fix (scaling everything up 1,000%) was still bothering me, so I came up with a second fix which removes the remaining jitter. Here's my new gravity function.

func _apply_gravity(delta: float) -> void:
    _velocity += Vector3.DOWN * 400.0 * delta

    # If acceleration via gravity makes our momentum something small like -6, there's a risk we won't actually collide
    # with the surface we're sitting on which causes causing an unpleasant jitter effect.
    if _velocity.y < 0.0 and _velocity.y > -10:
        _velocity.y = -10

The jitter occurs because the Y velocity is a small number like -6, which is too slow for Godot to detect a collision in some cases. Then the Y velocity increases to something faster like -13 and Godot detects a collision. It alternates between these states every other frame, causing the jitter

With this if statement I've added, the Y velocity is never a small number like -6, so Godot always detects a collision. You can adjust the '-10' value based on what works for you.

pouleyKetchoupp commented 3 years ago

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.

NicolasAubinet commented 3 years ago

I just experienced this bug on Godot 3.3. Switching to Godot Physics 3D fixed it for me as well.

VadimBigBoss commented 3 years ago

Switching to Godot Physics 3D does NOT fix this issue.

VadimBigBoss commented 3 years ago

To solve this problem, there must be a way to check that we are in the corner.

Art-Studio commented 3 years ago

I found a simple way, the jitter is almost imperceptible if you give a slight tilt for a СollisionShape, for example along the x-axis rotation degrees 0.1 and change the vector in move_and_slide() or whatever you have from (X, 0, Z) to (X, -0.1, Z).

pouleyKetchoupp commented 3 years ago

Switching to Godot Physics 3D does NOT fix this issue.

@VadimBigBoss Could you provide a minimal project to reproduce this issue with Godot Physics?

discomrade commented 2 years ago

Here is a very minimal 3D project that I believe reproduces this issue in both Bullet and GodotPhysics. MiniCollisionTest.zip

Steps to Reproduce: (tested on 3.4.0) 1) Select physics engine in Project Settings (Bullet or GodotPhysics) and test game 2) Move forward until colliding with green wall (W or Up key). 3) While continuing to push forwards against the wall, continuously hold right key to collide with blue wall (D or Right key). Notice effect. 4) (optional) Repeat with other corners by forcing your character into them. 5) (optional) Change collider to another shape. I reproduced the issue with a Sphere and a Cube, didn't test any others.

I believe this proves it is a major issue that affects both of the 3D engines, affects a commonly used feature and occurs during very basic usage.

note: I have made an equivalent project in Godot 4.0 (dev https://github.com/godotengine/godot/commit/2017590ef6ebb27a6c3ae0d9d7880a0fa6dd9b3c) and can reproduce the issue when Block on Wall is disabled.

prominentdetail commented 2 years ago

I'm running into this issue too in 3.4.2, which causes is_on_floor() to become unreliable.. It's really annoying, as I'd rather not have to look for workarounds for something that should be simple. floor

Calinou commented 2 years ago

As mentioned in Shifty's Godot Character Movement Manifesto, the is_on_floor() part can be worked around by using a filter on the is_on_floor() check:

  • Unstable floor results make my 'is grounded' flag jitter
    • This seems to be down to the collision margin system
    • Use a rolling window (2-3 frames should be sufficient) that folds down to a single bool using OR to de-noise the floor check

In other words, create a function that wraps is_on_floor() and returns true if is_on_floor() returned true at least once in the last 3 physics frames (or 2, but it may not always suffice).

Zireael07 commented 2 years ago

@Calinou: Instead of cross-posting an external tutorial, this should be documented somewhere, and even better if a wrapper were provided out of the box since it fixes a known issue.

prominentdetail commented 2 years ago

In other words, create a function that wraps is_on_floor() and returns true if is_on_floor() returned true at least once in the last 3 physics frames (or 2, but it may not always suffice).

Thanks! That solved that issue. That link also is interesting too. I'll have to try those other things if I try to do more complicated stuff in the future.

akien-mga commented 2 years ago

For the record, I can confirm that this issue is still reproducible in the current 3.x branch (including with #56801 which fixed other similar issues, but not this one). I tested all three MRPs linked above to confirm that the bug is reproducible in 3.5 RC 1 and in #56801.

https://github.com/godotengine/godot/issues/32182#issue-494793489 https://github.com/godotengine/godot/issues/32182#issuecomment-559967351 https://github.com/godotengine/godot/issues/32182#issuecomment-985066744

It does seem to be Bullet-specific indeed, switching the above three MRPs to GodotPhysics solves this specific issue.

IvailoBurov commented 2 years ago

I found a simple way, the jitter is almost imperceptible if you give a slight tilt for a СollisionShape, for example along the x-axis rotation degrees 0.1 and change the vector in move_and_slide() or whatever you have from (X, 0, Z) to (X, -0.1, Z).

Changing Vector3.UP with Vector3(0, -0.1, 0) seem to solve issue for me (Godot 3.4.4)

discomrade commented 1 year ago

Here is a very minimal 3D project that I believe reproduces this issue in both Bullet and GodotPhysics.

note: I have made an equivalent project in Godot 4.0 (dev 2017590) and can reproduce the issue when Block on Wall is disabled.

I have tried recreating this in various Godot 4.0 alphas and betas (including the current beta 9) and the effect is far less noticeable than in Godot 3.x.

MossFrog commented 1 year ago

This issue is still present in GDPhysics, the bottom collision point can oscillate between 2 values when using move_and_slide_with_snap() Godot Version 3.5.2 Stable, even on a flat surface after colliding with some corner or edge