Apprendre comment créer un moteur d'affichage 2D en TypeScript

Partie 2 : charger une texture

Nicolas Legrand

Pour réagir au contenu de ce tutoriel, un espace de dialogue vous est proposé sur le forum. Commentez

- Titre : Apprendre comment créer un moteur d'affichage 2D en TypeScript

- Auteur : Nicolas Legrand

- Parution : 17 février 2020

- Licence : Copyright ® 2020 Nicolas Legrand. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Voir également :

- Comment créer un moteur d'affichage 2D en HTML5

Sommaire

- I. Barista part II

- I-A. Charger une texture

- I-B. Dessiner une texture

- I-C. Méthodes de dessin avancées

- I-D. Ombres portées

- I-E. Dessiner à travers un masque

- II. Remerciements Developpez.com

I - Barista part II

I-A - Charger une texture

Voir le code : GitHub

Animation en format "youtube" non supportée.
Contenu uniquement consultable en ligne à cette adresse :
https://www.youtube.com/v/uOp9OejGraQ

En JavaScript (et donc en Typescript), il est très simple de charger une image, aussi appelée texture dans le domaine du jeu vidéo, d’ailleurs nous utiliserons ce terme dorénavant. Il y a plusieurs façons de faire, pour le besoin de ce chapitre, nous utiliserons la plus simple qui est d’inclure les images directement dans la structure HTML. Il nous suffira alors de démarrer le script au chargement complet de la page et pour ça, nous utiliserons l’événement natif de l’objet « window » à savoir « load ».

1. function init(){

2. const canvas = getCanvas();

3. const context  = getContext();

5. // on détermine les dimensions du canvas

6. canvas.width = 640;

7. canvas.height = 480;

9. // on sauvegarde le contexte

10. context.save();

12. // on va incorporer nos opérations de dessin ici

15. // on restaure le contexte

16. context.restore();

17. }

19. function getContext(){

20. return getCanvas().getContext("2d");

21. }

23. function getCanvas(){

24. return document.querySelector("canvas");

25. }

27. window.addEventListener("load", init);

1. <!DOCTYPE html>

2. <html lang="en">

3. <head>

4. <meta charset="UTF-8">

5. <meta name="viewport" content="width=device-width, initial-scale=1.0">

6. <meta http-equiv="X-UA-Compatible" content="ie=edge">

7. <title>Barista</title>

8. <script src="./dist/main.js"></script>

9. </head>

10. <body>

11. <canvas id="barista"></canvas>

12. <img src="kirby.png" />

13. </body>

14. </html>

I-B - Dessiner une texture

Pour dessiner une texture, l’API de dessin de canvas embarque une méthode dont voici la première signature possible :

context.drawImage(image,dx,dy)

- le paramètre image représente la texture que l’on souhaite dessiner ;

- les paramètres dx et dy représentent les coordonnées auxquelles on souhaite dessiner l’image.

Cette méthode vous permettra de dessiner directement la texture sur le canvas, sans aucune transformation.

1. function init(){

2. const canvas = getCanvas();

3. const context  = getContext();

5. // on détermine les dimensions du canvas

6. canvas.width = 640;

7. canvas.height = 480;

9. // on sauvegarde le contexte

10. context.save();

12. // on récupère l'élément html de type image qui nous sert de texture (ici kirby)

13. const kirby = document.querySelector("img");

15. // puis on le dessine à l'aide de la méthode drawImage de l'objet de type context

16. context.drawImage(kirby, 0, 0);

19. // on restaure le contexte

20. context.restore();

21. }

23. function getContext(){

24. return getCanvas().getContext("2d");

25. }

27. function getCanvas(){

28. return document.querySelector("canvas");

29. }

31. window.addEventListener("load", init);

I-C - Méthodes de dessin avancées

Jusqu’ici, nous avons vu la méthode classique pour dessiner une texture, nous allons à présent faire un tour du côté des méthodes de dessin avancées en commençant par dessiner une texture à la taille que l’on souhaite obtenir. Pour ce faire, nous allons voir la deuxième manière d'appeler la méthode context.drawImage() :

context.drawImage(texture, dx, dy, dw, dh)

- le paramètre texture représente la texture que l’on souhaite dessiner ;

- les paramètres dx, dy, dw et dh représentent un rectangle de largeur dw et de hauteur dh dont le coin en haut à gauche est situé en dx et dy. Ce rectangle représente les nouvelles dimensions de la texture au sein du canvas.

1. function init(){

2. const canvas = getCanvas();

3. const context  = getContext();

5. // on détermine les dimensions du canvas

6. canvas.width = 640;

7. canvas.height = 480;

9. // on sauvegarde le contexte

10. context.save();

12. // on récupère l'élément html de type image qui nous sert de texture (ici kirby)

13. const kirby = document.querySelector("img");

15. // puis on réalise le dessin à l'aide de la méthode drawImage de l'objet de type context

16. // on dessine cette texture à partir des coordonnées x = 10 et y = 35

17. // la nouvelle largeur de la texture est de 200px et la nouvelle hauteur vaut 20px

18. context.drawImage(kirby, 10, 35, 200,20);

20. // on restaure le contexte

21. context.restore();

22. }

24. function getContext(){

25. return getCanvas().getContext("2d");

26. }

28. function getCanvas(){

29. return document.querySelector("canvas");

30. }

32. window.addEventListener("load", init);

Ici, on dessine notre texture aux coordonnées x = 10px, y = 35px et on applique une transformation à cette texture afin qu’elle soit dessinée avec 200px de largeur et 20px de hauteur. Nous allons maintenant voir comment dessiner une portion de notre texture et pour cela, nous allons étudier la troisième et dernière signature possible de la méthode context.drawImage() :

context.drawImage(texture, sx, sy, sw, sh, dx, dy, dw, dh)

- le paramètre texture représente la texture que l’on souhaite dessiner ;

- les paramètres sx, sy, sw et sh représentent un rectangle dont les dimensions correspondent à une portion de la texture originelle ;

- les paramètres dx, dy, dw et dh représentent les nouvelles dimensions de la portion de texture originelle au sein du canvas.

1. function init(){

2. const canvas = getCanvas();

3. const context  = getContext();

5. // on détermine les dimensions du canvas

6. canvas.width = 640;

7. canvas.height = 480;

9. // on sauvegarde le contexte

10. context.save();

12. // on récupère l'élément html de type image qui nous sert de texture (ici kirby)

13. const kirby = document.querySelector("img");

15. // puis on le dessine à l'aide de la méthode drawImage de l'objet de type context

16. // on dessine la portion de l'image originelle comprise entre les coordonnées x = 0, y = 0, x = 100, y = 100

17. // on dessine cette portion de façon un peu déformée

18. context.drawImage(kirby, 0,0, 100, 100, 30,30,250,250);

20. // on restaure le contexte

21. context.restore();

22. }

24. function getContext(){

25. return getCanvas().getContext("2d");

26. }

28. function getCanvas(){

29. return document.querySelector("canvas");

30. }

32. window.addEventListener("load", init);

Cette portion correspond à un carré de 100px de côté et dont le coin en haut à gauche est situé aux coordonnées x = 0px et y = 0px.

Cette portion d’image sera dessinée sur le canvas dans un carré de 250px de côté et dont le coin en haut à gauche est situé aux coordonnées x = 30px et y = 30px.

I-D - Ombres portées

Maintenant, nous allons pouvoir passer au dessin avec des ombres, nous allons donc nous intéresser aux propriétés : shadowColor, shadowBlur, shadowOffsetX et shadowOffsetY de l’objet context. Ces propriétés peuvent être utilisées avec les méthodes de dessin classiques, avec ou sans texture.

- La propriété shadowOffsetX sert à définir le décalage en x que l’ombre aura par rapport au dessin, le type de cette propriété est un entier.

- La propriété shadowOffsetY sert à définir le décalage en y que l’ombre aura par rapport au dessin, le type de cette propriété est un entier.

- La propriété shadowColor, comme son nom l’indique, définit la couleur de l’ombre, le type de cette propriété est une chaîne.

- La propriété shadowBlur, elle, sert à spécifier la netteté (ou plus spécifiquement le flou) que l’on souhaite appliquer à cette ombre, le type de cette propriété est un entier.

1. function init(){

2. const canvas = getCanvas();

3. const context  = getContext();

5. // on détermine les dimensions du canvas

6. canvas.width = 640;

7. canvas.height = 480;

9. // on sauvegarde le contexte

10. context.save();

12. // on récupère l'élément html de type image qui nous sert de texture (ici kirby)

13. const kirby = document.querySelector("img");

15. // on définit les propriétés de l'ombre portée pour tous les dessins

16. // qui vont suivre.

18. context.shadowBlur = 10;

19. context.shadowColor = "#ff0000"; // l'ombre sera rouge

20. context.shadowOffsetX = 10; // ombre décalée de 10px vers la droite

21. context.shadowOffsetY = 10; // ombre décalée de 10px vers le bas

24. // puis on le dessine à l'aide de la méthode drawImage de l'objet de type context

25. // on dessine la portion de l'image originelle comprise entre les coordonnées x = 0, y = 0, x = 100, y = 100

26. // on dessine cette portion de façon un peu déformée

27. context.drawImage(kirby, 0,0, 100, 100, 30,30,250,250);

29. // on restaure le contexte

30. context.restore();

31. }

33. function getContext(){

34. return getCanvas().getContext("2d");

35. }

37. function getCanvas(){

38. return document.querySelector("canvas");

39. }

41. window.addEventListener("load", init);

I-E - Dessiner à travers un masque

Nous allons à présent apprendre à dessiner à travers un masque. Pour cela, nous allons avoir besoin de la propriété globalCompositeOperation de l’objet context. Cette propriété peut prendre plusieurs valeurs dont les résultats sont illustrés à l’aide du graphique ci-dessous :

Canvas autorise plusieurs modes de fusion. Le mode de fusion correspond à la façon dont la source interagit avec la destination. Ici, le carré bleu est ce qui a déjà été dessiné, il s'agit de la source. Le cercle rouge, quant à lui, correspond à la destination.

Comment interpréter ce graphique ? En premier lieu, il faut que je vous donne le code source qui va avec :

1. function init(){

2. const canvas = getCanvas();

3. const context  = getContext();

5. // on détermine les dimensions du canvas

6. canvas.width = 640;

7. canvas.height = 480;

9. // on sauvegarde le contexte

10. context.save();

12. // on récupère l'élément html de type image qui nous sert de texture (ici kirby)

13. const kirby = document.querySelector("img");

15. // on définit les propriétés de l'ombre portée pour tous les dessins

16. // qui vont suivre.

18. context.shadowBlur = 10;

19. context.shadowColor = "#ff0000"; // l'ombre sera rouge

20. context.shadowOffsetX = 10; // ombre décalée de 10px vers la droite

21. context.shadowOffsetY = 10; // ombre décalée de 10px vers le bas

23. // on va dessiner notre kirby à travers un masque circulaire

24. // commençons par dessiner le cercle en question

26. context.beginPath();

27. context.fillStyle = "red"; // ici peu importe la couleur du moment que le cercle est plein

28. context.moveTo(225,225);

29. context.arc(225,255,125, 0, 360 * Math.PI / 180);

30. context.fill();

32. // une fois notre cercle dessiné, on change le mode de fusion

33. // de la balise canvas pour le définir à source-in.

34. // tous les dessins qui vont suivre seront dessinés à travers un masque

36. context.globalCompositeOperation = "source-in";

39. // puis on le dessine à l'aide de la méthode drawImage de l'objet de type context

40. context.drawImage(kirby, 0,0);

42. // on restaure le contexte

43. context.restore();

44. }

46. function getContext(){

47. return getCanvas().getContext("2d");

48. }

50. function getCanvas(){

51. return document.querySelector("canvas");

52. }

54. window.addEventListener("load", init);

Il s’agit d’une opération en deux étapes :

- tout d’abord, on dessine le masque sur l’objet context (ici le cercle) ;

- ensuite, on spécifie la valeur de la propriété globalCompositeOperation de l’objet context (ici source-in) ;

- puis, on dessine le contenu que l’on souhaite voir apparaître à travers le masque (kirby).

Il suffit de changer la valeur de la propriété globalCompositeOperation pour pouvoir obtenir tous les résultats retranscrits sur le graphique. Pour dessiner un objet à travers un masque, la valeur qui nous intéresse est source-in. Voilà, les bases du dessin de texture ont été passées en revue !

II - Remerciements Developpez.com

Nous tenons à remercier Nicolas Legrand qui nous a aimablement autorisés à publier son tutoriel. Nos remerciements également à Yahiko pour la relecture technique, Malick pour la mise au gabarit et Escartefigue pour la relecture orthographique.