Nella vita professionale di uno sviluppatore accade prima o poi di affrontare la gestione del tempo. Sia che si lavori ad un gioco o ad un'applicazione infatti, può capitare di dover avviare un evento solo in un momento prestabilito, o ad intervalli regolari, o ancora dopo un certo numero di secondi da quando l'utente ha compiuto una determinata azione. Flash mette a disposizione alcuni comandi che, direttamente o combinati con altri, consentono di gestire il tempo all'interno del filmato SWF:
getTimer
(disponibile da Flash 4)setInterval
(disponibile da Flash Mx in poi)setTimeout
(funzione non documentata di Flash 8)
Rispettivamente, getTimer
restituisce i millisecondi trascorsi dall'inizio del filmato, setInterval
imposta un intervallo per cui ogni x
secondi verrà chiamata una funzione, mentre setTimeout
imposta un intervallo per cui dopo x
secondi la funzione verrà chiamata una sola volta.
In alcuni casi, specialmente in combinazione con getTimer, viene utilizzato anche il comando onEnterFrame
Questi comandi consentono di fare praticamente qualsiasi cosa con il tempo all'interno di Flash: li analizzeremo uno ad uno, proponendo qualche esempio pratico di utilizzo, iniziando dal più "anziano": getTimer
.
Il comando getTimer
È il comando che garantisce la maggior compatibilità, infatti è disponibile sin dalla versione 4 del Flash Player, ed è in grado di restituire il numero di millisecondi trascorsi dall'inizio del filmato. Poichè non è possibile impostare un "punto di avvio" del conteggio, questa funzione potrebbe apparire piuttosto limitata, ma con pochi accorgimenti si rivela utile in molteplici occasioni.
Sintassi di getTimer()
//Associamo ad una variabile il valore restituito da getTimer
variabile = getTimer();
Un semplice esempio
Vogliamo mostrare in un campo di testo il numero di secondi trascorsi dall'inizio del filmato. Per prima cosa creiamo il campo di testo dinamico (con nome istanza "tempo") sullo stage, quindi nelle azioni del primo fotogramma scriviamo:
_root.onEnterFrame = function(){
tempo.text = int(getTimer()/1000) + " secondi"
}
Ad ogni fotogramma della timeline principale verrà aggiornato il campo di testo "tempo", e vi sarà scritto il valore di getTimer
(diviso per 1000, per ottenere i secondi, e arrotondato all'intero) accompagnato dalla scritta "secondi".
Abbiamo utilizzato il comando onEnterFrame
per rendere l'azione continua: un altro metodo usato per tale scopo, specialmente in versioni precedenti a Flash Mx, era quello di creare due fotogrammi sulla timeline, con l'azione da ripetere sul primo fotogramma, in modo che il filmato ripartisse di continuo e ri-eseguisse l'azione. L'utilizzo di un EnterFrame
è decisamente più comodo, quindi assolutamente consigliato per chi abbia versioni di Flash superiori alla 5.
Modificare un oggetto nel tempo
Possiamo andare ben oltre al semplice scrivere all'interno di un campo di testo. Supponiamo di voler variare l'aspetto di un oggetto -ad esempio la sua trasparenza- in base al tempo trascorso dall'inizio del filmato.
Creiamo un movieclip (nome istanza "clip") sulla scena, e nelle azioni inseriamo questo codice:
clip.onEnterFrame = function(){
tempo = getTimer()/1000;
this._alpha -= tempo;
}
L'oggetto "clip" sparirà gradualmente. In questo caso l'enterFrame
è stato associato direttamente al movieClip da far sparire.
Impostare l'avvio del timer
Se è vero che non è possibile impostare il momento in cui parte del conteggio del tempo, possiamo sempre stabilirlo usando un tempo "relativo" ad un certo istante: memorizziamo in una variabile l'istante iniziale lo sottraiamo ai valori ottenuti da getTimer
. Otteniamo i millisecondi trascorsi dall'impostazione dell'istante iniziale.
Creiamo sullo stage un pulsante (nome istanza "puls"). Questo pulsante imposterà il punto di partenza del nostro timer; ecco il codice da utilizzare sul primo fotogramma della linea temporale:
_root.onEnterFrame = function(){
if(partenza){
if(getTimer() - partenza >= 2000){
trace("Booom");
delete this.onEnterFrame;
}
}
}
puls.onRelease = function(){
partenza = getTimer();
}
Anche in questo caso sfruttiamo un EnterFrame
: per prima cosa controlliamo che la variabile "partenza" sia stata impostata, nel qual caso rileviamo il valore di getTimer()
, vi sottraiamo quello di "partenza" e, se il risultato è maggiore o uguale a 2000 (ovvero 2 secondi dalla pressione del pulsante), tracciamo "Booom" ed eliminiamo l'onEnterFrame
.
La variabile partenza viene impostata alla pressione del pulsante "puls", ed assume il valore di getTimer()
: in questo modo fa da "punto di partenza" per il "conto alla rovescia" che abbiamo impostato nell'enterFrame
. Premendo di continuo il pulsante "puls" l'azione nell'enterFrame
non si verifica, in quanto continuiamo a spostare il punto di riferimento per il conteggio.
Creare un conto alla rovescia
Se nell'esempio precedente abbiamo calcolato il tempo trascorso dalla pressione del pulsante in poi, quindi un intervallo "a salire", possiamo facilmente immaginare che per eseguire un conto alla rovescia non dovremmo fare un'operazione del tipo getTimer()
- partenza, ma l'inverso. Ecco allora un esempio di codice per creare un conto alla rovescia:
// Secondi per il countdown
inizio = 10;
// ------------------------
inizio *= 1000;
_root.onEnterFrame = function(){
if(inizio - getTimer() > 0){
tempo.text = Math.round((inizio-getTimer())/1000);
}else{
tempo.text = "Tempo scaduto";
delete this.onEnterFrame;
}
}
Impostiamo subito il valore iniziale del countdown (10 secondi), lo moltiplichiamo per 1000 (lo trasformiamo in campo di testo "tempo". Finito il conteggio rimuoviamo l'enterFrame e impostiamo il testo a "Tempo scaduto".
Gli esempi fatti usando getTimer()
si possono realizzare più semplicemente da Flash Mx in poi, grazie al comando setInterval()
.
Il comando setInterval
Questo comando è disponibile dalla versione 6 del Flash Player, e permette di richiamare una determinata funzione ogni x
secondi. È anche possibile passare dei parametri alla funzione.
Sintassi di setInterval
setInterval(funzione,intervallo,parametri)
Possiamo associare il comando ad una variabile:
variabile = setInterval(funzione,intervallo,parametri)
in modo da poter poi usare il comando clearInterval(variabile)
per eliminare l'intervallo stabilito.
Come abbiamo detto, con setInterval
possiamo riprodurre alcuni degli esempi fatti per getTimer
, li analizziamo per verificare i vantaggi e gli aspetti negativi della sostituzione.
Mostrare il tempo trascorso in un campo di testo
Il primo esempio mostrava il numero di secondi trascorsi dall'inizio del filmato. Avevamo impostato sulla timeline principale un enterFrame
che scriveva nel campo di testo il valore (diviso per 1000) di getTimer
. Ecco la versione con setInterval
:
secondi=0;
function timer() {
secondi++;
tempo.text = secondi + " secondi";
}
setInterval(timer,1000);
Ad un primo sguardo potrebbe sembrare una soluzione "peggiore", poichè richiede più righe di codice. Analizziamo però queste righe: ogni 1000 millisecondi (quindi ogni secondo) viene chiamata la funzione timer, che aumenta di una unità il valore della variabile "secondi" e la mostra nel campo di testo. La funzione viene eseguita solo ogni secondo, e non "di continuo" come avveniva invece con il getTimer
, di conseguenza ottimizziamo elaborazione e memoria.
Mutare l'aspetto di un oggetto
In questo caso il solo setInterval
non ci permette di ottenere lo stesso risultato visto in precedenza, perchè il getTimer
faceva anche da valore di riferimento per la diminuzione dell'alpha del movieclip. Non possiamo rilevare tale valore col solo setInterval
, ma possiamo al ottenere un risultato visivamente simile.
scala=0;
function scala_alpha(){
scala += 1;
clip._alpha -= scala;
}
setInterval(scala_alpha, 60);
Poichè getTimer
veniva usato per impostare un valore che aumentava di volta in volta, il risultato è solo simile, ma non uguale. Inoltre la necessità di impostare un intervallo di tempo piuttosto basso fa decadere i vantaggi rispetto all'enterFrame
.
Impostare l'avvio del timer
Questo è sicuramente tra i vantaggi maggiori che setInterval
offre rispetto a getTimer
: è infatti possibile impostare l'intervallo e l'istante iniziale in qualsiasi momento, pertanto non sono necessari calcoli o punti di riferimento, basta associare setInterval
all'evento desiderato e impostare l'intervallo da attendere prima di scatenare l'azione.
function traccia() {
trace("Boooom");
clearInterval(a);
}
puls.onRelease = function() {
a = setInterval(traccia, 2000);
}
I vantaggi sono molteplici: non utilizziamo un enterFrame
, quindi risparmiamo memoria (in maniera notevole: nell'esempio con getTimer
l'enterFrame
era attivo già prima della pressione del pulsante), non dobbiamo impostare un controllo sull'esistenza della variabile e non dobbiamo inserire calcoli tra due o più valori.
Il tempo da far passare tra la pressione del pulsante e l'esecuzione dell'azione è impostato in setInterval
: è stata impostata una variabile di riferimento (a
) da sfruttare per la rimozione dell'intervallo, che viene eseguita nella funzione traccia()
, per evitare la ripetizione dell'azione ogni due secondi.
Il numero di righe di codice in questo caso è anche inferiore al corrispondente esempio con getTimer
, ed otteniamo esattamente lo stesso risultato. Volendo potremmo anche impostare come parametro il valore da tracciare.
function traccia(cosa){
trace(cosa);
clearInterval(a);
}
puls.onRelease = function(){
a = setInterval(traccia,2000,"Bum bum bum");
}
Creare un conto alla rovescia
Anche in questo esempio il codice cambia in maniera abbastanza radicale, principalmente per la possibilità di non utilizzare enterFrame
.
// Secondi per il countdown
inizio = 10;
// ------------------------
tempo.text = inizio
function countdown() {
inizio--;
if(inizio > 0) {
tempo.text = inizio;
} else {
tempo.text = "Tempo scaduto";
}
}
setInterval(countdown,1000);
Ogni secondo viene diminuito il valore della variabile "inizio", possiamo agire direttamente su di essa, senza dover eseguire sottrazioni e senza doverne convertire il valore in millisecondi. Visivamente il risultato è praticamente uguale a quanto già visto, ma anche in questo caso vi è un risparmio di memoria, inoltre abbiamo una maggiore versatilità: ad esempio possiamo decidere di diminuire il valore di "inizio" di più di una unità, oppure ad intervalli maggiori di un secondo, cambiando solo una riga di codice.
Problemi di setInterval
A giudicare da quanto abbiamo visto setInterval
sembra migliore di getTimer
: permette di risparmiare righe di codice in alcuni casi, quasi sempre consente un risparmio di memoria, e i risultati sono praticamente identici. Il limite di setInterval
è quello di avere una precisione minore di getTimer
. Quest'ultimo infatti restituisce un valore preciso in millisecondi, mentre gli intervalli di setInterval non sono poi così precisi. Possiamo verificarlo scrivendo poche righe di codice sulla linea temporale.
function traccia() {
trace(getTimer())
}
setInterval(traccia, 1000);
È lecito aspettarsi che i valori restituiti dal getTimer
siano 1000
, 2000
, 3000
, etc. Invece otterremo dei risultati come: 1076
, 2133
, 3188
, 4244
.
Non è un problema di durata degli intervalli, poichè anche impostando un intervallo di 10 millisecondi i valori restituiti da getTimer
sarebbero diversi. Certamente una discrepanza di circa 70 millisecondi non è così grave, nella maggior parte dei casi non da alcun problema ed è quasi impossibile accorgersene ad occhio, però qualora il progetto richiedesse una maggior precisione, questa discrepanza potrebbe creare dei problemi.
Il comando setTimeout
Questo è il comando più recente, disponibile da Flash Player 8 e risultante come funzione non documentata in Flash 8 (probabilmente sarà introdotto ufficialmente nella versione 9). Si rivela utile per azioni basate su intervalli di tempo ma da eseguire solo una volta, come nel nostro terzo esempio, in cui cercavamo il "punto di partenza" del timer.
Con getTimer
abbiamo usato il "delete onEnterFrame", mentre con setInterval
abbiamo sfruttato cleatInterval
per evitare che l'azione si ripetesse più di una volta. In questi casi il comando setTimeout
può semplificarci ulteriormente la vita, in quanto richiama una determinata funzione dopo x
secondi una sola volta.
Sintassi di setTimeOut
setTimeout(funzione,intervallo,parametri)
Dal nome, ma anche dalla "firma" (i parametri sono gli stessi) si intuisce che sia un "parente stretto" di setInterval
. Possiamo di fatto dire che il setTimeout è una "scorciatoia" dell'accoppiata setInterval
- clearInterval
nel caso in cui la funzione vada eseguita una volta sola, semplificando il codice in questi casi (come nell'esempio 3).
function traccia(cosa) {
trace(cosa);
}
puls.onRelease = function() { setTimeout(traccia,2000,"Bum bum bum") };
Purtroppo anche tale comando soffre della scarsa precisione di setInterval
.
Flash 9: la classe Timer
Come per molti altri comandi, anche quelli atti a gestire lo scorrere del tempo in Flash sembrano destinati a subire dei grossi cambiamenti con Actionscript 3 in Flash 9. Nel caso dei comandi getTimer
, setInterval
e setTimeout
, sembra che questi saranno sostituiti dalla classe Timer (nel package flash.utils
).
Usare la classe Timer
var tempo:Timer = new Timer(millisecondi, ripetizioni);
tempo.addEventListener("timer", funzione);
tempo.start();
In particolare il parametro "ripetizioni" è opzionale e stabilisce il numero di volte in cui andrà richiamata la funzione specificata nell'EventListener
. Il valore di default è 0, e comporta un funzionamento uguale a quello del setInterval
, ovvero un numero continuo di chiamate; qualora volessimo invece replicare il funzionamento del setTimeout
basterebbe impostare il numero di ripetizioni ad 1.
È possibile fermare il timer in ogni momento utilizzando il comando tempo.stop()
e reimpostarlo a zero utilizzando tempo.reset()
.
Si tratta insomma di una classe abbastanza versatile, con il vantaggio però di non dover utilizzare comandi diversi ma semplicemente variare un parametro per ottenere il risultato voluto.
Conclusioni
Flash mette a disposizione 3 comandi "dedicati" alla gestione degli intervalli di tempo, ognuno con le sue peculiarità e i suoi aspetti positivi e negativi; getTimer
è il più preciso, ma richiede quasi sempre un abbinamento con un enterFrame
e quindi si rivela più esoso in termini di memoria, setInterval
è il più versatile e consente un risparmio di memoria notevole in alcuni casi, ma soffre di scarsa precisione e setTimeout
si rivela prezioso per quelle situazioni dove la funzione va richiamata una volta sola ma sempre in base ad un intervallo di tempo, consente di risparmiare un po' di memoria rispetto all'accoppiata setInterval-clearInterval ma soffre anch'esso di piccole imprecisioni.
La classe Timer
infine, introdotta in Actionscript 3, appare decisamente versatile e semplice da utilizzare e può sostituire setInterval
, clearInterval
e setTimeout
. che rimarranno comunque disponibili all'interno della classe flash.utils
(così come getTimer).