Découvre comment les jeux vidéo fonctionnent avec Canvas, la game loop et les notions d'algorithmique clés.
IntermédiaireCanvas 2DDé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 --><canvasid="monCanvas"width="600"height="400"></canvas>// Dans script.js — Initialiser le canvasconstcanvas=document.getElementById('monCanvas');
constctx=canvas.getContext('2d');
// Dessiner un rectanglectx.fillStyle ='#EAB308';
ctx.fillRect(50, 50, 100, 80); // x, y, largeur, hauteur// Dessiner un cerclectx.fillStyle ='#EF4444';
ctx.beginPath();
ctx.arc(200, 100, 30, 0, Math.PI *2); // x, y, rayon, 0, 2πctx.fill();
// Écrire du textectx.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 :
// requestAnimationFrame : la fonction magique// Elle appelle ta fonction avant chaque affichage (~60fps)functiongameLoop() {
// 1. EFFACER le canvas (sinon les anciens dessins restent !)ctx.clearRect(0, 0, canvas.width, canvas.height);
// 2. METTRE À JOUR : déplacer les objetsmettreAJour();
// 3. DESSINER : afficher tous les objetsdessiner();
// 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 vitesseconstjoueur= {
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
};
functionmettreAJour() {
// Mouvement : position += vitesse (à chaque frame)joueur.x +=joueur.vx;
joueur.y +=joueur.vy;
// Rebondir sur les bords gauche/droiteif (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 AABBfunctioncollision(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 loopconstjoueur= { x: 100, y: 200, largeur: 40, hauteur: 40 };
constennemi= { x: 200, y: 190, largeur: 50, hauteur: 50 };
constpiece= { 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 écranpiece.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 physiquesconstGRAVITE=0.5; // px/frame² ajoutés à chaque frameconstFORCE_SAUT=-12; // négatif = vers le hautconstSOL=350; // hauteur du sol en pixelsletjoueur= { x: 100, y: 300, vy: 0, auSol: false };
functionmettreAJour() {
// Appliquer la gravité : accélère le joueur vers le basjoueur.vy +=GRAVITE;
// Appliquer le mouvement verticaljoueur.y +=joueur.vy;
// Collision avec le sol : empêcher de passer à traversif (joueur.y >=SOL) {
joueur.y =SOL;
joueur.vy =0; // stopper la chutejoueur.auSol =true;
}
}
// Saut : seulement si le joueur est sur le soldocument.addEventListener('keydown', function(e) {
if (e.key ===' '&&joueur.auSol) {
joueur.vy =FORCE_SAUT; // impulsion vers le hautjoueur.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éesconsttouches= {}; // objet qui stocke l'état de chaque touchedocument.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 framefunctiongererEntrees() {
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'ennemisletennemis= [
{ 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 forEachennemis.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 conditionennemis=ennemis.filter((e) =>e.vie >0);
// Trier le tableau de scores (du plus grand au plus petit)constscores= [
{ 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 tableauennemis.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.
letdernierTemps=0;
functiongameLoop(timestamp) {
// timestamp = temps écoulé depuis le début (en ms)constdt= (timestamp-dernierTemps) /1000; // en secondesdernierTemps=timestamp;
// Vitesse en pixels PAR SECONDE (pas par frame)constVITESSE=200; // 200px/seconde, identique à 30fps ou 60fps// Déplacement proportionnel au temps écouléjoueur.x +=VITESSE*dt; // ~3.3px à 60fps, ~6.6px à 30fpsctx.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 :