🎮

Jeu vidéo 2D en JS

Les bases

Création du Canvas

La taille d'un canvas définit la zone de pixels sur laquelle l'on va pouvoir dessiner.


							
							<canvas id="game" width="1024" height="768"></canvas>
						

Adaptation à la taille de l'écran


							<canvas id="game"></canvas>
							<script>
									const canvas = document.querySelector('#game');

									// Dimensionnement à la taille de l'écran
									canvas.width = window.innerWidth;
									canvas.height = window.innerHeight;
							</script>
						

Dessiner avec Canvas

Pour dessiner ou effectuer n'importe quel autre type d'action sur un <canvas>, il faut récupérer un contexte.


							const canvas = document.querySelector('#game');
							const context = canvas.getContext('2d'); // (en minuscules)
						

On utilisera ensuite les fonctions de dessins associées à ce contexte pour dessiner plein de choses.


							// Dessine un rectangle en x,y de taille width,height
							context.fillRect(x,  y,  width, height);

							// Dessine un cercle en x, y et de rayon radius
							context.arc(x, y, radius, angleStart, angleStop);
							context.fill();
						

La game loop

Il s'agit d'une boucle infinie qui permet au jeu de tourner.

C'est dedans que sera écrit le code qui gère
les intéractions, les calculs et le dessin.

Ces différentes parties de code seront traitées séparemment ...

Initialize, Update et Draw

Le code du jeu se trouve dans la game loop,
cependant, il est découpé en 3 parties :

  1. Initialisation
    Effectuer toutes les actions nécessaire au démarrage du jeu.
  2. Update
    L'utilité principale de cette partie est de préparer les objets a être dessinés.
    C'est ici que s'opèrent tous les calculs et intéractions.
  3. Rendering
    Dès que tout est calculé et prêt, on entre dans le rendering
    où l'on va dessiner tous nos objets à l'écran.


Source : gamedev tuts+

Loop avec setTimeOut (déconseillé)


							function run() {
									update(); // Partie "update"
									render(); // Partie "rendering"

									setTimeOut(run, 1000/60); // On boucle...
							}
						

Défauts

  • Utilise beaucoup de CPU
  • Risques de saccades
  • Continue de tourner si on a changé d'onglet

Avec requestAnimationFrame (conseillé)


							function run() {
									update(); // Partie "update"
									render(); // Partie "rendering"

									requestAnimationFrame(run); // On boucle...
							}
						

Avantages

  • Le navigateur décide quand passer à la prochaine frame
  • Animations plus fluides
  • Ne dessine que si le client voit l'onglet ou la page

Les images/sprites

Les images sont utilisées pour représenter
des personnages ou du décor.

Au même titre qu'on peut dessiner des formes,
on peut dessiner des images sur le canvas.

IMPORTANT : Il faut attendre que l'image soit chargée avant de pouvoir la dessiner !


						const myImage = new Image();
						myImage.src = 'path/to/file.png';

						myImage.onload = function() {
							// Dessine l'image spécifiée en position x,y
							context.drawImage(myImage, x, y);
						}
					

Les animations avec des spritesheets

Pour animer des personnages, des vaisseaux, ...etc, on utilise des spritesheets.

Bob

Côté code, il n'y a qu'à gérer le timing
entre le dessin des différents états.

Bob

Détails sur la fonction drawImage()

Une entité = Un objet

En développement de jeu vidéo, on définit généralement une entité (telle qu'un personnage, un tir, un bonus) par un objet possédant des propriétés.


							const hero = {
								x      : 100, // coordonnées en 'x'
								y      : 75,  // coordonnées en 'y'
								width  : 64,  // longueur
								height : 84,  // hauteur
								speed  : 2,   // vitesse de déplacement
								life   : 100,  // points de vie
								// …

								update() {
									this.x += this.speed; // déplacement à droite

									this.life--; // -1 point de vie
								},

								render(context) {
									context.fillRect(this.x, this.y, this.width, this.height);
								}
							};
						

Ce sont ces propriétés que l'on fera varier dans la game loop.

Gérer plusieurs entités
(e.g. des ennemis)


							const enemies = [];

							function create(nbEnemies) {
								for (let i = 0; i < nbEnemies; i++) {
									enemies.push({
										x      : 50,
										y      : 10,
										width  : 64,
										height : 84,
										speed  : 2,
										life   : 10
									});
								}
							}

							function updateEnemies() {
								for (const enemy of enemies) { // pour chaque ennemi …
									enemy.y += enemy.speed; // … on déplace vers le bas
								}
							}
						

Les entrées clavier

En général, on définit un objet Keyboard que l'on update dans les listeners keydown et keyup


							const Keyboard = {
								up   : false,
								down : false,
								left : false,
								right: false,
							};

							document.addEventListener('keydown', (event) => {
								if (event.key === 'ArrowUp') Keyboard.up = true;
								// …
							});

							document.addEventListener('keyup', (event) => {
								if (event.key === 'ArrowUp') Keyboard.up = false;
								// …
							});							
						

Une fois cela défini, on peut utiliser aisemment notre keyboard dans la boucle d'update.


							function update() {
								if (Keyboard.up) 			hero.y -= hero.speed;
								if (Keyboard.right) 	hero.x += hero.speed;
								if (Keyboard.bottom) 	hero.y += hero.speed;
								if (Keyboard.left) 		hero.x -= hero.speed;
							}
						

On procède également de la même manière pour la souris en récupérant sa position et les états des clics.


							const Mouse = {
								x    : 0,
								y    : 0,
								down : false,
							};
							
							document.addEventListener('mousemove', (event) => {
								Mouse.x = event.screenX;
								Mouse.y = event.screenY;
							});
							
							document.addEventListener('mousedown', (event) => {
								Mouse.down = true;
							});
							
							document.addEventListener('mouseup', (event) => {
								Mouse.down = false;
							});
						

L'utilisation du son

Si l'on souhaite traiter le son en natif, le chargement et l'utilisation se fait pratiquement de la même manière que pour une image.

Cependant, il existe des librairies permettant de gérer cela plus vite.

Une des plus connue se nomme Buzz.js.

Exemple d'utilisation


							const soundShot = new buzz.sound("sounds/shot.ogg");

							soundShot.play(); // 🎶🎵
						

Consulter la documentation complète pour plus d'informations

Quelques liens utiles

Papoy!