Avrete sicuramente sentito parlare del famoso gioco Flappy Bird ma qui nel forum non abbiamo ancora analizzato nel dettaglio come funziona la parte Javascript.
Iniziamo subito definendo le variabili principali:
let bird;
let pipes = [];
let birdImg;
Una per l'oggetto "bird" (in questo caso il logo EHF), un array "pipes" per contenere gli oggetti "Pipe" (tubi) e una variabile "birdImg" per caricare l'immagine dell'uccello.
Definiamo preload:
function preload() {
birdImg = loadImage('birdie.png');
}
La funzione "preload" viene eseguita una volta prima che venga avviata la funzione "setup()". Qui viene caricata l'immagine del logo "birdie.png".
Funzione setup:
function setup() {
createCanvas(800, 600);
bird = new Bird();
pipes.push(new Pipe());
}
La funzione "setup()" anche questa viene eseguita una volta come preload. Qui viene creato un canvas (area di disegno) di dimensioni 800x600 pixel dove viene creato "bird" come istanza della classe "Bird" e viene creato un primo tubo che viene aggiunto all'array "pipes".
Vediamo la funzione draw
function draw() {
background(112, 190, 201);
for (let i = pipes.length - 1; i >= 0; i--) {
pipes[i].show();
pipes[i].update();
if (pipes[i].hits(bird)) {
console.log("HIT");
}
if (pipes[i].offscreen()) {
pipes.splice(i, 1);
}
}
bird.update();
bird.show();
if (frameCount % 100 == 0) {
pipes.push(new Pipe());
}
}
A differenza delle funzioni citate sopra questa viene eseguita in loop disegnando il gioco sul canvas. All'interno di questa funzione viene creato:
- Sfondo: azzurro-verde.
- Iterazione sui tubi: viene eseguito un ciclo "for" per iterare su tutti i tubi nell'array "pipes". Per ogni tubo abbiamo le funzioni "show()" e "update()" dell'oggetto "Pipe" corrispondente.
- Collisione con il tubo: in questo caso con funzione "hits(bird)" su "Pipe".
- Rimozione dei tubi fuori dalla schermata: Se un tubo è fuori dalla schermata (alla sinistra del canvas) viene rimosso dall'array "pipes".
- Aggiornamento e visualizzazione dell'uccello: con funzioni "update()" e "show()" su "bird".
- Aggiunta di nuovi tubi: ad ogni 100 frame viene creato un nuovo tubo e aggiunto all'array "pipes".
Funzioni di controllo per il logo:
function mousePressed() {
bird.up();
}
function keyPressed() {
if (key === ' ') {
bird.up();
}
}
Importanti perché consentono di far volare l'uccello quando il giocatore preme il mouse o la barra spaziatrice. Entrambe chiamano la funzione "up()" dell'oggetto "bird" che modifica la velocità verticale dell'uccello per farlo "saltare" verso l'alto.
Classe bird:
function Bird() {
this.y = height / 2;
this.x = 64;
this.gravity = 0.6;
this.lift = -15;
this.velocity = 0;
this.show = function() {
image(birdImg, this.x, this.y, 32, 32);
}
// Funzione per far volare l'uccello verso l'alto
this.up = function() {
this.velocity += this.lift;
}
this.update = function() {
this.velocity += this.gravity;
this.velocity *= 0.9;
this.y += this.velocity;
if (this.y > height) {
this.y = height;
this.velocity = 0;
}
if (this.y < 0) {
this.y = 0;
this.velocity = 0;
}
}
}
Con questa parte si definiscono le caratteristiche base della classe bird come posizione (x, y), velocità verticale ("velocity"), gravità, "lift" (la forza con cui l'uccello salta verso l'alto) e viene disegnato con l'immagine "birdImg". I metodi "show()", "up()" e "update()" sono responsabili per la visualizzazione dei movimenti del logo.
Classe "Pipe":
function Pipe() {
this.top = random(height / 2);
this.bottom = random(height / 2);
this.x = width;
this.w = 20;
this.speed = 2;
this.highlight = false;
this.hits = function(bird) {
if (bird.y < this.top || bird.y > height - this.bottom) {
if (bird.x > this.x && bird.x < this.x + this.w) {
this.highlight = true;
return true;
}
}
this.highlight = false;
return false;
}
this.show = function() {
fill(52, 235, 55);
if (this.highlight) {
document.getElementById("endscreen").style.display = "block"
}
rect(this.x, 0, this.w, this.top);
rect(this.x, height - this.bottom, this.w, this.bottom);
}
this.update = function() {
this.x -= this.speed;
}
this.offscreen = function() {
return (this.x < -this.w);
document.getElementById("endscreen").style.display = "block"
}
}
Come per la classe bird in questo caso si definiscono le caratteristiche dei tubi: posizione orizzontale (x) iniziale, altezza superiore ("top") e inferiore ("bottom") casuali, una larghezza ("w") di 20 pixel e una velocità orizzontale ("speed") di 2 pixel.
Si può notare che ci sono diverse funzioni ripetute come ad esempio la funzione hits(bird) che controlla se l'uccello colpisce uno dei tubi, funzione "show()" visualizza i tubi sul canvas evidenziando il tubo se l'uccello lo colpisce, "update()" aggiorna la posizione orizzontale del tubo in base alla sua velocità, "offscreen()" controlla se il tubo è fuori dalla schermata restituendo "true" se il tubo ha superato la sinistra del canvas. Le animazioni sono state create con p5.
Mettiamo insieme i pezzi:
Codice:
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>EHFflappy</title>
<link href="style.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.jsdelivr.net/npm/p5@1.6.0/lib/p5.js"></script>
</head>
<body>
<div id="game">
<script src="script.js"></script>
</div>
<div id="endscreen">
<h1>Hai perso! Riprova!</h1>
</div>
</body>
</html>
CSS:
body{
font-family:"Arial";
}
#endscreen{
background-color:black;
color:white;
width:100%;
height:100%;
display:none;
}
JAVASCRIPT:
let bird;
let pipes = [];
let birdImg;
function preload() {
birdImg = loadImage('birdie.png');
}
function setup() {
createCanvas(800, 600);
bird = new Bird();
pipes.push(new Pipe());
}
function draw() {
background(112, 190, 201);
for (let i = pipes.length - 1; i >= 0; i--) {
pipes[i].show();
pipes[i].update();
if (pipes[i].hits(bird)) {
console.log("HIT");
}
if (pipes[i].offscreen()) {
pipes.splice(i, 1);
}
}
bird.update();
bird.show();
if (frameCount % 100 == 0) {
pipes.push(new Pipe());
}
}
function mousePressed() {
bird.up();
}
function keyPressed() {
if (key === ' ') {
bird.up();
}
}
function Bird() {
this.y = height / 2;
this.x = 64;
this.gravity = 0.6;
this.lift = -15;
this.velocity = 0;
this.show = function() {
image(birdImg, this.x, this.y, 32, 32);
}
this.up = function() {
this.velocity += this.lift;
}
this.update = function() {
this.velocity += this.gravity;
this.velocity *= 0.9;
this.y += this.velocity;
if (this.y > height) {
this.y = height;
this.velocity = 0;
}
if (this.y < 0) {
this.y = 0;
this.velocity = 0;
}
}
}
function Pipe() {
this.top = random(height / 2);
this.bottom = random(height / 2);
this.x = width;
this.w = 20;
this.speed = 2;
this.highlight = false;
this.hits = function(bird) {
if (bird.y < this.top || bird.y > height - this.bottom) {
if (bird.x > this.x && bird.x < this.x + this.w) {
this.highlight = true;
return true;
}
}
this.highlight = false;
return false;
}
this.show = function() {
fill(52, 235, 55);
if (this.highlight) {
document.getElementById("endscreen").style.display = "block"
}
rect(this.x, 0, this.w, this.top);
rect(this.x, height - this.bottom, this.w, this.bottom);
}
this.update = function() {
this.x -= this.speed;
}
this.offscreen = function() {
return (this.x < -this.w);
document.getElementById("endscreen").style.display = "block"
}
}
RISULTATO: