is_on_floor() works unreliably - am I doing something wrong? (Godot 3.2.beta)

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By unfa

I’m making a 3D platforming game.

The player character is a Kinematic Body.

I used the Kinematic Body’s is_on_floor() method to determine if the player is in contact with the ground while attempting to jump, or driving the state machine for playing animations.

I am constantly checking if the player is on the ground, and if not - I apply gravitational acceleration. However for whatever reason a toggle behaviour emerged where the player would alternate between -50 and 0 Y velocity, making him jitter on the ground and also loop in a landing animation, as the state machine things he’s constantly landing from a jump. This breaks the animations, but what’s worse - it makes jumping fail half of the times (if you happen to be pressing the jump button on a frame that the is_on_floor() gives false for.

I’ve tried to workaround the is_on_foor() method by using a raycast:

If it hits anything - the player is on the ground.
If it hits something and the hit normal is (0, 1, 0) - that means the player is standing on a perfectly flat surface, so there should be no gravitational pull applied.

Without that check the player is twitching while the Kinematic Body is constantly pushing into the ground, but also - if the player walks of a ledge, he’s got immediate downward velocity which is incorrect, and makes the player fall down much too fast.

Now, making this hack causes another twitches. So it barely solves one thing and break another one.

I’ve tried to aid the whole thing by adding another Area with a collider extending around the player’s feet - to detect if he’s in contact with the ground with a bit more lee-way.

I’ve set the collision masks so the Area is ignored by the Kinematic Body -however for some reason the player can’t jump at all and gets stuck above the ground like he’s standing on that extra Area.

I can’t seem to find a solution to this basic problem of player movement in a 3D space.

This is all because is_on_floor() seems to return true or false when the player rests on the ground, without apparent reason to failure.

Are there any known solutions to this problem?

:bust_in_silhouette: Reply From: kidscancode

I am constantly checking if the player is on the ground, and if not - I apply gravitational acceleration.

You must always apply gravity. If not, then nothing is pulling the player “into” the floor so they’re not touching it. is_on_floor() is set by move_and_slide() when it hits a floor object.

See the many available examples:

Thank you! I’m going to analyze my code and the examples you’ve provided to fix the issue.

I am a bit worried that all of the examples use 2D instead of 3D - does that matter?

unfa | 2019-12-31 18:50

Most examples use 2D because it’s more widely applicable, and a bit easier to work with. The functionality of KinematicBody2D and KinematicBody are identical, so anything shown in those examples I linked can be applied in 3D.

kidscancode | 2019-12-31 18:54

:bust_in_silhouette: Reply From: luislodosm

An alternative is using a RayCast2D node parented to the RigidBody2D / KinematicBody2D node with the parameter “exclude_parent” set on true.

var ray: RayCast2D = get_node("path")
if ray.is_colliding():
   #jump