Just lost this whole post because I ctrl+a ctrl+v to copy over my code into the box and it deleted everything. I will try and write it out again to the best of my ability.
This is my first ever project. I successfully followed Brackeys âHow to make a Video Game - Godot Beginner Tutorialâ and have been trying to add a coyote time feature for which I have been following the kidscancode tutorial here: Coyote Time :: Godot 4 Recipes
I feel bad asking for help especially as I am following some of the most popular, and basic, Godot tutorials out there but I am at my wits end when it comes to understanding what is causing the coyote time to never turn off here. I am pretty sure thatâs the issue as the game behaves normally until I walk off of a platform at which point I may jump infinitely.
I have been searching for an answer, looking at other tutorials and comparing my project to the â2d_moving_platformsâ project supplied by kidscancode for literal hours and I donât feel like Iâm making any progress. If this was something I needed to just get done then I would scrap it all and start again but crucially I need to understand why this is happening and Iâm not seeing what would cause this.
The main discrepancy I can see is that I have an actual timer node with One Shot enabled whereas the kidscancode project doesnât visibly have an actual timer node but I donât understand how or why that is the case because the CoyoteTimer is referenced in their code.
This will presumably be some of the ugliest, worst formatted code youâll ever see - at this point itâs ideas mashed up from at least three different sources but I feel like I must be missing something fundamental/obvious to people who actually know what theyâre doing.
extends CharacterBody2D
const SPEED = 130.0
const JUMP_VELOCITY = -300.0
# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
var coyote_frames = 6 # How many in-air frames to allow jumping
var coyote = false # Track whether we're in coyote time or not
var last_floor = false # Last frame's on-floor state
var jumping = false
@onready var coyote_timer = $CoyoteTimer
@onready var animated_sprite = $AnimatedSprite2D
func _ready():
$CoyoteTimer.wait_time = coyote_frames / 60.0
func get_input(delta):
# Handle Jump.
if Input.is_action_just_pressed("Jump") and (is_on_floor() or coyote):
velocity.y = JUMP_VELOCITY
jumping = true
func _physics_process(delta):
# Add the gravity.
if not is_on_floor():
velocity.y += gravity * delta
# Get input direction: -1, 0, 1
var direction = Input.get_axis("Move Left", "Move Right")
#Flip da sprite
if direction > 0:
animated_sprite.flip_h = false
elif direction < 0:
animated_sprite.flip_h = true
#Play Animations
if is_on_floor():
if direction == 0:
animated_sprite.play("Idle")
else:
animated_sprite.play("Run")
else:
animated_sprite.play("Jump")
#Apply Movement
if direction:
velocity.x = direction * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
get_input(delta)
move_and_slide()
if is_on_floor() and jumping:
jumping = false
if !is_on_floor() and last_floor and !jumping:
coyote = true
$CoyoteTimer.start()
last_floor = is_on_floor()
func _on_coyote_timer_timeout():
coyote = false
Try setting coyote to false right after the check. It doesnt matter if it is already false. Those times it is true are the ones you want to catch. No need to introduce an extra if instead of just setting it to false for every case.
So what weâve done here is under the _ready function we have created a connection to the CoyoteTimer node that is looking for the timeout signal? Does the name in the brackets define what we need to type to reference this connection later in the code? I apologise if connection is the wrong word to use here.
I am confused as to why there doesnât seem to need to be anything like that in the kidscancode â2d_moving_platformsâ project
Iâll put it here in case anyone would like to explain that to me but I am not asking you to do this and I really appreciate the help!
extends CharacterBody2D
@export var speed = 150
@export var jump_speed = -350
@export var acceleration = 15
@export var friction = 16
var emote_textures = {
"skull": Vector2i(20, 27),
"surprise": Vector2i(20, 25),
"question": Vector2i(22, 25)
}
var coyote_frames = 6
var coyote = false
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
var last_floor = false
var jumping = false
var dead = false
func _ready():
$Emote.hide()
$CoyoteTimer.wait_time = coyote_frames / 60.0
func get_input(delta):
var direction = Input.get_axis("move_left", "move_right")
if direction:
velocity.x = lerp(velocity.x, direction * speed, acceleration * delta)
$AnimatedSprite2d.play("walk")
else:
velocity.x = lerp(velocity.x, 0.0, friction * delta)
$AnimatedSprite2d.play("idle")
if jumping:
if velocity.y < 0:
$AnimatedSprite2d.play("jump_up")
elif velocity.y > 0:
$AnimatedSprite2d.play("jump_down")
if Input.is_action_just_pressed("jump") and (is_on_floor() or coyote):
velocity.y = jump_speed
jumping = true
func _physics_process(delta):
velocity.y += gravity * delta
if dead:
$AnimatedSprite2d.play("dead")
$Emote.show()
return
get_input(delta)
move_and_slide()
for i in get_slide_collision_count():
var c = get_slide_collision(i).get_collider()
var n = get_slide_collision(i).get_normal()
if c.is_in_group("pushable"):
c.apply_central_impulse(-n * 5)
# c.set_axis_velocity(-n * 10)
if is_on_floor() and jumping:
jumping = false
if !is_on_floor() and last_floor and !jumping:
coyote = true
$CoyoteTimer.start()
if velocity.x > 0:
$AnimatedSprite2d.flip_h = false
if velocity.x < 0:
$AnimatedSprite2d.flip_h = true
last_floor = is_on_floor()
func _on_coyote_timer_timeout():
coyote = false
This was a useful change to make, or at least not a harmful change to make, and was helpful for testing as it allowed me to set my coyote time to be very late without being able to double jump.
By itself however all this did was ensure I could only jump once after walking off of a ledge - the coyote state was still never turning off due to timeout iyswim
P.S. Canât believe the Baba wanted to help little old me
The official documentation have explained it the best
The line reads like so: we connect the Timer's "timeout" signal to the node to which the script is attached. When the Timer emits timeout, we want to call the function _on_timer_timeout(), that we need to define.