Séance 5 · Travaux pratiques
TP 5 · Sprites animés
Une petite scène de jeu de plateforme : un fond, un sol carrelé, des caisses, des pièces qui tournent et un personnage animé que l'on dirige pour ramasser les pièces. Tout est fait avec un seul rectangle et des textures.
Télécharger le code de départtp5-depart.zip · 13 Ko · à décompresser, puis ouvrir le dossier tp5/depart/ dans VS Code
Objectifs
- Ajouter des coordonnées UV et lire une texture dans le fragment shader.
- Créer une texture à partir de données, puis d'une image chargée de façon asynchrone.
- Utiliser la répétition, la transparence et les planches de sprites.
Les positions sont en pixels, avec (0, 0) en haut à gauche (matrice de projection du TP 4). La fonction sprite(tex, x, y, largeur, hauteur) dessine le rectangle unité mis à l'échelle et placé. Au départ, un carré blanc s'affiche en haut à gauche.
Une texture calculée
- 1.1 et 1.2 Ajoutez l'attribut
a_uv, le varyingv_uv, l'uniformsampler2D u_image, et lisez la texture :texture(u_image, v_uv) * u_teinte. - 1.3 Décrivez
a_uv: 2 flottants, stride de 4 flottants, offset de 2 flottants. - 1.4 Dans
creerTexture, réglez le filtrage et la répétition avecgl.texParameteri(quatre appels). - 1.5 Remplissez le damier : le texel (x, y) commence à l'indice
(y * N + x) * 4et occupe 4 octets R, G, B, A (de 0 à 255).
Un damier de 8 × 8 cases bien nettes remplace le carré blanc.
Passez le filtrage à gl.LINEAR. Que voit-on ? Pourquoi ce rendu ne convient-il pas au pixel art ? Que se passe-t-il si vous oubliez complètement de régler TEXTURE_MIN_FILTER ? (Indice : la valeur par défaut utilise des mipmaps que nous n'avons pas créées.)
Une image
- 2.1 Complétez
creerTexturepour une image :gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source). - 2.2 Chargez la caisse :
const imgCaisse = await loadImage("img/caisse.png");puisconst texCaisse = creerTexture(imgCaisse);. Le mot-cléawaitest autorisé car on est dans la fonctionasync main(). - 2.3 Dessinez la caisse en 96 × 96 à droite, posée sur le haut du sol :
y = SOL - 96.
La caisse s'affiche à l'endroit (la diagonale descend du coin haut gauche au coin bas droit).
Le cours recommandait UNPACK_FLIP_Y_WEBGL. Ajoutez-le avant texImage2D : l'image se retourne ! Pourquoi n'en a-t-on pas besoin ici ? (Comparez l'orientation de notre repère en pixels, des UV de nos sommets, et l'ordre des lignes dans un fichier image.) Retirez-le ensuite.
Répéter : le sol et le fond
Pour carreler un grand rectangle avec une petite image, on étire les UV au-delà de 1 et la répétition REPEAT fait le reste.
- 3.1 Ajoutez les uniforms
u_uvEchelleetu_uvDecalageau vertex shader et calculezv_uv = a_uv * u_uvEchelle + u_uvDecalage; - 3.2 et 3.3 Chargez la dalle (en
gl.REPEAT) et envoyez les deux uniforms danssprite. - 3.4 Dessinez le sol de
(0, SOL), de 800 × 128 pixels, avec une dalle tous les 64 pixels. Que doit valoiruvEchelle? - Étirez le damier sur tout le fond (de 0 à
SOL), avec des cases de 40 pixels.
Un sol de 2 rangées de 12,5 dalles, des dalles carrées (non étirées), et un fond en damier sombre.
La transparence
- 4.1 Chargez
texSlimeet dessinez-la en 64 × 64 à la position du slime (etat.slime). - Constat : on voit la planche entière, écrasée, avec un fond noir autour des personnages.
- 4.2 Avant de dessiner les objets transparents (après le décor) :
gl.enable(gl.BLEND)etgl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA). Désactivez le mélange à la fin du rendu.
Dessinez le slime avant le sol, à un endroit où ils se chevauchent. Que se passe-t-il ? Pourquoi l'ordre de dessin compte-t-il avec le mélange ?
L'animation
La planche du slime contient 4 images côte à côte : l'image i occupe les u de i/4 à (i+1)/4.
- 5.2 Écrivez
image(i, n)qui renvoie{ uvEchelle: [1 / n, 1], uvDecalage: [i / n, 0] }. - 5.3 Dessinez le slime avec
sprite(texSlime, x, y, 64, 64, image(i, 4)), oùi = Math.floor(etat.temps * 8) % 4(8 images par seconde). - 5.4 Chargez
texPieceet dessinez chaque pièce deetat.piecesen 48 × 48, animée à 12 images par seconde. Utilisezp.phasepour que les pièces ne tournent pas toutes en même temps.
Le slime rebondit sur place et six pièces tournent, chacune à son propre rythme.
Jouer
- 6.1 Dans
update, déplacez le slime avec les flèches (vitesse en pixels par seconde), sans sortir du canvas, et retenez sa direction dansversGauche. - 6.2 Le slime doit regarder dans la direction où il va. Ajoutez le paramètre
miroiràimage: une échelle négative en u (-1 / n) lit l'image de droite à gauche. Quel décalage faut-il alors ? - 6.3 Si le slime touche une pièce, elle disparaît et le score augmente (
etat.pieces.filter(...)est pratique). Quand toutes sont ramassées,placerPieces().
Le slime se déplace en regardant dans la bonne direction et ramasse les pièces ; le compteur augmente.
Bonus
- A. Saut. Une vitesse verticale
vy, une gravité (en pixels/s², par exemple 1400) ajoutée à chaque mise à jour, et une impulsion vers le haut avec Espace quand le slime est au sol. Les pièces hautes deviennent accessibles. - B. Teinte. Le slime clignote en jaune pendant 0,3 s quand il ramasse une pièce (paramètre
teintedesprite). - C. Défilement. Faites défiler lentement le fond en animant
uvDecalageavec le temps.