Game Feel · Blog
Mains tenant une manette de jeu, doigts sur les boutons
Photo: Kevin Malik / Pexels
Sommaire7
  1. Pourquoi ça marche
  2. La recette
  3. Le code
  4. Réglages et erreurs fréquentes
  5. Exemples célèbres
  6. Tableau de référence
  7. FAQ

Votre saut semble cassé parce que la physique de Godot ne pardonne pas les erreurs de timing — ces deux mécaniques règlent ça en 20 lignes de GDScript.

100 mscoyote time de Celeste (6 frames)feelframes.dev, 2024
67 msjump buffer de Celeste (4 frames)feelframes.dev, 2024
8 framescoyote time de Mario Odysseyfeelframes.dev, 2024

Pourquoi ça marche

Le temps de réaction humain à un signal visuel est d'environ 150–200 ms. À 60 fps, le bord d'une plateforme ne dure qu'un pixel — personne ne peut timer ça de façon fiable. Les mécaniques de pardon absorbent l'écart entre l'intention et l'exécution.

  • Le coyote time rattrape les appuis trop tardifs — le joueur a sauté juste après le bord.
  • Le jump buffer rattrape les appuis trop précoces — le joueur a sauté juste avant d'atterrir.

Ensemble, ils donnent l'impression au joueur d'être compétent, pas chanceux.

Niveau de platformer en pixel art avec des plateformes flottantes
Niveau de platformer en pixel art avec des plateformes flottantes — Photo: Steve A Johnson / Pexels

La recette

ParamètreValeur de départPlage pratiqueCe que ça change
coyote_time0.10 s (6 frames)0.06–0.15 sFenêtre de grâce après le bord
jump_buffer_time0.08 s (5 frames)0.05–0.12 sAcceptation de l'appui anticipé

Commencez avec ces valeurs exactes. Les jeux de précision descendent en dessous ; les jeux casual ou mobiles montent. Ajustez par paliers de 10 ms et testez à chaque changement.

Manette de jeu avec boutons colorés en gros plan
Manette de jeu avec boutons colorés en gros plan — Photo: Ömer Yılmaz / Pexels

Le code

À attacher à votre CharacterBody2D. Intégrez-le dans votre _physics_process existant, ou utilisez-le tel quel comme point de départ.

extends CharacterBody2D

const GRAVITY: float = 980.0
const JUMP_VELOCITY: float = -400.0
const SPEED: float = 200.0

@export var coyote_time: float = 0.10       # secondes — Celeste utilise 0.10 s
@export var jump_buffer_time: float = 0.08  # secondes — légèrement inférieur au coyote

var _coyote_timer: float = 0.0
var _jump_buffer_timer: float = 0.0

func _physics_process(delta: float) -> void:
    # 1. Gravité — appliquée uniquement en vol
    if not is_on_floor():
        velocity.y += GRAVITY * delta

    # 2. Timer coyote — se remet à zéro au sol, décompte dans les airs
    if is_on_floor():
        _coyote_timer = coyote_time
    else:
        _coyote_timer -= delta

    # 3. Buffer de saut — enregistre un appui saut pendant une courte fenêtre
    if Input.is_action_just_pressed("ui_accept"):
        _jump_buffer_timer = jump_buffer_time
    else:
        _jump_buffer_timer -= delta

    # 4. Saut — se déclenche si le buffer a un appui en attente ET que le coyote est valide
    if _jump_buffer_timer > 0.0 and _coyote_timer > 0.0:
        velocity.y = JUMP_VELOCITY
        _coyote_timer = 0.0       # consomme le coyote — évite le double saut aérien
        _jump_buffer_timer = 0.0  # consomme le buffer — évite un saut non voulu à l'atterrissage

    # 5. Déplacement horizontal
    var direction: float = Input.get_axis("ui_left", "ui_right")
    velocity.x = direction * SPEED

    move_and_slide()

L'astuce clé : comme _coyote_timer se remet à zéro à chaque frame au sol, la simple vérification _coyote_timer > 0.0 gère à la fois "actuellement au sol" et "vient de quitter le sol". Pas besoin d'un is_on_floor() séparé au moment du saut.

Développeur codant sur un écran montrant un éditeur de jeu
Développeur codant sur un écran montrant un éditeur de jeu — Photo: ThisIsEngineering / Pexels

Réglages et erreurs fréquentes

Conseils

  • Baissez coyote_time à 0.06 s pour les jeux de précision. Trop de grâce donne l'impression de marcher sur de l'air.
  • Gardez jump_buffer_time légèrement inférieur à coyote_time — ça semble plus intentionnel.
  • Exportez les deux variables pour les ajuster depuis l'Inspecteur sans recompiler.

Erreurs fréquentes

  • Ne pas remettre _coyote_timer à zéro après un saut → double saut depuis un bord de plateforme.
  • Ne pas remettre _jump_buffer_timer à zéro après un saut → l'appui en file se déclenche à l'atterrissage suivant, ce qui surprend le joueur.
  • Appeler is_on_floor() directement au moment du saut au lieu d'utiliser le timer → tout le pardon est perdu.
Croquis de game design et notes sur papier
Croquis de game design et notes sur papier — Photo: picjumbo.com / Pexels

Exemples célèbres

Maddy Thorson (co-créateur de Celeste) a publié un thread en mars 2020 listant les mécaniques de pardon cachées du jeu. Le coyote time est le point n°1 — JumpGraceTime = 0.1f dans le code source, mesuré exactement à 6 frames par feelframes.dev. Note de Thorson : "Je ne crois pas qu'on ait inventé ces mécaniques."

Fenêtre de coyote time par jeu (ms à 60 fps)
Mario Odyssey
ms133
Celeste
ms100
Hollow Knight
ms67
Super Meat Boy
ms50
feelframes.dev, 2024
Arcade rétro lumineuse dans une salle sombre
Arcade rétro lumineuse dans une salle sombre — Photo: Corey Dupree / Pexels
Fenêtre de jump buffer par jeu (ms à 60 fps)
Mario Odyssey
ms100
Celeste
ms67
Hollow Knight
ms50
feelframes.dev, 2024

Les 133 ms de Mario Odyssey s'accordent avec son design casual orienté exploration. Les 100 ms de Celeste sont délibérés — le jeu est difficile, mais la précision reste juste. Les ~50 ms de Super Meat Boy exigent une vraie maîtrise. Le devlog Behind the Code de GMTK inclut les deux mécaniques comme valeurs par défaut essentielles dans le Platformer Toolkit.

Développeur indépendant travaillant sur un laptop dans un studio sombre
Développeur indépendant travaillant sur un laptop dans un studio sombre — Photo: Cord Allman / Pexels

Tableau de référence

Style de jeuCoyote timeJump buffer
Précision (Super Meat Boy)0.06 s0.05 s
Action-platformer (Hollow Knight)0.10 s0.08 s
Casual / débutant (Mario)0.13 s0.10 s
Personnage de jeu vidéo sautant sur des plateformes en pixel art
Personnage de jeu vidéo sautant sur des plateformes en pixel art — Photo: Sami Aksu / Pexels

FAQ

Est-ce que le coyote time casse les wall jumps ou les double jumps ?

Ça peut. La correction est déjà dans le code ci-dessus : remettez _coyote_timer à zéro immédiatement après tout saut — au sol, sur un mur, ou double saut. Sans ça, un wall jump pourrait consommer le coyote time et accorder un saut supplémentaire.

Vaut-il mieux utiliser des nœuds Timer plutôt que des timers en float ?

Les timers en float dans _physics_process sont plus simples, déterministes et n'ajoutent aucun nœud à la scène. Utilisez des nœuds Timer uniquement si vous avez besoin de signaux (par exemple pour déclencher un son ou une animation à la fin exacte de la fenêtre de grâce).

Mon saut est encore trop flottant — quoi ajouter ensuite ?

Ajoutez la hauteur de saut variable : quand le joueur relâche le bouton de saut tôt, multipliez la gravité par 2.0–3.0 pour couper l'arc. Ça donne des pics de saut précis et contrôlables, et c'est l'étape naturelle suivante après le coyote time.

Est-ce compatible avec un state machine d'animation ?

Oui. Les timers sont purement de la logique de mouvement — ils ne touchent pas à l'animation. Connectez votre animation en vérifiant velocity.y et is_on_floor() dans une fonction séparée.

Ça fonctionne avec Godot 4.3 ou 4.4 ?

Oui. CharacterBody2D, is_on_floor() et move_and_slide() sont stables sur tout Godot 4.x. Aucune migration nécessaire.


Ajoutez le coyote time et le jump buffer une fois pour toutes et vous ne recevrez plus de retours "les contrôles ne répondent pas". Pour la boîte à outils complète — screen shake, hit stop, squash & stretch — consultez le Game Feel Cookbook.