If CharacterBody2D is already touching the floor by safe_margin after move_and_slide() is called, collision won't be reported until next call to move_and_slide().
It leads to very counterintuitive behavior. Imagine you give player a dash ability but it can be done only while not on floor. Dash sets vertical speed and gravity to 0. With a frame-perfect input player can dash while actually already touching the floor. is_on_floor() returns false so dash is possible but in the next frame magically is_on_floor() reports true even though there was no gravity nor vertical velocity so there is no reason to suddenly collide with the floor.
Steps to reproduce
Create some StaticBody2D and above it place a CharacterBody2D with following script:
extends CharacterBody2D
var touched = false
func _ready():
velocity.y = 8
func _physics_process(delta):
if touched:
return
var diff = velocity * delta
var expected_pos = position + diff
prints("BEFORE:", "floor:", is_on_floor(), "position:", position, "expected_pos:", expected_pos, "velocity:", velocity)
touched = move_and_slide()
prints(touched)
prints("AFTER:", "floor:", is_on_floor(), "position:", position, "expected_pos:", expected_pos, "velocity:", velocity)
At the very end of output you can see something like:
The one before last move_and_slide() call moves the body so it is already closer than safe_margin to StaticBody but the collision was not reported until the very last call to move_and_slide(). Last call does not move the body but actually reports the collision.
Tested versions
v4.2.1.stable.official.b09f793f5
System information
Godot v4.2.1.stable - Ubuntu 22.04.4 LTS 22.04 - X11 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 4080 () - AMD Ryzen 7 7800X3D 8-Core Processor (16 Threads)
Issue description
If
CharacterBody2D
is already touching the floor bysafe_margin
aftermove_and_slide()
is called, collision won't be reported until next call tomove_and_slide()
.It leads to very counterintuitive behavior. Imagine you give player a dash ability but it can be done only while not on floor. Dash sets vertical speed and gravity to 0. With a frame-perfect input player can dash while actually already touching the floor.
is_on_floor()
returnsfalse
so dash is possible but in the next frame magicallyis_on_floor()
reports true even though there was no gravity nor vertical velocity so there is no reason to suddenly collide with the floor.Steps to reproduce
Create some
StaticBody2D
and above it place aCharacterBody2D
with following script:At the very end of output you can see something like:
The one before last
move_and_slide()
call moves the body so it is already closer thansafe_margin
toStaticBody
but the collision was not reported until the very last call tomove_and_slide()
. Last call does not move the body but actually reports the collision.Minimal reproduction project (MRP)
example.zip