All lessons
Lesson 20

Jump Feel

Same jump as lesson 2, but it stops feeling broken. Coyote time, jump buffering and variable height. Flip them off to feel why they exist.

Loading the game engine. This takes a moment the first time.

Platformer only

THIS LESSON ADDS NO NEW FEATURES. IT ONLY CHANGES HOW THE JUMP FEELS.

Every other lesson gives you something you could not do before. This one takes a jump you already have and makes it stop feeling broken. That is most of what game "polish" actually is, and it is worth knowing that the difference between a game that feels cheap and one that feels good is usually about fifteen lines like these.

THE THREE FIXES, AND THE COMPLAINT EACH ONE ANSWERS

"I pressed jump but nothing happened!" → COYOTE TIME

You ran off the ledge and pressed jump a few hundredths of a second later. The strict rule says you must be touching the floor, and you were not any more. Coyote time keeps the jump available for a moment after you leave the ground. Players never notice it is there. They only notice when it is missing.

"I pressed jump the instant I landed and it ignored me!" → JUMP BUFFER

You pressed just BEFORE touching down. Same rule, opposite side. Buffering remembers the press for a moment and fires it on landing.

"Every jump is the same giant leap." → VARIABLE HEIGHT

Holding and tapping did the same thing, so you could never make a small hop. Letting go early now cuts the jump short.

HOW TO ACTUALLY FEEL IT

Press the button to turn forgiveness OFF, then try the course below. The two brown platforms with the gap are built to catch you out: run off the left one and jump late. With forgiveness ON you make it. With it OFF you drop in the pit, and it feels like the game ignored you, even though the game did exactly what you told it to.

The scenes

UI node · 2D node · % unique name · .tscn instanced scene

jump_feel.tscn
  • JumpFeel Node2D jump_feel.gd
  • Floor StaticBody2D
  • CollisionShape2D
  • Visual ColorRect
  • LeftWall StaticBody2D
  • CollisionShape2D
  • RightWall StaticBody2D
  • CollisionShape2D
  • Step StaticBody2D
  • CollisionShape2D
  • Visual ColorRect
  • LeftPlatform StaticBody2D
  • CollisionShape2D
  • Visual ColorRect
  • RightPlatform StaticBody2D
  • CollisionShape2D
  • Visual ColorRect
  • TallPlatform StaticBody2D
  • CollisionShape2D
  • Visual ColorRect
  • JumpPlayer CharacterBody2D % player.tscn
  • HUD CanvasLayer % hud.tscn
hud.tscn
  • HUD CanvasLayer hud.gd
  • MarginContainer
  • Rows VBoxContainer
  • ForgivingButton Button %
  • ExplainLabel Label %
  • HintLabel Label
  • HomeButton Button %
player.tscn
  • JumpPlayer CharacterBody2D player.gd
  • Sprite2D
  • CollisionShape2D
extends Node2D

@onready var player: CharacterBody2D = %JumpPlayer

func _ready() -> void:
	%HUD.forgiving_toggled.connect(_on_forgiving_toggled)
	# The player starts forgiving. The HUD's button label starts matching it.
	player.forgiving = true

func _on_forgiving_toggled(enabled: bool) -> void:
	player.forgiving = enabled