Se volessimo creare una presentazioni animata o un videogioco con una grafica "non vettoriale" ma con una grafica raster (pixellosa) dovremmo imparare a gestire gli sprites.
Ma cosa sono gli sprites?
Gli sprites sono delle immagini in sequenza che fatte scorrere ad una certa velocità ingannano l'occhio facendolo sembrare in movimento.
Per implementare gli sprite nel canvas abbiamo bisogno di uno spritesheet cioè un immagine che contiene tutte le sequenze della nostra animazione. Come in questo esempio.
Se come me non siete dei grandi grafici, ci sono un sacco di risorse in internet. Li potete trovare tra gli archivi di immagini o in siti come
- https://itch.io/game-assets
- https://www.spriters-resource.com/ (quest'ultima è sotto copyright, quindi se li usate fatelo per uso personale)
- https://opengameart.org/
Io ho scelto di animare questo cane che corre, è uno spritesheet semplice su una sola riga con 4 fasi dell'animazione, più avanti vedremo sprite più complessi su più righe e che comanderemo con i tasti.
INIZIALIZIAMO
Iniziamo inizializando un elemento canvas grande 600x400, stampando, a video l'immagine (spritesheet) che contiente i 4 sprite una volta caricata.
- HTML
<canvas id="myCanvas"></canvas>
- JS
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
canvas.width = 600;
canvas.height = 400;
let img = new Image();
img.src = 'https://i.ibb.co/tmdzwtT/dog.jpg';
img.addEventListener('load', () => {
ctx.drawImage(img, 0, 0);
})
Una volta caricata, si vede un'immagine 600 x 200 divisa in 4 frame della grandezza di 150 x 200 che comporranno la nostra animazione.
Visto che a noi serve solo un frame alla volta dobbiamo creare una maschera di 150 x 200 che mostra solo un frame alla volta.
Questa operazione di ritaglio si può fare direttamente con il metodo ctx.drawImage che ha delle opzioni che ci premettono di visualizzare a video sono una pozione rettangolare di un immagine.
Avevo già trattato in questo post, "Canvas Javascript: Come disegnare immagini", come fare, ma oggi andremo a vedere come si può utilizzare questo metodo per creare del movimento.
Ritagliamo lo spritesheet in modo da visualizzare solo il primo frame.
Partiamo dalla coordinata dell'immagine 0, 0 (rX, rY) e tagliamo una porzione larga 150px (rL) e lunga 200px (rA) ( il rettangolo che contiene il primo frame)
Posizioniamo la maschera sul canvas, circa al centro, alle coordinate 225, 100 (x,y) e lo visualizziamo con le stesse dimensioni del ritaglio, 150x200px (l, a)
img.addEventListener('load', () => {
//ctx.drawImage(img, rX, rY, rL, rA, x, y, l, a);
ctx.drawImage(img, 0, 0, 150, 200, 225, 100, 150, 200);
})
Adesso per creare l'effetto di movimento dobbiamo spostare la funzione ctx.drawImage in un ciclo e far scorrere la maschera di ritaglio ad ogni frame e una volta finiti tornare al frame 0.
Creiamo una funzione loop che si chiamerà in modo ricorsivo grazie al metodo requestAnimationFrame ad ogni fine ciclo.
requestAnimationFrame è il metodo creato apposta per gestire i cicli nel canvas. Ne parlo in maniera più approfondita in questo post: "Animazioni con il Canvas di javascript: Come cos'è e come implementarlo con un esempio base"
let img = new Image();
img.src = 'https://i.ibb.co/d264Yhf/greeting.png';
img.addEventListener('load', () => {
requestAnimationFrame(loop)
})
let loop = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, 150, 200, 225, 100, 150, 200);
requestAnimationFrame(loop)
}
Al caricamento dell'immagine viene chiamata la funzione loop.
Che ha 3 metodi al suo interno: clearRect che ripulisce il canvas, il metodo che stampa a video che abbiamo creato precedentemente e per ultimo requestAnimationFrame(loop) che richiama se stessa.
Il prossimo passo è quello di aumentare il frame ad ogni ciclo.
Nel nostro caso sono 4 frames e vanno dal frame 0 al frame 3.
I frame si trovano ad una distanza di 150px, quindi il valore per rX sarà di:
- 0 per il frame 0
- 150px per il frame 1
- 300px per il frame 2
- 450px per il frame 3
Da questo si può inturire che se moltiplichiamo il numero del frame con la larghezza (Lr) otterremo il valore rX.
rX = lr * frameCounter;
let lr= 150;
let frameCounter = 0;
let rX = 0
let loop = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
rX = lr * frameCounter;
ctx.drawImage(img, rX, 0, lr, 200, 225, 100, 150, 200);
framecounter < 3 ? frameCounter++ : frameCounter = 0;
requestAnimationFrame(loop)
}
Nel primo ciclo il secondo argomento prende come risultato della moltiplicazione 150 * 0, quindi il ritaglio partirà dalla posizione 0, poi il framecounter aumenta di uno perchè il "frame" è minore di 3.
Nel secondo ciclo, il frame vale 1 che moltiplicato a 150 fa rX: 150... e cosi via 150 * 2 = 300, 150 * 3 = 450 e poi l'animazione ricomincia perchè il valore frame non è maggiore di 3 e il valore di framecounter tornerà a 0.
Se dovessimo lasciare il codice così, il nostro cane correrebbe troppo veloce, perchè gli fps, frame per secondo, sono troppo alti.
Gli fps sono dei valore che indicano quante volte il ciclo viene riprodotto in un secondo.
Niente paura, nel prossimo post, spegherò in maniera semplice cos'è l'fps e qual'è la tecnica per implementarlo al meglio.
Per adesso gli applicheremo un setTimeout che non è la miglior soluzione, ma sicuramente la più veloce
let lr= 150;
let frameCounter = 0;
let rX = 0
let framesPerSecond = 10;
let loop = () => {
setTimeout( () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
rX = lr * frameCounter;
ctx.drawImage(img, rX, 0, lr, 200, 225, 100, 150, 200);
frameCounter < 3 ? frameCounter++ : frameCounter = 0;
requestAnimationFrame(loop)
}, 1000 / framesPerSecond);
}
CONCLUSIONE
Abbiamo visto come gestire un animazione semplice con gli Sprites.
Successivamente vedremo gli fps per la gestire la velocità dei frame, come gestire SpriteSheet su più righe e come unire i comandi di tastiera con le animazioni.
Se avete dei consigli, suggerimenti o critiche costruttive lasciatemi un commento qui sotto oppure contattatemi trammite i miei social.
Top comments (0)