LimeJS è un framework JavaScript che ci consente di creare in maniera rapida dei giochi in HTML5 per desktop e telefoni o dispositivi touch, permettendoci di realizzare applicazioni dalle funzionalità pressochè native ma dai vantaggi di portabilità tipici di HTML5. In sostanza, utilizzando queste librerie JavaScript avremo la possibilità di scrivere giochi – puzzle, giochi di abilità ma anche platform – che gireranno su tutti i browser e i device che supportano HTML5, dai browser desktop fino all'iPhone, Android e iPad.

Vediamo in questo tutorial introduttivo come procedere passo passo nell'installazione e nel primo utilizzo di LimeJS.

Installazione

L'ambiente di sviluppo ideale per LimeJS è Linux/Unix, per Windows sono disponibili le istruzioni di installazione nel file README distribuito con la libreria.

Per installare il framework è necessario avere Python 2.6+, Git e Subversion o Git-SVN. Se si desidera usare Closure Compiler è necessario anche Java. Per cominciare, dopo aver scaricato il tutto da github, basta lanciare un

bin/lime.py init

e questo dovrebbe scaricare e configurare tutti i componenti necessari. A questo punto possiamo creare il nostro primo progetto con un  bel

bin/lime.py create helloworld

e goderci la nostra creazione in helloworld/helloworld.html

 

La Timeline

Una volta che il framework è installato possiamo procedere con al creazione dell'elemento principale del nostro gioco, ovvero il Director:

var director = new dfkit.Director(document.body,320,460);

Questo è l'oggetto che collega e ci consente di gestire tutta la logica di LimeJS ed è dunque la base essenziale per il nostro gioco.

Con il director possiamo ad esempio creare e gestire delle "scene" (le schermate diverse del gioco: il menu iniziale, il gioco vero e proprio, la schermata di "game over") che facciamo apparire e scomparire (anche usando delle transitions) così:

director.replaceScene(menuscene,lime.transition.SlideInRight);<br />    director.replaceScene(gamescene,lime.transition.Dissolve,2);

Per gestire invece movimenti ed eventi che si ripetono (ad esempio il muoversi della nostra navicella) non dobbiamo usare le funzioni native JavaScript tipo setTimeout() ma piuttosto lo ScheduleManager di LimeJS, così:

var velocity = 2;<br />    lime.scheduleManager.schedule(function(dt){<br />        var position = this.getPosition();<br />        position.x += velocity * dt; // if dt is bigger we just move more<br />        this.setPosition(position);<br />    },ball);

Questo ci consente di avere a disposizione una serie di funzionalità (ad esempio il parametro dt che varia al variare della velocità e livello di occupazione della CPU) per garantire un movimento fluido anche quando abbiamo pochi cicli di CPU a disposizione.

Infine abbiamo a disposizione la gestione dei layer che possono essere immaginati proprio come i layer di Photoshop:

var layer = new lime.Layer().setPosition(100,100);<br />    scene.appendChild(layer);<br />    <br />    for(var i=0;i<5;i++){<br />        var box = customMakeBoxFunc().setPosition(i*50,0);<br />        layer.appendChild(box);<br />    }

 

Layout e nodi

Il layout del nostro gioco (orientamento, dimensioni, fullscreen) viene gestito direttamente da LimeJS. Noi non dobbiamo far altro che specificare le dimensioni dello stage (ad esempio nella creazione del Director) e sarà il framework ad occuparsi poi di gestirne le effettive dimensioni a seconda del device o farlo apparire un fullscreen o in un DIV.

I nodi, invece, sono l'oggetto fondamentale di LimeJS dal momento che questa classe definisce le funzionalità comuni per il display di un oggetto. I nodi possono venire creati (anche specificandone le dimensioni) e possono essere posizionati, ridimensionati o ruotati:

var parent = new lime.Node();<br />    var child = new lime.Node();<br />    parent.appendChild(child);<br />    <br />    var node = new lime.Node().setSize(50,50);<br />    <br />    var size = node.getSize(); // returns object<br />    size.width+=100;<br />    node.setSize(size);<br />    <br />    node.setScale(.7); //scale in both axis<br />    <br />    var pos = node.getPosition(); // return object<br />    pos.y = 150;<br />    node.setPosition(pos);<br />    <br />    var node = new lime.Node().setRotation(90);

 

Forme, sprites e riempimenti

Sebbene abbiamo detto che il nodo è l'oggetto di base di LimeJS, in reatà noi raramente avremo a che fare direttamente con questo oggetto ma ne utilizzeremo piuttosto l'incarnazione più utile sprite. Com'è facile immaginare uno sprite è un oggetto che può avere un aspetto grafico e che possiamo spostare, ruotare e modificare a nostro piacimento:

var redsquare = new lime.Sprite().setSize(50,50).<br />        setFill('#c00').setAnchorPoint(0,0);<br />    layer.appendChild(redsquare);

E' da notare che per il fill può essere indicata un'immagine, dandoci così la possibilità di creare uno sprite in piena regola (ad esempio, la nostra navicella spaziale in viaggio nelle profondità siderali):

sprite.setFill('assets/navicella.png');

 

Eventi

La gestione degli eventi in LimeJS è molto simile a quella nella libreria Closure e cioè ad esempio

goog.events.listen(ball,['mousedown','touchstart'],function(e){<br />        this.setFill('#c00'); // la palla diventa rossa quando è toccata<br />    });

per associare uno o più eventi ad una funzione e

goog.events.unlisten

per rimuovere la funzione dall'evento.

Una delle differenze di LimeJS è che nella funzione di callback richiamata al verificarsi dell'evento invece del solito oggetto noi riceveremo un oggetto di tipo lime.events.Event che ci consente di avere informazioni sulla posizione (sia relativa allo spazio dell'oggetto che dello schermo), sull'oggetto e sull'evento avvenuto.

LimeJS ci consente inoltre di gestire in maniera semplice il drag aggiungendo il metodo startDrag all'oggetto interessato:

goog.events.listen(ball,['mousedown','touchstart'],function(e){<br />        e.startDrag(true); // true is for snapToCenter<br />    });

 

Animazioni

Le animazioni ci consentono di variare alcune caratteristiche di un oggetto (dimensioni, posizione, aspetto) nel tempo. Le animazioni vengono prima definite e poi eseguite:

var fadehalf = new lime.animation.FadeTo(.5).setDuration(2);<br />    ball.runAction(fadehalf);

Non dimenticando che dobbiamo caricare le animazioni che ci interessano con un goog.require() visto che nessuna animazione viene caricata di default, possiamo usare le seguenti tipologie:

  • MoveBy: sposta un oggetto rispetto alla posizione attuale
  • MoveTo: sposta un oggetto in un punto specifico
  • ScaleBy: ridimensiona (ad esempio passando "2" come argomento lo fa diventare grande il doppio)
  • ScaleTo: ridimensiona ad una scala specifica
  • RotateBy: ruota di…
  • RotateTo: ruota ad un certo angolo
  • ColorTo: cambia colore
  • FadeTo: fading

Per accorgersi quando una animazione ha finito si può intercettare l'evento "stop":

var moveleft = new lime.animation.MoveBy(-100,0);<br />    ball.runAction(moveleft);<br />    goog.events.listen(moveleft,lime.animation.Event.STOP,function(){<br />        alert('La palla ha finito di muoversi');<br />    })

 

Diverse modalità di rendering

Una delle cose più interessanti di LimeJS è la possibilità di specificare come fare il rendering degli oggetti. Sarebbe errato considerarlo solo un engine Canvas, infatti, dal momento che con un semplice

setRenderer(renderer)

possiamo dire all'engine di usare uno dei due metodi di rendering, e cioè:

  • lime.Renderer.DOM: in questo caso vengono creati dei DIV nel DOM e le animazioni vengono gestite tramite CSS. E' il metodo ideale ad esempio per iOS che è molto lento con il Canvas mentre è estremamente efficiente nella gestione delle trasformazioni CSS
  • lime.Renderer.CANVAS: viene creato un canvas 2D su cui vengono disegnati e mossi tutti gli oggetti. E' la soluzione ideale per browser che non supportano le trasformazioni CSS3 (ad esempio IE fino a IE9 beta) o dove abbiamo una CPU sufficientemente veloce da consentirci una gestione fluida e piacevole del gioco

 

Compilare

Una volta finito il nostro progetto possiamo "compilarlo", ovvero renderlo più compatto e distribuibile (non è ovviamente una buona idea distribuire tutti i nostri file così come ci abbiamo lavorato). Basta allora un semplice:

bin/lime.py build helloworld -o helloworld/compiled/hw.js

e avremo un file JavaScript pronto per essere distribuito

 

Conclusioni

In questo tutorial introduttivo abbiamo voluto affrontare in maniera molto rapida gli aspetti salienti di questo framework per lo sviluppo di giochi cross-device in HTML5 di cui ci piacciono alcune soluzioni (ad esempio i diversi motori di rendering) e che secondo noi può essere interessante per tutti i game developer in ascolto. Ovviamente vi consigliamo vivamente di studiarvi con attenzione la documentazione ufficiale e di partire dal sito http://www.limejs.com/

Happy coding!