back to top

LimeJS per creare giochi in HTML5

In questo articolo vi presenterò un framework molto interessante e che ci consentirà di creare velocemente – e in maniera piuttosto semplice – dei complessi giochi in HTML5 adatti, quindi, ad essere fruiti mediante i classici computer desktop e notebook, ma anche su smartphone e tablet dotatati di touchscreen: il nome di questo interessante framework è LimeJS.

Con LimeJS sarà possibile creare, quindi, giochi davvero spettacolari e compatibili con i dispositivi più in voga del momento come Apple iPhone, iPod e iPad ma anche con gli smartphone Android.

Installazione

L’ambiente di lavoro ottimale è senza dubbio Linux/Unix, quindi anche MacOS va più che bene. Le istruzioni che seguono riguardano, quindi, l’installazione sotto questo ambiente. Per quanto riguarda invece gli utenti Windows l’installazione avviene seconde le modalità descritte sul file README presente nella distribuzione della libreria.

Per funzionare correttamente il framework ha bisogno di alcune dipendenze:

  • Python 2.6+
  • Git
  • Subversion or Git-SVN
  • Java (solo se si vuole usare Closure Compiler)

Per cominciare basta scaricare la libreria tramite github.

La libreria si compone principlamente di due cartelle: la cartella lime/ in cui sono contenuti i sorgenti Javascript e la cartella bin/ in cui è contenuto il file lime.py che permette di eseguire diverse operazioni come, ad esempio, aggiornamenti dipendenze, creazioni progetti, etc.

Per inizializzare il nostro ambiente di sviluppo è sufficiente sposotarsi nella root della cartella di LimeJS e lanciare il seguente comando:

$ ./bin/lime.py init

Questo permetterà di scaricare le librerie necessarie e di configurare tutti i componenti (per la precisione le librerie che vengono scaricate sono Closure Library, Box2D physics library, Closure Compiler e Templates). LimeJS è basato completamente sulla libreria Javascript Closure sviluppata da Google la quale costituisce il core di molte applicazioni di Mountain View come, ad esempio, GMail.

Partiamo con la realizzazione del nostro piccolo giochino, dando però un occhio a quelle che sono le classi più importanti caratteristiche di LimeJS.

Classi principali

Le classi principali che compongono sono utilizzate all’interno di LimeJS sono le seguenti:

  • Director: è l’elemento centrale di ogni gioco. E’ presente uno ed un solo Director per ogni gioco ed il suo compito è quello di gestire tutte le altre classi che verranno evidenziate successivamente.
  • Scene: classe che rappresenta le scene all’interno del gioco (schermate). A questa classe sono aggiunti i vari livelli.
  • Layer: rappresenta una porzione di schermo. L’utilizzo che viene fatto è simile a quello dei livelli più noti di Photoshop.
  • ScheduleManager: classe utilizzata all’interno del framework per gestire tutti quelli che sono gli eventi ripetuti e i movimenti di oggetti.
  • Node: sono gli oggetti, ovvero le entità all’interno del gioco. Sono dotate delle proprietà tipiche di ogni oggetto grafico, come le dimensioni, la posizione, la scala; sono anche spostabili all’interno del layer di pertinenza
  • Sprite: sono le forme all’interno del gioco. Non si usano infatti direttamente i nodi, ma bensì queste classi. Possono rappresentare una forma geometrica, ma anche un’immagine.

Nel proseguo di questo articolo vedremo come utilizzare questo framework per creare un semplice giochino che, mi auguro, vi sarà utile per comprendere in funzionamento di questo potente srumento di sviluppo.

Un piccolo giochino

Per creare il nostro primo gioco dobbiamo eseguire il seguente comando:

$ ./bin/lime.py create mygame

Il parametro passato dopo create non è nient’altro che il nome del progetto/gioco.

Nella nostra directory ora avremo una nuova cartella (mygame) con all’interno un file mygame.html e il suo corrispondente file Javascript chiamato mygame.js. L’esempio appena creato è già funzionante e ci consente di avere un primo assaggio della potenza di questo framework.

Il gioco che voglio realizzare insieme a voi è molto semplice e magari non vi entusiasmerà per originalità, ma serve per farvi prendere dimestichezza con LimeJS. Il gioco è semplicissimo e si basa su tre tre componenti principali:

  • un’area di gioco ben definita;
  • una barra che scorre orizzontalmente e che rappresenta il nostro player;
  • una pallina che rimbalza a gran velocità su tutta l’area di gioco;

Lo scopo del giocatore è non far cadere mai la pallina per terra, utilizzando la barra messa a disposizione.

Esempio di gioco creato con LimeJS

UNa demo del gioco che andremo a creare è disponibile a questa pagina.

Ho scelto di creare questo piccolo gioco ispirandomi all’ottimo articolo su questo sito, in cui veniva invece creato un semplice ping pong.

La scena

Per creare la scena su cui si basa il nostro gioco partiremo dal codice già presente nel file mygame.js. Di questo file in realtà non ci serve tutto, quindi modifichiamo l’entrypoint come di seguito:

// entrypoint
mygame.start = function(){

  var director = new lime.Director(document.body,640,480);
  var scene = new lime.Scene();

  var floor = new lime.Layer().setPosition(0,0);
  var walls = new lime.Layer().setPosition(0,0);
  var board = new lime.Layer().setPosition(0,0);

  scene.appendChild(floor_);
  scene.appendChild(walls_);
  scene.appendChild(board_);

  director.makeMobileWebAppCapable();

  // set current scene active
  director.replaceScene(scene);
}

Come possiamo vedere abbiamo definito il director e l’area di gioco in una dimensione di 640×480. A questo abbiamo appeso una scena, che sarà anche l’unica del nostro gioco. Successivamente si sono definiti i diversi livelli del gioco, ovvero lo sfondo, le mura laterali e l’area in cui la pallina si muoverà e avverranno tutte le azioni. Ognuno di questi è stato appeso alla scena.

Il player

Come annunciato il giocatore muoverà una barra posizionata in basso. La dinamica e la logica del gioco verranno introdotte successivamente, ora ci occupiamo solo della parte statica. Creiamo quindi questa barra e posizioniamola in basso al centro della nostra area di gioco. Per poter modulare meglio il gioco è opportuno inserire tutte le istruzioni che riguarderanno il "player" dentro un file apposito, denominato appunto player.js.

goog.provide('mygame.Player');
goog.require('lime.Sprite');

mygame.Player = function() {
  goog.base(this);
  this.setSize(100,20).setFill('#463E41'); // barra
}
goog.inherits(mygame.Player, lime.Sprite);

La disponibilità di questo file (player.js) come risorsa è effettuata tramite la prima istruzione:

goog.provide('mygame.Player');

che consentirà di richiamare questo file nel file principale tramite un’apposita require. Come si vede la definizione della barra è fatta tramite una Sprite, a cui è stata data una dimensione di 100×20 e un colore (personalizzabile). Io ho voluto utilizzare un rettangolo per definire questo oggetto, ma si possono utilizzare anche forme irregolari come poligoni o immagini. Nella definizione del Player non abbiamo definito il posizionamento, cosa che faremo nel file principale:

....
goog.require('mygame.Player'); // includo player.js

....
player = new mygame.Player().setPosition(320,450);

....
board_.appendChild(player);

....

Ovviamente una volta definito, deve essere aggiunto al livello board che conterrà i nostri "attori".

Possiamo già vedere un primo risultato (la sola barra) se accediamo al file mygame.html. Prima di accedervi è però necessario ricordare di aggiornare il progetto tramite il comando:

$ ./bin/lime.py update

La pallina

Ora passiamo alla creazione della pallina che sarà il fulcro del nostro gioco. La sua creazione è molto semplice e anche qui utilizzeremo un file denominato ball.js che conterrà tutte le proprietà di questo oggetto. Il file in questione sarà, almeno per ora, strutturalmente simile al precedente, a parte il fatto che si è utilizzato un cerchio invece di un rettangolo:

goog.provide('mygame.Ball');
goog.require('lime.Circle');

mygame.Ball = function() {
  goog.base(this);
  this.setFill(255,0,0,.7).setSize(20,20);
}
goog.inherits(mygame.Ball,lime.Circle);

Aggiungiamo quindi questa pallina alla nostra scena:

....
goog.require('mygame.Ball'); // includo ball.js

....
ball = new mygame.Ball().setPosition(320,240);

....
board_.appendChild(ball);

....

Il risultato che otterremo a video è una barra marroncina con una pallina rossa. Non molto entusiasmante, ma presto migliorerà…

Le ultime cose statiche da inserire prima di passare alla logica del gioco sono lo sfondo e le mura laterali.

Lo sfondo ed i bordi

Per quanto riguarda lo sfondo, basta che scegliamo un colore che vogliamo e l’inserimento è fatto tramite una semplice Sprite:

....
floor_.appendChild(
  new lime.Sprite().setPosition(320,240).setSize(640,480).setFill(200,200,200)
);
....

Per quanto riguarda invece le mura il discorso è differente. L’idea è di creare una sorta di effetto mattoncino (ben visibile in Chrome, meno in FF), ovvero una serie di elementi (oggetti distinti) raggruppati a formare il muro esterno. Per questo si è creato anche in questo caso un file esterno denominato appunto wall.js in cui si farà riferimento ad uno di questi "mattoncini".

goog.provide('mygame.Wall');
goog.require('lime.Sprite');

mygame.Wall = function() {
  goog.base(this);
  this.setFill(140,57,44).setSize(20,20);
}
goog.inherits(mygame.Wall, lime.Sprite);

Anche in questo caso l’elemento utilizzato è una Sprite, di dimensioni 20×20 e anche qui di colore personalizzabile. Aggiungiamo quindi i vari mattoncini nella scena a loro dedicata attraverso due cicli che mirano a distribuire i mattoncini in orizzontale ed in verticale.

....    
goog.require('mygame.Wall'); // includo wall.js

....
floor_.appendChild(
  new lime.Sprite().setPosition(320,240).setSize(640,480).setFill(200,200,200)
);

.....
// orizzontale
for (x = 10; x <= 630; x += 20) {
  walls_.appendChild(new mygame.Wall().setPosition(x, 10));
  walls_.appendChild(new mygame.Wall().setPosition(x, 470));
}   
                               
// verticale                 
for (y = 30; y <= 450; y += 20) {  
  walls_.appendChild(new mygame.Wall().setPosition(10, y));
  walls_.appendChild(new mygame.Wall().setPosition(630, y));
}
....

Come si può vedere ogni ciclo non fa nient’altro che scorrere lungo la sua dimensione (X e Y) e variare la posizione del mattoncino.

Logica della giocatore

Passiamo finalmente a vedere come realizzare la logica che sta dietro al funzionamento di questo gioco, in particolare ora mi soffermerò sulla logica del giocatore, ovvero della barra e su tutti i movimenti e le azioni che questa deve compiere.

Il comportamento che vogliamo ottenere è che la barra si muova orizzontalmente al click del mouse o al touch sullo schermo. Oltre a questo comportamento, la barra deve stare attenta a non oltrepassare la sua area di movimento, ovvero non deve andare oltre le mura (si dovrà quindi definire un rettangolo in cui questa può muoversi).

mygame.Player.prototype.setMovementBounds = function(top,right,bottom,left) {
  this._moveBounds = new goog.math.Box(top,right,bottom,left);
  return this;
}

mygame.Player.prototype.alignBounds = function(x, y) {
  if (this._moveBounds === undefined) return new goog.math.Coordinate(x, y);
  var size_ = new goog.math.Size(this.getSize().width * this.getScale().x,this.getSize().height * this.getScale().y);
  var newX = x, newY = y;
  if (x < (this._moveBounds.left + (size_.width / 2)))
    newX = this._moveBounds.left + (size_.width / 2);
  if (x > (this._moveBounds.right - (size_.width / 2)))
    newX = this._moveBounds.right - (size_.width / 2);
  if (y < (this._moveBounds.top + (size_.height / 2)))
    newY = this._moveBounds.top + (size_.height / 2);
  if (y > (this._moveBounds.bottom - (size_.height / 2)))
    newY = this._moveBounds.bottom - (size_.height / 2);
  return new goog.math.Coordinate(newX, newY);
}<

Questi due metodi inseriti nel file player.js permettono rispettivamente di impostare il rettangolo di gioco (setMovementBounds) e di recuperare la posizione durante il movimento (alignBounds). Come potete vedere si fanno uso di Oggetti e metodi tipici del framework, ma che sono molto semplici da capire nel loro significato.

Il primo dei due metodi (setMovementBounds) dovrà essere accodato alla definizione del player:

player = new mygame.Player().setPosition(320,450).setMovementBounds(440,620,460,20);

mentre il secondo verrà utilizzato nel definire il movimento della barra stessa al verificarsi di specifici eventi (click del mouse o tap sullo schermo in caso di dispositivo touch screen).

goog.events.listen(floor_,['mousedown','touchstart'],function(e){
  var player_ = player
  player_.runAction(
    new lime.animation.MoveTo(
      player_.alignBounds(e.position.x,player_.getPosition().y)
    ).setDuration(0.5)
  );
});

Con l’aggiunta di queste nuove funzionalità possiamo vedere che la nostra barra si muoverà lungo lo schermo rispettando i vincoli impostati. Ovviamente potete cambiare a piacimento cambiando il valore della durata dell’animazione, variando quindi la velocità della barra.

Logica della pallina

Passiamo ora alla definizione dei comportamenti della pallina. Le regole che la pallina deve rispettare sono essenzialmente tre:

  1. muoversi dentro l’area di gioco;
  2. essere dotata di una sua velocità;
  3. resettare la propria posizione quando il giocatore manca di colpirla.

Ovviamente tutti questi comportamenti andranno inseriti nel file ball.js. La definizione della pallina cambierà nel modo che vediamo qui sotto, in quanto andiamo ad inserire dei parametri che ci serviranno per impostare la velocità e la posizione iniziale.

mygame.Ball = function() {
  goog.base(this);
  this.setFill(255,0,0,.7).setSize(20,20);

  this._xCoef = 1;
  this._yCoef = 1;

  this._resetPos = new goog.math.Coordinate(0, 0);
  this._velocity = 2;
}
goog.inherits(mygame.Ball,lime.Circle);

A questo punto inseriamo le caratteristiche che ho elencato sopra:

mygame.Ball.prototype.setMovementBounds = function(top,right,bottom,left) {
  this._moveBounds = new goog.math.Box(top,right,bottom,left);
  return this;
}

mygame.Ball.prototype.setVelocity = function(velocity) {
  if (velocity) this._velocity = velocity;
  return this;
}

mygame.Ball.prototype.setResetPosition = function(x, y) {
  this._resetPos = new goog.math.Coordinate(x, y);
  return this;
}

Il primo metodo (setMovementBounds) definisce, come per il giocatore, qual’è l’area di gioco in cui si muoverà la pallina. Il secondo (setVelocity) ed il terzo metodo (setResetPosition) servono rispettivamente per impostare la velocità e la posizione di start della pallina. Tutte queste proprietà dovranno essere aggiunte alla definizione della pallina nel file mygame.js:

ball = new mygame.Ball().setPosition(320,240)
       .setMovementBounds(20,620,460,20)
       .setVelocity(.2)
       .setResetPosition(320,240);

Nel nostro giochino, ovviamente, ci dovrà essere una procedura che identifica se il giocatore effettivamente ha colpito la pallina e che, eventualmente reindirizzi la stessa verso l’area di gioco. Si dovrà quindi simulare quello che è il rimbalzo della pallina sulla barra. A tal proposito bisognerà modificare sia il file ball.js sia il file palyer.js. Nel file con le logiche della pallina inseriremo il seguente codice:

mygame.Ball.prototype.updateAndCheckHit = function(dt,player) {
  var newPos_ = this.getPosition();
  var size_ = new goog.math.Size(this.getSize().width * this.getScale().x,
                                   this.getSize().height * this.getScale().y);
  newPos_.x += this._xCoef * this._velocity * dt;
  newPos_.y += this._yCoef * this._velocity * dt;
  var hitVBounds_ = false; // vertical bounds were hit
  var hitOBounds_ = false;
  if (this._moveBounds !== undefined) {
    if (newPos_.x <= (this._moveBounds.left + (size_.width / 2))) { this._xCoef = 1; }
    if (newPos_.x >= (this._moveBounds.right - (size_.width / 2))) { this._xCoef = -1; }
    if (newPos_.y <= (this._moveBounds.top + (size_.height / 2))) { this._yCoef = 1; }
    if (newPos_.y >= (this._moveBounds.bottom - (size_.height / 2))) { this._yCoef = -1; hitOBounds_ = true; }
  }
  var p1catched_ = player.catched(newPos_);
    
  if (hitOBounds_ && !p1catched_) {
    this.setPosition(this._resetPos.x,this._resetPos.y);
    return newPos_;
  }
  else if (p1catched_) { this.xCoef = 1; return null; }
  this.setPosition(newPos_.x, newPos_.y);
  return null;
}

Tramite questo codice vengono assolti diversi compiti:

  • vengono calcolate le nuove coordinate della pallina derivanti dal movimento;
  • viene monitorato il contatto eventuale tra la pallina e la barra ed eventualmente anche il reset della posizione della pallina stessa (pallina finita nel muro in basso).

Come si può notare bisogna fare un check se la pallina ha effettivamente "sbattuto" contro il player: questa logica è inserita nel player, in cui semplicemente si confrontano quelle che sono le posizioni dei due oggetti in questione:

mygame.Player.prototype.catched = function(pos) {

  var ballP = pos;
  var inPoly = false;

  var playerPos = this.getPosition();
  var s = this.getScale();

  var checkY = playerPos.y-(this.getSize().height/2);
  var checkXMin = playerPos.x-(this.getSize().width/2);
  var checkXMax = playerPos.x+(this.getSize().width/2);

  if (ballP.x>=checkXMin && ballP.x<=checkXMax && ballP.y >= checkY) inPoly = true;

  return inPoly;
}

Il ritorno di questa funzione è vero se la pallina è nel poligono che rappresenta la barra, falso in caso contrario.

Perchè la pallina si muova bisogna utilizzare lo scheduleManager, citato nell’introduzione di questo articolo. Il codice qui di seguito deve essere inserito nel file mygame.js, subito dopo la definizione del listener contenente l’animazione del player:

goog.events.listen(floor_,['mousedown','touchstart'],function(e){
  .......
});
.....
lime.scheduleManager.schedule(function(dt){
 // SCRITTA DI GAME OVER
},ball);

Messaggio di Game Over

Per concludere degnamente il nostro gioco dobbiamo inserire il messaggio di Game Over visualizzato quando la pallina non viene presa in tempo dal giocatore. Se avete diligentemente copiato il codice precedente, al posto del commento, va messo quello che visualizzeremo. Prima di questo però dobbiamo definire l’oggetto Label, ovvero l’etichetta dove verrà mostrato tale messaggio (ciò va fatto all’interno di mygame.js).

label = new lime.Label().setPosition(320,180)
        .setText('').setFontFamily('Verdana')
        .setFontColor('#E42217').setFontSize(24)
        .setFontWeight('bold').setSize(200,30);

Ovviamente siete liberi di inserirla dove volete, i parametri sono intuibili e non necessitano di spiegazioni. Per quanto riguarda invece il completamento dello scheduler invece bisogna procedere in questo modo:

var hitPos_,defDelay_ = 1000, delay_ = defDelay_;
lime.scheduleManager.schedule(function(dt){
  delay_ -= dt;
  if (delay_ <= 0){ 
    label.setText('');
    if (hitPos_ = ball.updateAndCheckHit(dt, player)) {
      label.setText('HAI PERSO !!!');
      delay_ = defDelay_;
    }
  }
},ball);

Come possiamo vedere abbiamo introdotto alcuni parametri in modo da simulare il ritardo, ovvero quanto tempo deve rimanere esposto il messaggio prima che il gioco possa ricominciare. Potete tranquillamente giocare su questo parametro a vostro piacimento.

Compilazione del file Javascript

Terminato il nostro progetto sicuramente vorremo distribuirlo. LimeJS mette a disposizione degli automatismi per creare direttamente il file Javascript necessario, oltretutto già compresso. I comandi da lanciare sono i seguenti, con ovviamente le dovute personalizzazioni:

$ ./bin/lime.py update
$ ./bin/lime.py build mygame -o mygame/compilato/mygame.js

A questo punto troverete il file Javascript mygame.js nel percorso da voi indicato. Questo file conterrà tutto il necessario per far partire il vostro gioco e quindi basterà includere solo questo file (togliendo tutte gli altri) nella propria pagina HTML ed il gioco è fatto !!!!

Se siete interessati potete scaricare il sorgente completo del nostro gioco cliccando qui.

Pubblicitร 

Leggi anche...

Infinite scroll, come programmarlo su AMP e su Web con Javascript

L'infinite scroll è una tecnica di design e navigazione...

Codice Fiscale: 5 javascript per la verifica e il calcolo

Il codice fiscale รจ un identificativo tributario univoco che...

Math.ceil() – Arrotondare per eccesso con Javascript

Il metodo ceil() dell'oggetto Math di Javascript è utilizzato...

Minificare Javascript: librerie e strumenti online per comprimere il sorgente JS

La minificazione è un processo abbastanza diffuso nell'implementazione delle...

Javascript: svuotare un campo input o una textarea con un click

Quando si fornisce agli utenti un modulo per l'inserimento...

6 video player HTML5 per il tuo sito web

Con il rilascio delle specifiche definitive per HTML5 molte...
Pubblicitร