godotengine / godot

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

VehicleWheel sinks into other colliders #45339

Open MrMinimal opened 3 years ago

MrMinimal commented 3 years ago

Godot version: 3.2.3-stable

OS/device including version: Linux

Issue description: Anytime I use VehicleWheel, it sinks into the ground as seen here: image

The wheel is made to fit the mesh like so: image

Steps to reproduce:

  1. Start Godot's truck town demo
  2. Select the truck to drive
  3. Observe it sink into the ground

Minimal reproduction project: Truck town has it all over: https://github.com/godotengine/godot-demo-projects/tree/master/3d/truck_town

MrMinimal commented 3 years ago

Once confirmed, worth to mention it in https://github.com/godotengine/godot/issues/45333

pouleyKetchoupp commented 3 years ago

Confirmed in 3.2.3 stable and 3.2.4 beta 6.

Bullet: wheels sink into the ground as described. Godot Physics: the truck doesn't move forward.

Zireael07 commented 3 years ago

Isn't limited to the truck, btw - a standard 4 wheel car also has the wheels partly sinking - I thought it was just a graphical glitch until I saw this report.

AgentSmith0 commented 2 years ago

I can confirm this issue on Godot 3.3.4. This especially happens when a vehicle collides very fast with a collision shape. For example an airplane: On ground the wheels are above the ground perfectly, but at landing (with fast movement) the wheels go below the ground on touch down (and stay there). is_in_contact() returns false and the aircraft sinks to ground so that only the collision body is visible.

AgentSmith0 commented 2 years ago

Also present in Godot 3.4.

qaptoR commented 2 years ago

So, I previously thought this was an issue with the engine in 3.4 (currently using), but I think it may be the documentation that is incorrect on how to use it, and the default values may just not be relevant to the example project mentioned.

The answer to this question( https://godotengine.org/qa/83746/confusion-about-vehiclebody ) helped me solve my issues with not understanding how the wheels actually worked, and the clipping I experienced went away.

They suggest watching this video (https://www.youtube.com/watch?v=B5vE-nNszxA) where the settings are explained a bit more in detail. In particular it's noted that the 'y-value' of the wheel transform should be equal to the 'rest_length' to compensate for the fact that the center of the wheel is actually where the spring mounts to the vehicle, so the visual representation of the spring being drawn upwards is somewhat confusing as rest_length increases.

some other things which were an issue was too low of settings for max force, and travel being way too high (should be equal or lower than rest_length). And increasing stiffness helped the movement feel more smooth.

I suggest the documentation be looked into being made clearer. When I have time I'll try to look into how I might contribute to that, but if someone tackles it before me, grazie.

Zireael07 commented 2 years ago

Whoa, thanks for saying that y of wheel should be equal to rest_length - I was one of the people mislead by the visual spring...

MrMinimal commented 2 years ago

@qaptoR @Zireael07 I'm trying to replicate this but am not successful with it. Do you have an example project to share? Then I can apply the fix the Truck Town demo.

Zireael07 commented 2 years ago

I don't have a minimal project, unfortunately. I think the issue went away at some point, or became so unnoticeable that I just don't notice it

LinuxUserGD commented 2 years ago

Also present in Godot 3.4.

Also present in Godot 4.0.alpha.custom_build.950750fb9 (https://github.com/godotengine/godot-demo-projects/tree/5566c748c210e9ca47baa77b7bdc9ba95e7b2f58/3d/truck_town)

akien-mga commented 1 year ago

Another more minimal MRP from #65739:

WheelTest.zip

Skaruts commented 1 year ago

Makes me wonder:

If the rest_length and the wheel's y value should always be equal, then what's the point of having to set both? It's unwieldy and error prone. Either the implementation should infer the rest_length from the y, or the rest_length should work on its own regardless of the y.

Unless I'm missing something...

(EDIT: actually it seems to me that you can never have raised suspensions if you make rest_length equal to y. I tested it in Godot 4, though.)

Also, according to @BastiaanOlij, the suspension travel shouldn't be higher than the rest_length, so wouldn't it be easier to work with if the travel was a percentage instead?

As it is, changing the rest_length always requires that you also change two other values, which isn't ideal, in my opinion.

betalars commented 6 months ago

As I had the same Issue, I looked into the Code. It seems like the spring length is being calculated incorrectly. I made an own issue, as I originally thought this was just a visual glitch.

betalars commented 6 months ago

I just noticed the already existing fix for this. Please merge.

rburing commented 6 months ago

The original btRaycastVehicle https://github.com/bulletphysics/bullet3/blob/6bb8d1123d8a55d407b19fd3357c724d0f5c9d3c/src/BulletDynamics/Vehicle/btRaycastVehicle.cpp#L160 from Bullet does a raycast from the wheel's world position down to a point of distance raylen = suspension_rest_length + wheel_radius below the wheel. When the ray hits a point at distance hit_distance from its starting point, it makes sense to set suspension_length = hit_distance - wheel_radius. The PR https://github.com/godotengine/godot/pull/47412 restores that behavior from Bullet. It also simplifies the calculation of hit_distance and makes it a bit more precise, under the restriction in Godot that the raycast doesn't return the value of the line parameter param (the number between 0.0 and 1.0 that indicates where the raycast hit occurred), which was calculated manually instead. (In fact with this approach the whole param and depth variables can be removed and the method can be made to return void as the return value is never used.)

Now, the vehicle body code in Godot was changed 10 years ago in 1a2cb75 (around Godot 1.0) to move the starting point of the raycast up to a point of distance wheel_radius above the wheel (and keeping the endpoint the same as before, so increasing the length of the ray). My guess is that this was done to fix issues like what @Calinou reports in https://github.com/godotengine/godot/pull/47412#pullrequestreview-1910032892. The issue with that fix was that suspension_length is then computed incorrectly, as the meaning of hit_distance changed. To keep the hit_distance relative to the wheel's world position, it should be e.g. (as the only change on top of master):

diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp
index c23032d3b9..36e428c648 100644
--- a/scene/3d/vehicle_body_3d.cpp
+++ b/scene/3d/vehicle_body_3d.cpp
@@ -425,7 +425,7 @@ real_t VehicleBody3D::_ray_cast(int p_idx, PhysicsDirectBodyState3D *s) {
                        wheel.m_raycastInfo.m_groundObject = Object::cast_to<PhysicsBody3D>(rr.collider);
                }

-               real_t hitDistance = param * raylen;
+               real_t hitDistance = param * (raylen + wheel.m_wheelRadius) - wheel.m_wheelRadius;
                wheel.m_raycastInfo.m_suspensionLength = hitDistance - wheel.m_wheelRadius;
                //clamp on max suspension travel

The difference is param * wheel_radius - wheel_radius = (param - 1.0) * wheel_radius. This is small when param is close to 1.0 (which was also made more likely by moving the ray up and increasing its length), but often visibly nonzero as in this issue. The patch above combines "move the ray up" (already in master) and "calculate the hit distance correctly", whereas https://github.com/godotengine/godot/pull/47412 undoes "move the ray up" but does "calculate the hit distance correctly".

The problem (probably) with the above patch is that hit_distance can be negative, which may cause weird physics behavior (maybe worth testing).

To resolve this issue I think somebody should look at how some popular implementation of a car with raycasts solves this problem, and implement it in Godot.

Zireael07 commented 6 months ago

how some popular implementation of a car with raycasts solves this problem, and implement it in Godot.

Other than Bullet, I think you mean, as Godot's implementation is pretty much Bullet's...

Calinou commented 6 months ago

Jolt has a pretty complete vehicle implementation which also has more features (such as gears and RPM), but it'll need to be moved to core so it can be plugged in VehicleBody/VehicleWheel effectively. We could technically have properties that have no effect when using GodotPhysics, but it's not ideal to have properties that have no effect until you install an extension (e.g. VideoStreamPlayer seeking).