< Home
Stampa

Un gioco coi canvas

Sommario

Movimento di una pallina 2D

Utilizzando i canvas proviamo a simulare il comportamento di una pallina in 2D. Qui uno screenshot di come si può vedere la “pallina” (cioè un piccolo cerchio).

Vediamo nello specifico il funzionamento di questo algoritmo. 

Algoritmo di animazione

Per rappresentare uno o più oggetti che si muovono nel tempo, è necessario creare una animazione, ovvero un algoritmo che coi canvas consiste nella ripetizione (tramite un setInterval) dei seguenti passi:

a) aggiornamento della posizione di tutti gli oggetti all’interno del canvas.

b) eliminazione degli oggetti esistenti nel canvas

c) disegno degli oggetti nella nuova posizione.

Se l’intervallo è sufficientemente veloce, l’utente ha l’illusione di vedere gli oggetti muoversi nella schermata.

Questo meccanismo, come vedremo, è alla base anche dei videogiochi. Applichiamolo all’animazione della pallina.

Il componente ball

Utilizziamo la progettazione a componenti, quindi creiamo ua funzione costruttrice che crea la pallina. Questa riceve come parametro una coppia di coordinate iniziali, una dimensione, un angolo ed una velocità, ed infine la dimensione del canvas stesso.

La refresh

L’effetto visivo dello spostamento della pallina si ottiene mediante aggiornamento della posizione (x,y) della pallina stessa. Dobbiamo quindi scrivere una funzione refresh che calcola le nuove coordinate in cui si dovrà trovare la pallina in base alla sua velocità e la sua direzione. Questa funzione dovrà essere eseguita periodicamente tramite setInterval con una frequenza abbastanza veloce da rendere il movimento fluido per l’occhio umano.

Ma come si calcolano le nuove coordinate?

La pallina ha una direzione (angle) e una velocità (speed). La nuova posizione ad ogni aggiornamento corrisponderà ad uno spostamento verso destra o sinistra e verso l’alto o verso il basso che sono rispettivamente le componenti orizzontale e verticale della direzione della pallina. Qui un esempio di spostamento con angolo di 30 gradi.

La traslazione della pallina ha una componente orizzontale, che corrisponde al coseno dell’angolo di direzione,  ed una componente verticale pari al seno dell’angolo. Questi valori poi sono moltiplicati per la velocità della pallina stessa.

Quindi la nuova posizione sarà determinata da queste due formule:

Quindi se ad esempio all’istante t la pallina si trova nella posizione (100,100) ed ha velocità di 20, le nuove posizioni saranno:

Ricordare sempre che nei canvas il punto in alto a sinistra è (0,0). Quello che si deve fare nell’esercizio è quindi semplicemente calcolare le nuove posizioni con la formula sopra riportata.

Ma cosa succede quando la pallina raggiunge immancabilmente la parete? Nella nostra simulazione ipotizziamo che l’urto sia elastico, quindi non c’è perdita di velocità, ma cambia solo l’angolo di uscita, che per effetto del rimbalzo cambia secondo la seguente regola:

– se l’urto è verso le pareti verticali (dx o sx) il nuovo angolo è: 

 se l’urto è verso le pareti orizzontali (sopra o sotto) il nuovo angolo è:

A livello di codice, bisogna controllare se la futura posizione della palla è sul bordo orizzontale, o sul bordo verticale, e quindi aggiornare l’angolo di conseguenza. Se si vede che sta uscendo bisogna ricalcolare il nuovo angolo, così alla traslazione successiva la pallina avrà cambiato direzione, per effetto appunto del rimbalzo. 

Qui il codice completo:

const createBall = (posx, posy, width, startAngle, speed, size) => {
    const ball = {
      x: posx,
      y: posy,
      width: width,
      angle: startAngle,
      speed: speed
    }
    return {
        refresh: () => {
            let newx = ball.x + ball.speed * Math.cos(ball.angle);
            let newy = ball.y + ball.speed * Math.sin(ball.angle);
            console.log("newx: " + newx + " - newy: " + newy);
            if (newx > size - ball.width || newx < ball.width) {
                ball.angle = Math.PI - ball.angle;
            } else if (newy > size - ball.width || newy < ball.width) {
                ball.angle = 2 * Math.PI - ball.angle;
            } else {
                ball.x = newx;
                ball.y = newy;
            }
        },
        get: ball
    }
}

A questo punto creiamo il componente che gestisce la render.

const createDrawComponent = (canvas) => {
    const ctx = canvas.getContext("2d");
    return {
        render: (ball) => {
            ctx.beginPath();
            ctx.arc(ball.x, ball.y, ball.width, 0, 2 * Math.PI);
            ctx.lineWidth = 1;
            ctx.fill();
        },
        clear: () => {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
        }
    }
}

La funzione render riceve i dati della pallina, e la disegna sul canvas, mentre la funzione clear ripulisce l’intero canvas per l’aggiornamento.

A questo punto possiamo mettere tutto insieme ed avviare l’animazione:

const ball = createBall(50, 50, 10, Math.random()%360, 5, 500);
const drawComponent = createDrawComponent(document.getElementById("canvas"));

setInterval(() => {
    ball.refresh();
    drawComponent.clear();
    drawComponent.render(ball.get);
}, 10);

Vogliamo creare due palline? Nessun problema:

const ball1 = createBall(50, 50, 10, Math.random()%360, 5, 500);
const ball2 = createBall(450, 450, 10, Math.random()%360, 6, 500);
const drawComponent = createDrawComponent(document.getElementById("canvas"));

setInterval(() => {
    ball1.refresh();
    ball2.refresh();
    drawComponent.clear();
    drawComponent.render(ball1.get);
    drawComponent.render(ball2.get);
}, 10);

La programmazione per componenti permette quindi di separare la logica dell’animazione (il refresh) dall’aggiornamento dell’interfaccia utente (render).

Eventi da tastiera

Per intercettare un evento da tastiera in una pagina web è sufficiente eseguire il seguente codice:

document.onkeydown = function(e) {
    console.log(String.fromCharCode(e.keyCode)+" --> "+e.keyCode);
};
 
Il valore di keyCode indica il codice numerico del tasto premuto.

Qui un elenco di codici.

Keyboard key PressedJavaScript Key Code value
backspace8
tab9
enter13
shift16
ctrl17
alt18
pause/break19
caps lock20
escape27
page up33
Space32
page down34
end35
home36
arrow left37
arrow up38
arrow right39
arrow down40
print screen44
insert45
delete46
048
149
250
351
452
553
654
755
856
957
a65
b66
c67
d68
e69
f70
g71
h72
i73
j74
k75
l76
m77
n78
o79
p80
q81
r82
s83
t84
u85
v86
w87
x88
y89
z90
left window key91
right window key92
select key93
numpad 096
numpad 197
numpad 298
numpad 399
numpad 4100
numpad 5101
numpad 6102
numpad 7103
numpad 8104
numpad 9105
multiply106
add107
subtract109
decimal point110
divide111
f1112
f2113
f3114
f4115
f5116
f6117
f7118
f8119
f9120
f10121
f11122
f12123
num lock144
scroll lock145
semi-colon186
equal sign187
comma188
dash189
period190
forward slash191
open bracket219
back slash220
close braket221
single quote222