← Retour au sommaire
🎮

Canvas & Jeu vidéo —
L'algo derrière les jeux

Découvre comment les jeux vidéo fonctionnent avec Canvas, la game loop et les notions d'algorithmique clés.

Intermédiaire Canvas 2D Démos interactives
🖼️
C'est quoi le Canvas ?

L'élément HTML <canvas> est une zone de dessin dans laquelle JavaScript peut dessiner en temps réel — des formes, des images, du texte, des animations… C'est la base de la plupart des jeux 2D sur le web !

<!-- Dans le HTML --> <canvas id="monCanvas" width="600" height="400"></canvas> // Dans script.js — Initialiser le canvas const canvas = document.getElementById('monCanvas'); const ctx = canvas.getContext('2d'); // Dessiner un rectangle ctx.fillStyle = '#EAB308'; ctx.fillRect(50, 50, 100, 80); // x, y, largeur, hauteur // Dessiner un cercle ctx.fillStyle = '#EF4444'; ctx.beginPath(); ctx.arc(200, 100, 30, 0, Math.PI * 2); // x, y, rayon, 0, 2π ctx.fill(); // Écrire du texte ctx.fillStyle = '#fff'; ctx.font = '24px Nunito'; ctx.fillText('Score: 0', 20, 30);
🎨 Canvas — Dessin de base
💡Le système de coordonnées du canvas : x=0, y=0 est en haut à gauche. x augmente vers la droite, y augmente vers le bas. C'est l'inverse du repère mathématique !
🔄
La Game Loop — Le cœur du jeu

La game loop (boucle de jeu) est LA notion algorithmique fondamentale de tout jeu vidéo. C'est une boucle infinie qui s'exécute ~60 fois par seconde et qui fait toujours la même chose :

1. Lire les entrées
2. Mettre à jour l'état
3. Dessiner
↻ Recommencer
Clavier/souris → Déplacer les objets, vérifier collisions → Effacer + redessiner → 60 fois/seconde
// requestAnimationFrame : la fonction magique // Elle appelle ta fonction avant chaque affichage (~60fps) function gameLoop() { // 1. EFFACER le canvas (sinon les anciens dessins restent !) ctx.clearRect(0, 0, canvas.width, canvas.height); // 2. METTRE À JOUR : déplacer les objets mettreAJour(); // 3. DESSINER : afficher tous les objets dessiner(); // 4. RAPPELER la game loop à la prochaine frame requestAnimationFrame(gameLoop); } // Démarrer la boucle requestAnimationFrame(gameLoop);
60 FPS
La game loop tourne ~60 fois par seconde. Chaque tour = 1 frame. C'est pour ça que les mouvements paraissent fluides.
clearRect
On DOIT effacer le canvas à chaque frame. Sinon l'objet laisse une traînée à chaque déplacement — comme une peinture humide qu'on aurait frottée.
🔄 Game Loop — Balle en mouvement
📐
Coordonnées & Mouvement — Les variables de position

Tout objet dans un jeu a une position (x, y) et une vitesse (vx, vy). À chaque frame, on ajoute la vitesse à la position. C'est aussi simple que ça !

// Un objet "joueur" avec sa position et vitesse const joueur = { x: 100, // position horizontale (px depuis la gauche) y: 200, // position verticale (px depuis le haut) vx: 3, // vitesse horizontale (px par frame) vy: 0, // vitesse verticale largeur: 40, hauteur: 40 }; function mettreAJour() { // Mouvement : position += vitesse (à chaque frame) joueur.x += joueur.vx; joueur.y += joueur.vy; // Rebondir sur les bords gauche/droite if (joueur.x < 0 || joueur.x + joueur.largeur > canvas.width) { joueur.vx *= -1; // inverser la direction ! } }
La physique du mouvement en résumé
x += vx → déplace horizontalement à chaque frame
y += vy → déplace verticalement à chaque frame
vy += gravité → accélère vers le bas (gravité !)
vx *= -1 → inverse le sens (rebond sur un mur)
💥
Détection de collision — L'algo AABB

La collision est la détection de quand deux objets se touchent. L'algorithme le plus utilisé en jeu 2D s'appelle AABB (Axis-Aligned Bounding Box = "boîte rectangulaire alignée"). C'est un test logique simple !

Deux rectangles A et B se touchent si ET SEULEMENT SI toutes ces conditions sont vraies :
A.gauche < B.droite
A.droite > B.gauche
A.haut < B.bas
A.bas > B.haut
// Fonction de détection de collision AABB function collision(a, b) { return ( a.x < b.x + b.largeur // gauche de A < droite de B && a.x + a.largeur > b.x // droite de A > gauche de B && a.y < b.y + b.hauteur // haut de A < bas de B && a.y + a.hauteur > b.y // bas de A > haut de B ); } // Utilisation dans la game loop const joueur = { x: 100, y: 200, largeur: 40, hauteur: 40 }; const ennemi = { x: 200, y: 190, largeur: 50, hauteur: 50 }; const piece = { x: 300, y: 195, largeur: 20, hauteur: 20 }; if (collision(joueur, ennemi)) { vies--; console.log("Touché ! Vies restantes : " + vies); } if (collision(joueur, piece)) { score += 10; // "ramasser" la pièce : la déplacer hors écran piece.x = -100; }
💥 Collision AABB — Bouge la balle avec les touches ← →

← → pour déplacer la balle. Elle devient rouge si collision !

⬇️
Gravité & Saut — Simuler la physique

Dans un jeu de plateforme, la gravité s'applique à chaque frame en ajoutant une valeur à la vitesse verticale. Le saut, c'est juste donner une grande vitesse vers le haut d'un coup !

// Constantes physiques const GRAVITE = 0.5; // px/frame² ajoutés à chaque frame const FORCE_SAUT = -12; // négatif = vers le haut const SOL = 350; // hauteur du sol en pixels let joueur = { x: 100, y: 300, vy: 0, auSol: false }; function mettreAJour() { // Appliquer la gravité : accélère le joueur vers le bas joueur.vy += GRAVITE; // Appliquer le mouvement vertical joueur.y += joueur.vy; // Collision avec le sol : empêcher de passer à travers if (joueur.y >= SOL) { joueur.y = SOL; joueur.vy = 0; // stopper la chute joueur.auSol = true; } } // Saut : seulement si le joueur est sur le sol document.addEventListener('keydown', function(e) { if (e.key === ' ' && joueur.auSol) { joueur.vy = FORCE_SAUT; // impulsion vers le haut joueur.auSol = false; } });
⬇️ Gravité & Saut — Appuie sur Espace ou clique "Sauter"
⌨️
Gestion des entrées clavier — La bonne façon

Pour un jeu fluide, on ne doit pas déplacer directement le joueur dans l'événement keydown. On mémorise quelles touches sont enfoncées dans un objet, et on applique les déplacements dans la game loop.

// ❌ Mauvaise approche : déplacement direct dans keydown // → Mouvement saccadé à cause du délai de répétition OS document.addEventListener('keydown', (e) => { if (e.key === 'ArrowRight') joueur.x += 5; // saccadé ! }); // ✅ Bonne approche : mémoriser les touches enfoncées const touches = {}; // objet qui stocke l'état de chaque touche document.addEventListener('keydown', (e) => { touches[e.key] = true; }); document.addEventListener('keyup', (e) => { touches[e.key] = false; }); // Dans la game loop : lire l'état des touches à chaque frame function gererEntrees() { if (touches['ArrowLeft']) joueur.x -= 5; // ← mouvement fluide ! if (touches['ArrowRight']) joueur.x += 5; if (touches['ArrowUp']) joueur.y -= 5; if (touches['ArrowDown']) joueur.y += 5; }
🏆
Tableau de scores — Tableaux & tri

En jeu vidéo, on gère souvent des listes d'objets — ennemis, obstacles, projectiles… On utilise des tableaux JS et des algorithmes pour les filtrer, les trier et les parcourir.

// Gérer une liste d'ennemis let ennemis = [ { x: 100, y: 50, vie: 3, largeur: 40, hauteur: 40 }, { x: 250, y: 80, vie: 1, largeur: 40, hauteur: 40 }, { x: 400, y: 30, vie: 2, largeur: 40, hauteur: 40 }, ]; // Dessiner TOUS les ennemis avec forEach ennemis.forEach((ennemi) => { ctx.fillStyle = '#EF4444'; ctx.fillRect(ennemi.x, ennemi.y, ennemi.largeur, ennemi.hauteur); }); // Supprimer les ennemis morts (vie <= 0) // filter() garde seulement ceux qui satisfont la condition ennemis = ennemis.filter((e) => e.vie > 0); // Trier le tableau de scores (du plus grand au plus petit) const scores = [ { nom: "Alice", points: 980 }, { nom: "Bob", points: 1250 }, { nom: "Carla", points: 760 }, ]; scores.sort((a, b) => b.points - a.points); // → [Bob 1250, Alice 980, Carla 760] // Ajouter un nouvel ennemi au tableau ennemis.push({ x: 500, y: 60, vie: 2, largeur: 40, hauteur: 40 });
⏱️
Delta Time — Mouvement indépendant du FPS

Si un jeu tourne à 30fps sur une vieille machine et 60fps sur une nouvelle, les objets bougeront à des vitesses différentes sans deltaTime. Le deltaTime (temps écoulé depuis la dernière frame) permet un mouvement identique sur tous les appareils.

let dernierTemps = 0; function gameLoop(timestamp) { // timestamp = temps écoulé depuis le début (en ms) const dt = (timestamp - dernierTemps) / 1000; // en secondes dernierTemps = timestamp; // Vitesse en pixels PAR SECONDE (pas par frame) const VITESSE = 200; // 200px/seconde, identique à 30fps ou 60fps // Déplacement proportionnel au temps écoulé joueur.x += VITESSE * dt; // ~3.3px à 60fps, ~6.6px à 30fps ctx.clearRect(0, 0, canvas.width, canvas.height); dessiner(); requestAnimationFrame(gameLoop); } requestAnimationFrame(gameLoop);
💡Pour les projets débutants, le deltaTime n'est pas obligatoire. Mais si tu veux que ton jeu soit équitable sur tous les appareils, c'est une bonne pratique à adopter dès le départ.
🏗️
Architecture d'un mini-jeu complet

Voici comment organiser le code d'un vrai mini-jeu avec toutes les notions vues :

// === CONFIGURATION === const canvas = document.getElementById('jeu'); const ctx = canvas.getContext('2d'); // === ÉTAT DU JEU === let score = 0; let vies = 3; let enJeu = true; const joueur = { x: 50, y: 200, vx: 0, vy: 0, w: 40, h: 40 }; let ennemis = []; const touches = {}; // === ENTRÉES === document.addEventListener('keydown', (e) => touches[e.key] = true); document.addEventListener('keyup', (e) => touches[e.key] = false); // === MISE À JOUR === function update() { if (!enJeu) return; // 1. Mouvements joueur if (touches['ArrowLeft']) joueur.x -= 4; if (touches['ArrowRight']) joueur.x += 4; // 2. Mouvements ennemis ennemis.forEach((e) => { e.x -= e.vitesse; }); ennemis = ennemis.filter((e) => e.x > -50); // nettoyer hors écran // 3. Collisions ennemis.forEach((e) => { if (collision(joueur, e)) { vies--; if (vies === 0) enJeu = false; } }); } // === DESSIN === function draw() { ctx.clearRect(0, 0, canvas.width, canvas.height); // dessiner joueur, ennemis, HUD (score, vies)… if (!enJeu) { ctx.fillText('GAME OVER', 200, 200); } } // === GAME LOOP === function loop() { update(); draw(); requestAnimationFrame(loop); } loop();
🎮 Mini-jeu complet — ← → pour esquiver les blocs rouges !

← → pour déplacer. Esquive les blocs rouges ! Clique sur le canvas d'abord pour le focus.

Les notions d'algorithmique du jeu vidéo

Voici un récap de toutes les notions algorithmiques utilisées dans les jeux :

🔄 Game Loop — Boucle infinie : lire entrées → update → draw → repeat
📐 Coordonnées x/y + vitesse vx/vy — Position += vitesse à chaque frame
💥 Collision AABB — 4 comparaisons pour savoir si 2 rectangles se touchent
⬇️ Gravité — vy += gravité à chaque frame, simuler la physique réelle
⌨️ État des touches — Mémoriser keydown/keyup dans un objet pour un mouvement fluide
📊 Tableaux forEach/filter — Gérer des listes d'ennemis, projectiles, pièces…
⏱️ DeltaTime — Rendre le jeu indépendant du FPS de l'appareil

Tu connais les bases du jeu vidéo en JS ! 🎮
Il ne reste plus qu'à coder ton propre jeu !

← Revoir les bases JS

Variables, fonctions, DOM, événements