AS3 - La "display list"

L'evoluzione dei MovieClip

Inserito il 13-12-2007

Premessa: le informazioni contenute nell'articolo sono frutto di una miscela tra l'interpretazione personale della documentazione ufficiale e la mia esperienza diretta. L'argomento è stato sviscerato per quanto possibile nella sua interezza, ma non basta da solo a garantire un'apprendimento completo, senza effettuare parecchie prove e leggere la documentazione di ogni classe del package flash.display, spero comunque che possa stabilire una buona base di partenza per chi intende imparare AS3.

Fino a Flash 8 con AS2, il compito di gestire gli elementi visibili all'interno di un filmato flash era assegnato unicamente ad un singolo oggetto complesso, facente capo alla classe astratta MovieClip. Tale classe raccoglieva in sé la possibilità di caricare file esterni al suo interno (SWF, JPEG, PNG e GIF), di disegnarci in runtime delle forme con i drawing methods e di attaccare/creare/duplicare altri elementi all'interno, gestendone il livello di "aggancio", la posizione e le altre proprietà tipiche di un elemento da visualizzare sullo schermo nel filmato swf. Lo stesso _root (o _level0) era un MovieClip con tutte le caratteristiche rappresentative di questo tipo di elemento.

Il problema della gestione inquadrata in un unico elemento di visualizzazione è evidenziato principalmente in uno spreco di risorse per la gestione di elementi semplici che potrebbero non necessitare di tutte le proprietà e i metodi presenti nella classe MovieClip, e richiamati ogni volta che si chiama in causa un oggetto di questo tipo.

Sulla base di questo problema, per fare in modo che il player gestisca più velocemente le applicazioni, in AS3 sono state introdotte una serie di classi separate, che gestiscono, a seconda del tipo, un numero di metodi e proprietà inferiore a quelle della classe MovieClip, e strettamente necessarie all'oggetto che intendono rappresentare.

Entrando nel dettaglio, grazie alla moltiplicazione degli elementi di visualizzazione, si possono riscontrare i seguenti vantaggi:

In AS3, la classe principale, da cui derivano, e di cui prendono i metodi, tutti gli elementi di visualizzazione, è la classe DisplayObject che contiene nella sua definizione, le proprietà basilari degli oggetti, come ad esempio alpha, x, scaleX, rotation eccetera.

Definiamo ora un elenco delle classi di visualizzazione utilizzabili in AS3, partendo da un piccolo estratto della guida interna di Flash, raggiungibile dal programma attraverso il menù a tendina Help > Flash Help.

Il pacchetto flash.display di ActionScript 3.0 include una serie di classi per gli oggetti visivi che possono essere visualizzati in Flash Player. L'illustrazione seguente mostra le relazioni tra le sottoclassi di tali classi di oggetti di visualizzazione di base.

Diagramma della gerarchia delle classe di visualizzazione di base. Tutte le classi ereditano da DisplayObject

Nell'illustrazione è riportata l'ereditarietà di classe delle classi di oggetti di visualizzazione. Si noti che alcune di queste classi, in particolare StaticText, TextField e Video, pur non essendo nel pacchetto flash.display, ereditano comunque dalla classe DisplayObject.

L'aumento esponenziale degli elementi di visualizzazione contemplati all'interno del diagramma può fuorviare rispetto agli effettivi elementi che si possono utilizzare in Actionscript, infatti ad esempio non è possibile creare direttamente un'istanza della classe DisplayObjectContainer, ma questa classe viene creata per creare a sua volta una serie di classi che possono contenere altri elementi, come appunto Loader, Sprite e Stage. Altro esempio è proprio la classe Stage di cui non possiamo creare un'istanza, ma che è presente all'interno di ogni applicazione swf come oggetto display contenitore di tutto il resto, come possiamo evincere da quest'altro diagramma:

Diagramma di un elenco di visualizzazione in un file SWF. Lo stage si trova alla base della gerarchia e contiene l istanza della classe principale che, a sua volta, contiene un oggetto di visualizzazione e un contenitore di oggetti di visualizzazione

In finale le classi di cui possiamo effettivamente creare un'istanza e manipolare in actionscript per realizzare le nostre applicazioni sono:

Le altre classi, come da esempio della classe DisplayObjectContainer, fungono da classi principali per altri oggetti di visualizzazione, combinando più funzionalità comuni in un'unica classe.

Ognuno degli elementi visivi presenti nella lista, può essere creato via codice, attraverso l'operatore new ed aggiunto alla timeline principale o ad un altro oggetto contenitore, attraverso addChild() o addChildAt(). Facciamo un esempio pratico per comprendere meglio la questione:

// creo un'istanza della classe Shape
var myShape:Shape = new Shape();
// disegno dentro myShape un rettangolo, attraverso le API di disegno di Actionscript, utilizzabili attraverso la classe dedicata "Graphics"
// la classe Shape contiene già al suo interno la dichiarazione di un'istanza della classe Graphics, raggiungibile dalla proprietà Shape.graphics
myShape.graphics.lineStyle(1);
myShape.graphics.beginFill(0);
myShape.graphics.drawRect(0,0,300,200);
myShape.graphics.endFill();
// attualmente l'istanza non è visualizzata, ma è già pronta in memoria, in attesa di essere aggiunta alla timeline principale
// aggiungiamo l'istanza attraverso addChild();
addChild(myShape);

Come si può evincere dal codice, per creare un elemento visivo non abbiamo più bisogno di decidere a priori il MovieClip dentro il quale crearlo, nè il livello a cui attaccare il clip appena creato, sarà il player a gestire autonomamente le profondità, rimarrà comunque salvo il fatto di poter decidere autonomamente gli "swap" e le sostituzioni dei clip in un determinato livello. Approfondiremo poco più avanti nel discorso i comandi di gestione dei "child"

Vediamo ora un altro esempio per capire meglio la gestione delle display list:

// questa volta creo un'istanza della classe Sprite
var mySprite:Sprite = new Sprite();
// riscrivo il codice dell'esempio precedente
var myShape:Shape = new Shape();
myShape.graphics.lineStyle(1);
myShape.graphics.beginFill(0);
myShape.graphics.drawRect(0,0,300,200);
myShape.graphics.endFill();
// ora invece di attaccare myShape direttamente alla timeline principale, lo attacco su mySprite
mySprite.addChild(myShape);
// se non eseguissi l'addChild di mySprite sulla timeline principale, non vedrei neanche myShape
// quindi myShape diventando "figlio" di mySprite, ne segue la stessa sorte "grafica"
addChild(mySprite);

Penso che appaia chiaro che addChild() è un metodo delle classi che derivano dalla classe DisplayObjectContainer, ma chiaramente non è l'unico metodo di gestione dei "figli" ereditato dalla classe, vediamoli tutti:

Per utilizzare pienamente i metodi sù descritti è necessaria anche una proprietà fondamentale per la display list, la proprietà numChildren, che determina quanti elementi ci sono all'interno della display list dell'elemento che la richiama. Attraverso tale proprietà è possibile ad esempio effettuare un ciclo per eliminare tutti gli oggetti visivi presenti in un determinato DisplayObjectContainer:

// creo un oggetto Sprite
var s:Sprite = new Sprite();
// creo 5 oggetti Bitmap
var b1:Bitmap = new Bitmap();
var b2:Bitmap = new Bitmap();
var b3:Bitmap = new Bitmap();
var b4:Bitmap = new Bitmap();
var b5:Bitmap = new Bitmap();
// attacco i 5 oggetti alla display list di "s"
s.addChild(b1);
s.addChild(b2);
s.addChild(b3);
s.addChild(b4);
s.addChild(b5);
// attacco "s" alla timeline principale
addChild(s);
// ... 
// dopo varie azioni voglio eliminare tutti i Bitmap da "s"
// lo faccio attraverso un semplice ciclo
while(s.numChildren > 0) {
	s.removeChildAt(s.numChildren-1);
}

Ovviamente questo è solo un piccolo esempio della gestione possibile sulla display list, ma serve a comprendere il meccanismo fondamentale che le ruota attorno, e che (a mio parere) semplifica la gestione ed aumenta le potenzialità del linguaggio rispetto alle versioni precedenti, oltre a garantire prestazioni decisamente maggiori sulle applicazioni.

Vediamo ora un'ultima cosa fondamentale in questa nuova gestione degli elementi visivi, l'estensione delle classi derivate da DisplayObject.

Chi ha sviluppato in OOP in AS2, avrà notato che, nonostante sia possibile estendere una classe di tipo MovieClip, non sarà poi possibile utilizzarla in maniera dinamica, richiamando direttamente tale classe sul codice, ma bisognerà creare manualmente un MovieClip nell'IDE Flash e assegnargli la classe estesa nelle proprietà.

In AS3 è possibile estendere la classe di un elemento di visualizzazione, conservandone le proprietà e i metodi e aggiungendone di nuovi, richiamando attraverso l'operatore new la classe, ed aggiungendola alla display list desiderata, attraverso addChild. Ecco un esempio per comprendere meglio la cosa:

// in AS3 la sintassi di creazione delle classi prevede che la classe venga creata all'interno dell'istruzione "package{}"
package {
	// è necessario importare la classe che si intende estendere, in questo caso vogliamo estendere 
	// la classe Shape, per creare una classe che si disegni automaticamente in forma di cerchio
	import flash.display.Shape;
	// creo la classe con attributo public perchè sia visibile da ogni percorso ed uso l'istruzione "extends" per estendere la classe Shape
	public class Circle extends Shape {
		// creo un riferimento privato per i parametri che passerò nel costruttore
		private var __r:Number;
		private var __c:uint;
		// la funzione di costruzione dovrà avere lo stesso nome della classe
		// in AS3 possiamo assegnare un valore di default ai parametri della 
		// funzione, in questo modo non siamo costretti a scriverli quando creiamo una nuova istanza della classe
		public function Circle (radius:Number=100, color:uint=0) {
			// assegno i valori dei parametri alle variabili private della classe
			__r = radius;
			__c = color;
			// richiamo la funzione privata che consente di disegnare il cerchio
			__init__ ();
		}
		private function __init__ () {
			// attraverso la classe Graphics disegno dentro la sottoclasse di Shape un cerchio 
			// con i valori passati come parametri della funzione di costruzione
			graphics.beginFill (__c);
			graphics.drawCircle (__r,__r,__r);
			graphics.endFill ();
		}
	}
}

In flash richiamerò la classe attraverso il costruttore con la riga: var c:Circle = new Circle() e lo attaccherò allo stage attraverso la riga addChild(c), ottenendo la visibilità di un cerchio nero in alto a sinistra del filmato.

L'esempio dimostra la facilità di estensione degli elementi di visualizzazione in AS3, che genera infinite possibilità nella creazione di elementi con l'utilizzo del solo strumento di programmazione, senza la necessità di disegnare manualmente la grafica. Si pensi ad esempio alla possibilità di disegnare un box con scrollbar senza dover ricorrere allo ScrollPane o di creare pulsantiere più o meno complicate, attraverso una sola classe da aggiungere poi allo stage.

In finale possiamo dire di aver considerato un po' tutti gli aspetti riguardanti la novità della display list in Actionscript; ovviamente, come premesso, non saranno certo le poche righe di questo articolo a far comprendere appieno l'argomento, ma voglio sperare che questo sia un valido aiuto, saluti a tutti!