How to check if rigid body is on floor

Godot Version

godot4

Question

how to check if rigid body is on floor is it possible?!?

pls anybody

You will need contact monitoring on and at least 1 max contacts reported. The most accurate way would be to use _integrate_forces and iterate over contacts looking for a “up” facing normal vector.

func _integrate_forces(state: PhysicsDirectBodyState3D) -> void:
	var on_floor: bool = false
	var i := 0
	while i < state.get_contact_count():
		var normal := state.get_contact_local_normal(i)
		on_floor = normal.dot(Vector3.UP) > 0.99 # this can be dialed in
		#  1.0 would be perfectly straight up
		#  0.0 is a wall
		# -1.0 is a ceiling
		i += 1

A less accurate way would be using on_body_entered signal and check if the other body is lower on the y axis.

im still kinda new. what is contact monitoring

a property of rigid bodies

2024-06-07-161417_396x326_scrot

ok. now would the function you made above return anything? how would i use it in the context of

if on_floor:
jump

The function above is an override of RigidBody3D’s _integrate_forces it cannot return anything. Data to be shared from this function needs to be stored elswhere, like a global variable. Also, multiple collisions might not be on the floor so some extra logic to merge per-frame collisions should help.

var on_floor: bool = false # now global!

func _integrate_forces(state: PhysicsDirectBodyState3D) -> void:
	on_floor = false # reset on_floor for this physics frame

	var i := 0
	while i < state.get_contact_count():
		var normal := state.get_contact_local_normal(i)
		var this_contact_on_floor = normal.dot(Vector3.UP) > 0.99

		# boolean math, will stay true if any one contact is on floor
		on_floor = on_floor or this_contact_on_floor
		i += 1

im getting this error

Parser Error: The function signature doesn’t match the parent. Parent signature is “_integrate_forces(PhysicsDirectBodyState2D) → void”.

nvrm just had to change from Vector3 to Vector2 also changed PhysicsDirectBodyState3D to PhysicsDirectBodyState2D. how do i call this function now

1 Like

it will be called automatically every physics frame. much like how func _process is called every frame automatically.

i did this and it only says false

func _physics_process(delta):
	print(on_floor)

i know that the function is running, the function just isnt working

Might be too accurate/particular 0.99 is little tolerance, might be running the function too often for non-physics processes to catch it. To try an deduce which let’s use the last floor touched time instead

var on_floor_time: int = 0 # now numeral!

func _integrate_forces(state: PhysicsDirectBodyState3D) -> void:
   # we do not reset time

   var i := 0
   while i < state.get_contact_count():
   	var normal := state.get_contact_local_normal(i)
   	var this_contact_on_floor = normal.dot(Vector3.UP) > 0.99

   	if this_contact_on_floor:
   		on_floor_time = Time.get_ticks_msec()
   		break
   	i += 1

Now printing the on_floor_time should show a number other than zero at some point, otherwise our 0.99 really is a stickler and should be reduced. Try some incrementally lower numbers until it feels right.

now its just giving me 0 i tried changinmg the number but it didnt work

Strange, my minimal tests are working even with 0.99 (thinking about it more > 0.0 would be prefered aka “not a wall”). Are you sure contact monitoring is on and there is at least 1 max reportable contact?

that was the problem. i turned on contact monitoring and 1 max reportable contact (didnt work) then i turned on cast shape and now it works. thanks

is there a way to change the tolerence? example: if rigid body is like 10 pixels away from ground it still becomes true

I would say shrink the collision shape, a higher physics tick rate might also help but you would have to take the time since last floor hit approach.