is_on_floor is always true, even on midair

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

Hello! So I’m trying to implement cayote time for my player in my 2d platformer game. One of the requirements is to check if the player is in midair by checking it using !is_on_floor. However, regardless if the player is in the air or not, the boolean still returns true and it’s driving me nuts. I’ve already supplied the UP Vector for move_and_slide so in theory, it should detect it. Here is the code for Player.gd:

extends KinematicBody2D

var motion = Vector2.ZERO

export var ACCELERATION = 1750
const SPEED = 350
const MAXSPEED = 600
const MAX_FALL = 160
const GRAVITY = 2000
const JUMP_FORCE = -700

onready var spawnPoint = get_parent().get_node("SpawnPoint")
onready var currentSpawn = spawnPoint.position

# TODO: Make State Machines

var health : int = 3
var maxHealth : int = 3
var current_hold_time = 0
var jump_hold_time = 0.2
var hasHitSomething = false
var hasReachedEnd : bool = false
var canJumpMidAir : bool = false

# TODO
var isWaterLevel : bool = false

signal health_changed(player_hearts)
signal levelComplete()
signal died()

onready var animation = $Sprite

func _ready():
	connect("health_changed", get_parent().find_node("HUD"), "_on_health_changed")
	connect("levelComplete", get_parent().find_node("HUD"), "_on_level_complete")
	
	emit_signal("health_changed", health)
	
	Global.player = self
	position = spawnPoint.position
			
func _exit_tree():
	# I forgor what this does
	Global.player = null

func _physics_process(delta) -> void:
	var direction : int = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
	var jumping : bool = Input.is_action_pressed("jump")
	
	#Do physics while level hasn't finished yet
	if !Global.levelFinished:
		
		# Jumping
		if jumping && is_on_floor() && canJumpMidAir:
			$jump.play()
			motion.y = JUMP_FORCE
			current_hold_time = jump_hold_time
		elif !is_on_floor():
			enableCayoteTime()
		
		#while on air
		elif current_hold_time > 0:
			if jumping:
				# then keep jumping
				motion.y = JUMP_FORCE
			else:
				current_hold_time = 0
				
		# Subtract every frame
		current_hold_time -= delta
		
		if direction > 0:
			animation.flip_h = false
		elif direction < 0:
			animation.flip_h = true
		
		if is_on_floor():
			# set up cayote time
			canJumpMidAir = true
			
			if direction != 0:
				animation.play("walk")
			else:
				animation.play("idle")
		elif jumping or hasHitSomething:
			animation.play("jump")
			
		
		if direction != 0:
			motion.x += direction * ACCELERATION * delta
			motion.x = clamp(motion.x, -MAXSPEED, MAXSPEED)
		else:
			motion.x = lerp(motion.x, 0, 0.125)
		
		motion.y += GRAVITY * delta
		
		#2nd paramater is the up direction; you need it or otherwise is_on_floor method won't work.
		motion = move_and_slide(motion, Vector2.UP)
		
		if(position.y > 750) or health <= 0:
			$respawn.play()
			health = 3
			backToSpawn()
	else:
		# TODO -> if level finished, make him decelerate, play victory and jump a few times
		$Sprite.play("idle")
		motion.x = lerp(motion.x, 0, 0.05)
		motion.y += GRAVITY * delta
		
func enableCayoteTime() -> void:
    #currently activates every frame
	print("cayote time active")
	yield(get_tree().create_timer(.1), "timeout")
	canJumpMidAir = false
		
func bounce() -> void:
	hasHitSomething = true
	motion.y = JUMP_FORCE * .8
	hasHitSomething = false
	
func bounceDirection(direction) -> void:
	hasHitSomething = true
	motion.y = JUMP_FORCE * .90
	motion.x = direction * 700
	hasHitSomething = false
	
func flashWhenHit() -> void:
	modulate = Color(255, 145, 145)
	yield(get_tree().create_timer(.3), "timeout")
	modulate = Color(1, 1, 1)
	
func damage() -> void:
	if health != 0:
		$hurt.play()
		flashWhenHit()
		health -= 1
		emit_signal("health_changed", health)
	else:
		backToSpawn()
		
func backToSpawn() -> void:
	emit_signal("health_changed", health)
	position = currentSpawn
	
	motion.x = 0

func _on_checkpoint(newSpawnPoint) -> void:
	currentSpawn = newSpawnPoint

func _on_one_up() -> void:
	print(health)
	health += 1
	emit_signal("health_changed", health)
	$OneUp.play()

func levelComplete() -> void:
	emit_signal("levelComplete")
	for member in get_tree().get_nodes_in_group("music"):
		member.stop()	
	yield(get_tree().create_timer(.2), "timeout")
	$win.play()
	

Does anyone know what seems to be the problem? Thanks!

:bust_in_silhouette: Reply From: Gluon

Are you sure it is always showing as true or is it possible you are assuming that because enableCayoteTime() doesnt get activated?

You have the following code

elif !is_on_floor():
    enableCayoteTime()

and I think you need to change this too

    elif not is_on_floor():
        enableCayoteTime()

I’ve put a print statement inside cayote time and indeed, I can confirm that it’s active as it prints uninterruptedly when the game starts. Also is there any difference between not and “!”? They seem all same to me

Napstaguy04 | 2021-12-26 04:39

Both “not” and “!” mean not but my understanding is they apply in different circumstances. ! applies to applications of equals for example you could write

elif is_on_floor() != True:

which would mean the same thing as

elif not is_on_floor():

When you write

elif !is_on_floor(): 

the system isnt looking for the same string, it applies the ! to the rest of the string so it is looking for a function called

!is_on_floor 

rather than

is_on_floor

does that make sense?

Gluon | 2021-12-26 11:49

Thanks for the explanation! I’ll take note of this. Also figured out the character starts midair which explains the print logs, and realized it wasn’t endless when I pressed jump and saw the scroll bar only shrinking during the jump duration. It’s likely the fault of how the cayote time function is set up, but isn’t part of my post’s question so I guess I’ll pick your explanation for now

Napstaguy04 | 2021-12-26 13:10