Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Real time con ActionScript 3: onEnterFrame, Timer e scenari ulteriori

Gestire la temporizzazione evitando i ritardi causati dal rendering della scena
Gestire la temporizzazione evitando i ritardi causati dal rendering della scena
Link copiato negli appunti

Parlare di "real-time" in Flash/ActionScript è di rilevanza fondamentale: sia che vogliamo profilare una ruotine complessa in Papervision3D, sia che pensiamo alla temporizzazione della lettura di un feed RSS, avere codice da eseguire con una certa periodicità è caso d'uso quasi costante.

Alcune funzioni, utili a questo scopo, sono state introdotte con l'evoluzione di ActionScript e Flash Player, quando inizalmente erano pressoché inesistenti. Ciò permette oggi la gestione delle dinamiche temporali, negli ambiti applicativi più disparati.

Discostandoci dall'antichissimo approccio a 2 frame, (codice - gotoAndPlay() frame con codice), lo scenario attuale annovera 2 approcci che permettono la temporizzazione del codice:

  • l'uso del "classico" evento onEnterFrame, che si basa sul framerate dell'swf
  • l'utilizzo della classe Timer, che si aggiorna secondo su un valore in millisecondi specificato nel costruttore della classe

Introdurremo il concetto di drift temporale, indicando con esso la differenza accumulata tra il valore "nominale" da noi impostato per la temporizzazione e il tempo effettivo (tick duration), computato grazie ad alcune funzioni ActionScript.

onEnterFrame

Utilizzare il frame rate come base temporale non è sempre la scelta perfetta. In questo articolo Tinic Uro, uno degli ingegneri di Adobe, svela alcuni retroscena del Flash Player e dichiara che il frame rate di un swf può fluttuare tra 5-10fps, a seconda delle risorse occupate dal sistema operativo e/o dal browser.

Un esempio di questo lo possiamo avere, immaginando il classico loop di onEnterFrame:

import flash.utils.getTimer;

//variabile di drift
var cumulativeDrift = 0;
//system time
var lastTick = getTimer();

mc.addEventListener(Event.ENTER_FRAME, loop);

function loop(e:Event):void
{
  // FPS
  var fps	= this.stage.frameRate;
  
  // TICK
  var newTick = getTimer();
  var expectedTickDuration = 1000 / fps;
  var actualTickDuration = newTick - lastTick;
  lastTick = newTick;
  
  // DRIFT
  var tickDrift = actualTickDuration - expectedTickDuration;
  cumulativeDrift += tickDrift;
  var cumulativeDriftSeconds = cumulativeDrift / 1000;
  
  trace("drift=", cumulativeDrift, "fps=", fps, "actualTickDuration=", actualTickDuration);
}

La classe Timer

L'introduzione della classe Timer in ActionScript 3, ha rimpiazzato il vecchio metodo setInterval. Nel costruttore possiamo specificare l'intervallo temporale richiesto e, grazie al consueto addEventListener, una funzione da invocare all'evento di temporizzazione (o "tick event").

Come rileva l'esempio a seguire, anche questo modo non è immune, tra un intervallo e l'altro, da drift temporale. Ecco il codice per il test:

import flash.utils.getTimer;
import flash.utils.Timer;

//intervallo in millisecondi
var time:int=50;
//variabile di drift
var cumulativeDrift=0;
//system time
var lastTick=getTimer();

var timer:Timer=new Timer(time);
timer.addEventListener( TimerEvent.TIMER, onTick);
timer.start();

function onTick( event:TimerEvent ):void {
  // FPS
  var fps= time;
  
  // TICK
  var newTick=getTimer();
  var expectedTickDuration=time;
  var actualTickDuration=newTick-lastTick;
  lastTick = newTick;
  
  // DRIFT
  var tickDrift=actualTickDuration-expectedTickDuration;
  cumulativeDrift+=tickDrift;
  var cumulativeDriftSeconds=cumulativeDrift/1000;
  trace("drift=", cumulativeDrift, "time=", time, "actualTickDuration=", actualTickDuration);
}

TimeKeeper, una soluzione migliore ...

Nel sito www.computus.org, John Dalziel esplora varie tematiche correlate alla computazione temporale. Come astronomo nonchè sviluppatore Flash, John ha sviluppato una più efficiente soluzione al problema di a-periodicità nel Flash Player (altresì detto "isocronismo"), creando una classe TimeKeeper e il relativo TimekeeperEvent che possono convenientemente essere usati nelle nostre applicazioni.

La classe TimeKeeper dispone di un regolatore interno, implementato con classe Timer, operante alla frequenza di 20 volte al secondo: l'accuratezza nel calcolo del periodo è mantenuta da un accumulatore basato sul valore via via dato dal "clock" del regolatore: quando il valore dell'accumulatore supera la frequenza impostata (cioè la temporizzazione da noi richiesta) viene generato e propagato un "tick event".

Osservando più in dettaglio la classe TimeKeeper, ne individuiamo il punto fondamentale:

private function onTimerEvent( e:TimerEvent ):void
{
  var regulatorNew:Number = getTimer() // rileva time di sistema
  var regulatorDelta:Number = regulatorNew - regulatorCache	// calcola la differenza dall'ultimo tick
  regulatorAcc += regulatorDelta // incrementa accumulatore
  
  if ( regulatorAcc > _tickFrequency ) // se valore accumulatore supera frequenza di tick desiderata...
  {
    // imposta il valore del temporizzatore come tempo iniziale + durata del tick e propaga un tick event
    
    if ( _isTicking == true ) { value = time + _tickDuration } 
    
    // reset accumulator
    regulatorAcc -= _tickFrequency // reset accumulatore
  }
  regulatorCache = regulatorNew	// cache previous regulator	value
}

Il time base delle computazioni successive viene calcolato con un new Date().valueOf().

Riferimenti:

Ti consigliamo anche