Dans ce chapitre, nous allons aborder le système qui va nous permettre (enfin !) d'afficher des éléments (texte, dessins, images, etc.) à l'écran ! Mais nous ne pourrons réellement afficher quelque chose qu'au chapitre suivant, car il nous faut aborder quelques notions de base auparavant.
Nous allons voir notamment :
- comment décrire une couleur en ActionScript,
- le fonctionnement l'affichage sur un écran,
- et enfin le système d'affichage de Flash.
C'est parti !
- Introduction
- L'arbre des objets d'affichage
- Manipuler les conteneurs
- Propriétés utiles des objets d'affichage
- Supprimer un objet d'affichage de la mémoire
Introduction
Les couleurs
Composer une couleur
En informatique, les couleurs sont décomposées en couleurs principales : le rouge, le vert et le bleu. À partir de ces trois couleurs, il est possible de créer n'importe quelle autre couleur en les dosant (voir figure suivante).
Exemple : je souhaite créer une couleur orange. L'orange est constitué de jaune et de rouge. Ainsi, il nous faut d'abord faire du jaune (rouge + vert), puis rajouter du rouge (voir figure suivante). Au final, l'orange se crée grosso-modo avec deux quantités de rouge pour une quantité de vert.
Notation hexadécimale
Pour décrire une couleur composée de rouge, de vert et de bleu, nous utiliserons la notation hexadécimale. On commence par écrire 0x
pour signifier que l'on utilise la notation hexadécimale, puis on décrit les quantités de chacune des trois couleurs rouge, vert et bleu : deux chiffres, allant de 0 (le minimum) à F (le maximum) pour chacune, ce qui fait six chiffres.
Pourquoi jusqu'à F ? Depuis quand F est un chiffre ?
Excellente question ! En réalité, il existe différents systèmes de notation pour les nombres. Celle que vous et moi utilisons tous les jours s'appelle la notation décimale. Cela signifie que nous utilisons les chiffres de 0 à 9 pour écrire les nombres. On dit aussi que nous écrivons les nombres en base dix.
Une autre notation très connue est la notation binaire : nous décrivons les nombres avec seulement les chiffres 0 et 1. On parle alors de base deux. Par exemple, 5
en base dix s'écrit 101
en base deux !
La notation hexadécimale utilise non pas dix, mais seize chiffres ! On parle de base seize pour cette notation. Les chiffres vont de 0 à 15 ; avouez cependant qu'écrire un chiffre à l'aide de deux chiffres serait plutôt embêtant… Comment faire alors la différence entre 12 (le chiffre) et 12 (le nombre composé du chiffre 1 et du chiffre 2) ? Impossible !
Ainsi, nous utilisons les 6 premières lettres de l'alphabet pour remplacer les chiffres 10 à 15, ce qui est tout de même bien plus pratique.
Chiffre (décimal) |
Notation hexadécimale |
---|---|
10 |
A |
11 |
B |
12 |
C |
13 |
D |
14 |
E |
15 |
F |
En dehors de l'ActionScript, on utilise plus généralement le #
pour commencer une notation hexadécimale.
Ainsi, pour écrire notre chiffre 12, nous utilisons C ! Voici quelques exemples de nombre en base dix convertis en base seize :
Base dix |
Base seize |
---|---|
12 |
C |
5 |
5 |
31 |
1F |
32 |
20 |
16 |
10 |
160 |
A0 |
66 |
42 |
Afin de décrire une couleur, nous utiliserons donc deux chiffres en hexadécimal pour le rouge, le vert et le bleu, de 00
à FF
, ce qui fait de 0
à 255
en décimal. Par exemple, pour notre couleur orange, nous écrirons : 0xFF8000
, car il y a deux doses de rouge pour une dose de vert (voir figure suivante).
La plupart des logiciels de dessin et retouche d'image affichent la notation hexadécimale des couleurs, qu'il est alors facile de copier-coller. Il existe également des outils ou des sites web spécialisés dans la création de couleur, qui fournissent cette notation (comme par exemple, ColRD). Attention toutefois, il ne faut pas oublier qu'en ActionScript, la notation hexadécimale commence par 0x
et non pas par #
!
Stocker une couleur dans une variable
Le type qu'il faut utiliser pour stocker un nombre en notation hexadécimale est le type uint
:
1 2 | var orange:uint = 0xFF8000; var orange2:uint = 0xff8000; // Les lettres peuvent aussi être notées en minuscule |
La variable ainsi créée peut être manipulée comme n'importe quelle autre variable :
1 2 3 4 | trace("La couleur orange vaut " + orange + " en base dix."); // 16744448 trace("La couleur orange vaut " + orange.toString(10) + " en base dix."); // 16744448 trace("La couleur orange vaut " + orange.toString(16) + " en base seize."); // FF8000 trace("La couleur orange vaut " + orange.toString(2) + " en base deux."); // 1111 1111 1000 0000 0000 0000 |
Vous remarquerez que la couleur est stockée sous la notation décimale, car c'est la seule notation supportée par les types de nombres. Par contre, il est facile de retrouver la notation hexadécimale ou binaire grâce à la méthode toString()
comme dans l'exemple ci-dessus.
L'affichage sur un écran
Tous les écrans d'affichage sont composés d'une grille de petits carrés de lumière que l'on nomme pixels (voir figure suivante). Nous allons voir comment utiliser cette grille pour positionner des objets.
Le repère
Les pixels forment donc une grille suivant deux axes : l'axe des X et l'axe des Y. On se situe alors sur un espace en 2D. Pour mieux visualiser la grille, nous allons utiliser à la figure suivante un repère, qui nous montre les axes et l'origine de la grille.
Une position (c'est-à-dire un point précis de ce repère) est décrite par deux coordonnées : la position en X, et la position en Y. On écrit toujours les coordonnées en 2D dans cet ordre : d'abord les X, puis les Y. Ainsi il n'est pas nécessaire de tout le temps préciser qui est quoi. Dans l'exemple à la figure suivante, le pixel que l'on a colorié en bleu se trouve à la position (5,6), c'est-à-dire à 5 pixels du bord gauche de l'écran, et à 6 pixels du bord supérieur de l'écran.
Tout au long du cours, il faudra avoir ce repère en tête dès qu'il s'agira de positionner un objet à l'écran. Il faut savoir qu'il est possible que les coordonnées soient négatives, mais dans ce cas, les pixels qui dépassent ne seront pas affichés (faute de pixels sur votre écran ).
L'arbre des objets d'affichage
L'arbre d'affichage
Pour bien comprendre de quoi il s'agit, commençons par un exemple concret, et revenons sur notre chère voiture. Nous voulons maintenant l'afficher et l'animer sur notre écran ! Nous allons utiliser un modèle simplifié, celui de la figure suivante.
Supposons que cette voiture soit composée d'une carrosserie et de deux roues. Vue de l'extérieur, la voiture est un objet et un seul (voir figure suivante).
Mais en réalité, il y a trois objets à l'intérieur de cet objet (voir figure suivante) : la carrosserie en orange et les deux roues en bleu.
Lorsque nous voudrons afficher la voiture, nous créerons tout d'abord un conteneur (par exemple, avec la classe Sprite
), puis nous ajouterons à l'intérieur trois autres objets d'affichage (voir figure suivante) : un pour la carrosserie, et un pour chaque roue.
Ainsi, si nous bougeons le conteneur « voiture », les roues se déplaceront en même temps que la carrosserie ; si nous tournons le conteneur, l'ensemble fera une rotation comme s'il s'agissait d'un unique objet, à l'instar de ce qui se passerait dans la vraie vie ! La voiture pourrait elle-même se trouver dans un autre conteneur "rue", que l'on pourrait faire défiler comme un tout…
Ainsi, l'arbre d'affichage est une arborescence d'objets d'affichage inclus les uns dans les autres dans une certaine logique. Voici un schéma à la figure suivante représentant l'arbre d'affichage de notre application
Vous remarquerez que la racine de l'arbre est composée d'un objet que j'ai appelé scène principale. C'est un objet spécial de la classe Stage
qui est toujours présent pour la gestion de l'affichage.
Maintenant, si nous voulons qu'il y ait deux voitures dans la rue, il suffit d'en ajouter une autre dans le conteneur « rue ».
Il est important de se rappeler que modifier l'apparence ou la position d'un conteneur impactera tous les enfants comme s'il s'agissait d'un seul et unique objet. Il est néanmoins possible de modifier les enfants séparément.
Les classes d'affichage
En ActionScript, il existe plusieurs classes différentes dédiées à l'affichage, comme on peut le constater à la figure suivante.
Par exemple, la classe TextField
sert à afficher du texte ; nous en reparlerons dans le prochain chapitre. Elle est une sous-classe de InteractiveObject
(pour les objets interactifs), elle-même sous-classe de DisplayObject
. Ainsi, toutes les classes d'affichage sont des sous-classes de la classe DisplayObject
.
Rappel : les classes abstraites ne peuvent pas être instanciées directement, c'est-à-dire que l'on ne peut pas créer de nouveaux objets de ces classes avec le mot-clé new
. Il est toutefois possible de spécifier des paramètres de type de classes abstraites dans les méthodes, ce n'est pas gênant.
La classe DisplayObject (abstraite)
Il s'agit de la classe d'affichage de base. C'est à partir d'elle que toutes les autres classes d'affichage sont dérivées ; elle contient les propriétés utiles pour manipuler des objets d'affichage simples. Encore une fois, étant donné qu'il s'agit d'une classe abstraite, inutile d'essayer de créer un objet de la classe DisplayObject
.
La classe Bitmap
Cette classe permet d'afficher des images composées de pixels. Les objets de cette classe sont purement visuels ; il est impossible de gérer directement les clics de souris dessus par exemple. Nous apprendrons à manipuler des images dans les prochains chapitres.
La classe Shape
Cette classe permet de créer des objets légers permettant de dessiner à l'écran : tracer des lignes, des formes, remplir… À l'instar des objets de la classe Bitmap
, les objets de la classe Shape
sont purement visuels.
La classe Video
Comme vous vous en doutez, cette classe permet l'affichage de vidéos (avec le son). C'est aussi une classe créant des objets purement visuels.
La classe InteractiveObject (abstraite)
Cette classe permet d'introduire de l'interactivité pour nos objets d'affichage : il sera ainsi possible de savoir si l'utilisateur clique sur un objet d'une sous-classe de la classe InteractiveObject
.
La classe SimpleButton
Cette classe permet de fabriquer des boutons basiques rapidement : on peut ainsi lui spécifier quel objet sera affiché dans différents états (normal, souris au-dessus, cliqué, …), et automatiquement changer le curseur de la souris au-dessus en doigt. Cette classe est utilisée par Flash Pro
d'Adobe pour créer les boutons.
La classe TextField
Avec cette classe, nous présenterons du texte à l'écran pendant le prochain chapitre. Il est possible de formater le texte à l'aide d'une forme très allégée d'HTML (le langage principal utilisé pour créer des sites web).
La classe DisplayObjectContainer (abstraite)
Cette dernière classe abstraite introduit la notion de conteneur : il est désormais possible d'ajouter plusieurs objets d'affichage enfants dans un seul conteneur, afin de les manipuler ensemble comme s'il s'agissait d'un seul objet (pensez à la fonction Grouper des logiciels de bureautique).
La classe Loader
Cette classe nous permettra de charger du contenu externe à notre application, à condition qu'il soit visuel.
La classe Stage (abstraite)
Les objets de cette classe sont des objets spéciaux qui représentent la scène d'affichage de l'application. Il est bien évidemment impossible de créer directement une instance de la classe Stage
. Attention, la plupart des propriétés héritées des classes-mères ne fonctionnent pas sur eux (comme par exemple la position, l'opacité, etc.).
La classe Sprite
Non, il ne s'agit pas de la boisson ! Les objets de cette classe sont les plus utilisés en tant que conteneurs, et en tant que dessins interactifs. Vous en userez (et abuserez) en programmant en ActionScript !
La classe MovieClip
Cette classe dérivée de la classe Sprite
ajoute un composant complexe et plutôt lourd : une ligne de temps (ou timeline en anglais). Cela consiste en une série d'images-clés pouvant être réparties sur différents niveaux, afin de créer des animations complexes (voir figure suivante). Cette classe a tout d'abord été conçue pour être utilisée dans le logiciel d'animation Flash Pro
d'Adobe ; ainsi, étant d'un intérêt limité pour une utilisation en ActionScript, nous ne l'utiliserons quasiment pas dans ce cours.
Il existe d'autres classes d'affichage que je n'ai pas mentionnées ici : nous ne les aborderons pas, étant trop avancées. Mais si vous êtes curieux, vous pouvez toujours explorer les possibilités supplémentaires qui s'offrent à vous en visitant la documentation.
Si nous reprenions notre exemple de la voiture, les schémas des arbres d'affichage ressembleraient aux deux figures suivantes.
Manipuler les conteneurs
Toutes les nouvelles propriétés (attributs et méthodes) que nous allons voir sont issues de la classe DisplayObjectContainer
. Je vous conseille d'aller lire en parallèle la documentation officielle du langage ActionScript 3, en français et disponible à cette adresse. N'hésitez surtout pas à l'utiliser aussi souvent que vous en sentez le besoin, elle est très pratique !
Buvez du Sprite !
Comme nous l'avons vu précédemment, la classe Sprite
va nous permettre de créer des conteneurs d'affichage très polyvalents tout en restant légers. Commençons par créer un conteneur que nous appellerons voiture
.
1 2 | // Création du conteneur 'voiture' var voiture:Sprite = new Sprite(); |
Comme vous pouvez le remarquer, le constructeur de la classe Sprite
ne prend pas de paramètre.
Ajouter des enfants
Pour ajouter des objets d'affichage enfants dans notre conteneur voiture
, il faut utiliser la méthode addChild(enfant:DisplayObject):void
fournie par la classe DisplayObjectContainer
. Elle prend en paramètre l'enfant à ajouter dans le conteneur.
1 2 3 4 5 6 7 8 | // Créons une roue var roue:Sprite = new Sprite(); // Ajoutons-la dans la voiture voiture.addChild(roue); // Créons la rue et ajoutons la voiture dans la rue var rue:Sprite = new Sprite(); rue.addChild(voiture); |
L'objet roue1
est désormais affiché à l'intérieur du conteneur voiture
, lui-même affiché dans le conteneur rue
. Mais il manque quelque chose : il faut ajouter l'objet rue
dans la scène principale, sinon il ne sera pas affiché !
Afficher un objet sur la scène principale
Dans notre projet, nous utilisons une classe principale que nous avons appelée Main
(dans le fichier Main.as
) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package { import flash.display.Sprite; import flash.events.Event; /** * ... * @author Guillaume */ public class Main extends Sprite { public function Main():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // entry point trace("Hello world !"); } } } |
Regardez droit dans les yeux la déclaration de la classe Main
: c'est une sous-classe de Sprite
! Notre classe principale est donc un conteneur, qui est automatiquement ajouté à la scène au démarrage de notre application ; ainsi, il ne nous reste plus qu'à ajouter nos objets dans le conteneur de classe Main
qui nous est offert !
Pour ajouter notre rue sur la scène principale, nous procéderons ainsi :
1 | this.addChild(rue); |
Le code peut être raccourci en enlevant le mot-clé this
:
1 | addChild(rue); |
Rappel : le mot-clé this
permet d'accéder à l'objet courant. Si nous l'utilisons dans des méthodes non statiques de la classe Main
, nous pointerons sur l'objet qui représente notre application. Il est toutefois possible d'omettre le mot-clé this
si cela ne pose pas de problème de conflit de noms (typiquement, lorsque vous avez à la fois un attribut dans votre classe et un paramètre du même nom dans la méthode, il faut utiliser this
pour accéder à l'attribut de la classe). Du coup, le code ci-dessus ne fonctionnera pas dans une autre classe ; il faudrait alors utiliser une variable pointant sur l'objet de la classe Main
.
Voici le code complet de la classe Main
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | package { import flash.display.Sprite; import flash.events.Event; /** * ... * @author Guillaume */ public class Main extends Sprite { public function Main():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // entry point // Création du conteneur 'voiture' var voiture:Sprite = new Sprite(); // Créons une roue var roue:Sprite = new Sprite(); // Ajoutons-la dans la voiture voiture.addChild(roue); // Créons la rue et ajoutons la voiture dans la rue var rue:Sprite = new Sprite(); rue.addChild(voiture); // On ajoute la rue sur la scène principale addChild(rue); } } } |
Si vous testez le code à ce stade, vous risquez d'être un peu déçus : en effet, les conteneurs sont vides par défaut ! Vous devrez vous contenter d'une triste scène vide, mais nous allons bientôt remédier à cela.
Reprenons maintenant le schéma représentant l'arbre d'affichage, que nous avons détaillé plus haut (voir figure suivante).
Corrigeons-le pour y inclure notre objet de la classe Main
(voir figure suivante).
Un objet ne peut être affiché à deux endroits différents ; si jamais vous l'ajoutez à un conteneur alors que l'objet est déjà dans un autre conteneur, il sera automatiquement retiré de ce dernier avant d'être ajouté dans le nouveau conteneur.
L'index d'affichage
Cette notion correspond à l'ordre d'affichage des enfants dans un conteneur. Si un objet a un index plus grand qu'un autre, il sera affiché devant, et inversement. L'index est compris entre 0
et conteneur.numChildren - 1
(le nombre d'enfants moins un).
À la figure suivante par exemple, la voiture bleue est au-dessus des autres car elle a l'index d'affichage le plus élevé. En revanche, la voiture orange est tout en dessous car elle a un index égal à zéro.
Il est impossible de mettre un enfant à un index en dehors des bornes que nous avons définies : ainsi, on ne peut pas mettre un objet à un index inférieur à 0
ou supérieur à conteneur.numChildren - 1
.
On peut obtenir l'index d'un enfant en utilisant la méthode getChildIndex():int
(renvoyant un int
), qui prend en paramètre l'objet enfant en question :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // On crée la rue var rue:Sprite = new Sprite(); // On ajoute trois voiture dans la rue var voiture1:Sprite = new Sprite(); rue.addChild(voiture1); var voiture2:Sprite = new Sprite(); rue.addChild(voiture2); var voiture3:Sprite = new Sprite(); rue.addChild(voiture3); trace("La voiture1 est à l'index " + rue.getChildIndex(voiture1)); // Affiche 0 trace("La voiture2 est à l'index " + rue.getChildIndex(voiture2)); // Affiche 1 trace("La voiture3 est à l'index " + rue.getChildIndex(voiture3)); // Affiche 2 |
Ajouter un enfant à un index précis
Il existe une variante de la méthode addChild
que nous avons vue il y a peu de temps. Il s'agit de la méthode addChildAt(enfant:DisplayObject, index:int):void
, qui prend deux paramètres : l'enfant à ajouter, puis l'index d'affichage où il faut l'ajouter.
Voici un exemple, où l'on ajoute chaque nouvelle voiture en arrière-plan :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // On crée la rue var rue:Sprite = new Sprite(); // On ajoute des voitures à la rue, en les mettant à l'arrière-plan à chaque fois var voiture1:Sprite = new Sprite(); rue.addChildAt(voiture1, 0); var voiture2:Sprite = new Sprite(); rue.addChildAt(voiture2, 0); var voiture3:Sprite = new Sprite(); rue.addChildAt(voiture3, 0); trace("La voiture1 est à l'index " + rue.getChildIndex(voiture1)); // Affiche 2 trace("La voiture2 est à l'index " + rue.getChildIndex(voiture2)); // Affiche 1 trace("La voiture3 est à l'index " + rue.getChildIndex(voiture3)); // Affiche 0 |
Comme vous pouvez le constater, l'index d'affichage de chaque objet varie, car il représente à tout instant la position en profondeur de celui-ci ; si jamais un objet passe derrière un autre par exemple, son index sera automatiquement modifié en conséquence !
Opérations sur les enfants
Nombre d'enfants
Pour obtenir le nombre d'enfants que contient un conteneur, vous pouvez utiliser le getter numChildren
de type int
.
Attention, c'est un attribut en lecture seule car aucun setter n'est défini dans la classe DisplayObjectContainer
!
1 2 3 4 5 6 7 8 9 10 11 12 | // On crée une rue var rue:Sprite = new Sprite(); // On ajoute trois voitures dans la rue var voiture1:Sprite = new Sprite(); rue.addChild(voiture1); var voiture2:Sprite = new Sprite(); rue.addChild(voiture2); var voiture3:Sprite = new Sprite(); rue.addChild(voiture3); trace("Il y a " + rue.numChildren + " voitures dans la rue."); // Affiche 3. |
Accéder au parent d'un objet d'affichage
L'attribut parent
permet d'accéder au conteneur parent qui contient l'objet d'affichage. Cette propriété est notamment utile à l'intérieur d'une classe, quand on ne peut pas savoir quel est le conteneur.
1 2 3 4 | trace(rue == voiture1.parent); // Affiche true trace("Nombre de voitures : " + rue.numChildren); // Affiche 3 trace("Nombre de voitures : " + voiture1.parent.numChildren); // Affiche 3 également |
Modifier l'index d'un enfant
Une fois un objet d'affichage enfant ajouté à un conteneur, il est possible de modifier son index d'affichage. Pour cela, nous utiliserons la méthode setChildIndex(enfant:DisplayObject, index:int):void
qui prend en paramètre l'enfant en question puis son nouvel index.
1 2 3 4 5 6 7 8 | // On met successivement les voitures au premier-plan rue.setChildIndex(voiture1, 2); rue.setChildIndex(voiture2, 2); rue.setChildIndex(voiture3, 2); trace("La voiture1 est à l'index " + rue.getChildIndex(voiture1)); // Affiche 0 trace("La voiture2 est à l'index " + rue.getChildIndex(voiture2)); // Affiche 1 trace("La voiture3 est à l'index " + rue.getChildIndex(voiture3)); // Affiche 2 |
Rappel : la valeur de l'index doit être située entre 0
et conteneur.numChildren - 1
!
Échanger les index d'affichage de deux enfants
Il existe deux méthodes de la classe DisplayObjectContainer
pour échanger la profondeur de deux objets enfants : swapChildren()
qui prend en paramètre deux références des enfants du conteneur et swapChildrenAt()
qui prend en paramètre deux index différents à échanger.
1 2 3 4 5 6 7 8 9 | rue.swapChildren(voiture1, voiture2); trace("La voiture1 est à l'index " + rue.getChildIndex(voiture1)); // Affiche 1 trace("La voiture2 est à l'index " + rue.getChildIndex(voiture2)); // Affiche 0 // La voiture 1 est affichée devant la voiture 2 rue.swapChildrenAt(0, 1); trace("La voiture1 est à l'index " + rue.getChildIndex(voiture1)); // Affiche 0 trace("La voiture2 est à l'index " + rue.getChildIndex(voiture2)); // Affiche 1 // La voiture 1 est affichée en dessous de la voiture 2 |
Déterminer si un objet est enfant d'un conteneur
Une méthode très pratique de la classe DisplayObjectContainer
renvoie un booléen pour le savoir : j'ai nommé contains()
! Elle prend en paramètre un objet de la classe DisplayObject
et retourne true
si cet objet est dans la liste d'affichage du conteneur (y compris parmi les petits-enfants, parmi les enfants des petits-enfants, etc.), ou s'il s'agit du conteneur lui-même (nous considérons que le conteneur se contient lui-même). Sinon, elle renvoie false
.
1 2 3 4 5 6 7 8 9 10 11 12 | var sprite1:Sprite = new Sprite(); var sprite2:Sprite = new Sprite(); var sprite3:Sprite = new Sprite(); var sprite4:Sprite = new Sprite(); sprite1.addChild(sprite2); sprite2.addChild(sprite3); trace(sprite1.contains(sprite1)); // Affiche: true trace(sprite1.contains(sprite2)); // Affiche: true trace(sprite1.contains(sprite3)); // Affiche: true trace(sprite1.contains(sprite4)); // Affiche: false |
Retirer des enfants
Pour retirer un enfant d'un parent, la méthode removeChild(enfant:DisplayObject):void
est toute indiquée ! À l'instar de la méthode addChild()
, cette fonction prend en paramètre l'enfant à enlever de l'affichage.
1 2 3 4 | // On enlève les trois voitures de la rue rue.removeChild(voiture1); rue.removeChild(voiture2); rue.removeChild(voiture3); |
Il est également possible de supprimer un enfant à un certain index, sans savoir précisément duquel il s'agit, à l'aide de la méthode
removeChildAt(index:int):void
. Le paramètre que nous passons correspond à l'index de l'enfant que nous souhaitons enlever.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // On crée la rue var rue:Sprite = new Sprite(); // On ajoute des voitures à la rue, en les mettant à l'arrière-plan à chaque fois var voiture1:Sprite = new Sprite(); rue.addChildAt(voiture1, 0); var voiture2:Sprite = new Sprite(); rue.addChildAt(voiture2, 0); var voiture3:Sprite = new Sprite(); rue.addChildAt(voiture3, 0); trace("La voiture1 est à l'index " + rue.getChildIndex(voiture1)); // Affiche 2 trace("La voiture2 est à l'index " + rue.getChildIndex(voiture2)); // Affiche 1 trace("La voiture3 est à l'index " + rue.getChildIndex(voiture3)); // Affiche 0 // On enlève a voiture le plus au fond rue.removeChildAt(0); // voiture3 enlevée ! // On peut enlever les deux autres de la même manière rue.removeChildAt(0); // voiture2 enlevée ! rue.removeChildAt(0); // voiture1 enlevée ! |
Et si on souhaite enlever tous les enfants du conteneur, n'y a-t-il pas un moyen moins fastidieux ?
La réponse est oui, grâce à la méthode removeChildren(beginIndex:int = 0, endIndex:int = 0x7fffffff):void
qui permet de supprimer plusieurs enfants d'un coup d'un seul ! Les deux paramètres facultatifs spécifient à partir de quel index on supprime, et jusqu'à quel index. Par défaut, tous les enfants sont supprimés, donc si c'est ce que vous souhaitez faire, laissez les parenthèses vides.
1 | rue.removeChildren(); // Toutes les voitures sont enlevées d'un coup ! |
Propriétés utiles des objets d'affichage
Nous allons maintenant aborder des propriétés communes à tous les objets d'affichage, qui s'avèreront très utiles ! Toutefois, nous nous limiterons pour l'instant à l'affichage 2D dans le repère mentionné en début de chapitre.
Les attributs et méthodes que nous allons voir sont des propriétés de la classe DisplayObject
, dont la documentation est disponible ici.
Position
Tout d'abord, commençons par la position de l'objet d'affichage : deux attributs, x
et y
, permettent de contrôler respectivement la position horizontale et verticale de l'origine d'un objet d'affichage.
Reprenons l'exemple de l'introduction, visible à la figure suivante.
Pour déplacer l'origine d'un objet à cet endroit précis (5, 6), nous écrirons ceci :
1 2 3 4 5 | var monObjet:Sprite = new Sprite(); // Modifions la position de l'objet ! monObjet.x = 5; monObjet.y = 6; addChild(monObjet); |
La position par défaut de tout nouvel objet d'affichage est (0, 0), c'est-à-dire que x
vaut 0
et que y
vaut 0
si vous n'y touchez pas.
Un mot sur l'origine
L'origine d'un objet d'affichage est le point qui représente sa position actuelle. Par défaut, elle est située en haut à gauche (voir figure suivante), et elle ne peut pas être déplacée.
Comment modifier l'origine de notre conteneur si on ne peut pas la déplacer ?
Et bien, c'est très simple, il suffit de déplacer les enfants à l'intérieur de notre conteneur, pour donner l'illusion de modifier l'origine (voir figure suivante) !
Et c'est gagné (voir figure suivante) !
L'affichage à l'écran de la voiture à la position (0, 0) ressemblerait alors à la figure suivante.
Par exemple, si vous ajoutez un objet d'affichage voiture
dans votre conteneur rue
, et que vous positionnez la voiture en négatif à 100 pixels de l'origine de la rue, une partie de votre voiture sera très certainement en dehors de l'écran. Mais l'origine de la rue n'a pas changé entre-temps : elle est toujours à sa position de départ (0, 0). Maintenant, vous pouvez faire réapparaître la voiture en entier en déplaçant la rue de 100 pixels vers la droite ! Son origine va ainsi changer et devenir (100, 0), ainsi la position de la voiture par rapport à la scène sera (0, 0) ! Tout est relatif !
Taille
Taille absolue
La taille absolue d'un objet d'affichage, c'est-à-dire la taille qu'il prend à l'écran, peut être lue ou modifiée à l'aide des attributs width
(longueur) et height
(hauteur), en pixels. La taille est toujours exprimée en valeurs positives.
1 2 3 4 5 | var maVoiture:Voiture= new Voiture(); // Modifions la taille de la voiture pour qu'elle fasse 100x100 pixels ! maVoiture.width = 100; maVoiture.height = 100; addChild(maVoiture); |
Modifier la taille d'un conteneur vide et sans dessin n'a aucun sens, et ne donnera aucun résultat. Ses deux attributs width
et height
seront toujours égaux à 0
.
Taille relative
On peut également redimensionner un objet d'affichage à l'aide de pourcentages, grâces aux attributs scaleX
pour redimensionner en longueur, et scaleY
en hauteur. Les pourcentages ne sont pas directement exprimés en tant que tel : par exemple, scaleX = 1
signifie que la longueur de l'objet est à 100%, scaleY = 2
signifie que l'objet est deux fois plus haut que sa taille absolue d'origine et scaleY = 0.5
signifie que l'objet est moitié moins haut.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | var maVoiture:Voiture= new Voiture(); // Modifions la taille de la voiture pour qu'elle fasse 100x100 pixels ! maVoiture.width = 100; maVoiture.height = 100; // Réduisons la taille de la voiture de moitié ! maVoiture.scaleX = 0.5; maVoiture.scaleY = 0.5; trace(maVoiture.width); // Affiche: 50 trace(maVoiture.height); // Affiche: 50 // Remettons-là à sa taille normale : maVoiture.scaleX = 1; maVoiture.scaleY = 1; trace(maVoiture.width); // Affiche: 100 trace(maVoiture.height); // Affiche: 100 addChild(maVoiture); |
Avec ses propriétés, il est possible d'inverser un objet d'affichage à l'aide de valeurs négatives :
1 2 3 4 5 6 7 8 9 10 | var maVoiture:Voiture= new Voiture(); // Modifions la taille de la voiture pour qu'elle fasse 100x100 pixels ! maVoiture.width = 100; maVoiture.height = 100; // Inversons horizontalement la voiture ! maVoiture.scaleX = -1; trace(maVoiture.width); // Affiche: 100 trace(maVoiture.height); // Affiche: 100 addChild(maVoiture); |
Rappel : les attributs de taille width
et height
sont toujours positifs.
Rotation
L'attribut rotation
permet de faire tourner des objets d'affichage autour de leur origine (voir figure suivante), en utilisant des degrés. Les valeurs comprises entre 0 et 180 représentent la rotation en sens horaire ; les valeurs comprises entre 0 et -180 représentent la rotation en sens antihoraire. Les valeurs hors de cette plage sont alors réajustées (ajoutées ou soustraites de 360) pour obtenir une valeur comprise dans la plage. Par exemple, l’instruction voiture.rotation = 400
correspond à voiture.rotation = 40
(car 400 - 360 = 40).
Si vous voulez utiliser des valeurs en radians, il ne faut pas oublier de les convertir en degrés avant d'utiliser l'attribut rotation
: on écrira par exemple voiture.rotation = maValeurEnRadians / Math.PI * 180;
en utilisant la constant Math.PI
qui contient une valeur assez précise du nombre pi.
Transparence
Visibilité
L'attribut visible
vaut true
si l'objet est visible, false
s'il est invisible. Par défaut, sa valeur est true
(l'objet est visible).
1 2 3 4 | var maVoiture:Voiture= new Voiture(); // Rien ne sera affiché à l'écran maVoiture.visible = false; addChild(maVoiture); |
Un objet d'affichage avec une visibilité à faux n'est pas du tout rendu à l'écran, et ne peut donc pas interagir avec la souris par exemple. Cela permet toutefois d'économiser des ressources de l'ordinateur pour améliorer les performances. Un bon conseil : si un objet doit être invisible, utilisez cette propriété à chaque fois que vous le pouvez !
Opacité
L'attribut alpha
de type Number
détermine l'opacité d'un objet d'affichage, c'est-à-dire s'il est plus ou moins transparent. Les valeurs vont de 0 (invisible) à 1 (opaque).
1 2 3 4 | var maVoiture:Sprite = new Sprite(); // La voiture sera à moitié transparente maVoiture.alpha = 0.5; addChild(maVoiture); |
L'opacité d'un conteneur est un multiplicateur de l'opacité de tous ces enfants, et ainsi de suite. Par exemple, si un enfant a une opacité de 0.5, et que le conteneur a une opacité de 0.5, l'opacité réelle de l'enfant sera de 0.5 x 0.5 = 0.25 : l'enfant est donc à trois quarts transparent.
1 2 3 4 5 6 7 8 9 10 | var enfant:Sprite = new Voiture(); // L'enfant sera à moitié transparent enfant.alpha = 0.5; var conteneur:Sprite = new Sprite(); // Le conteneur sera aussi à moitié transparent conteneur.alpha = 0.5; conteneur.addChild(maVoiture); // Au final, l'opacité de l'enfant vaut 0.25 |
Il est possible de mettre une valeur supérieure à 1 : cela aura pour effet d'augmenter l'opacité des enfants de l'objet. Par exemple, si un enfant a pour opacité 0.5, et que l'on met l'opacité de son conteneur à 2, l'opacité de l'enfant réelle sera 0.5 x 2 = 1 : l'enfant est donc entièrement opaque.
Un objet visible mais transparent (c'est-à-dire que sa visibilité est à vrai mais que son opacité est à 0) est quand même rendu à l'écran, et donc consomme des ressources de l'ordinateur. Toutefois, il reste interactif, il est possible de cliquer dessus par exemple.
Supprimer un objet d'affichage de la mémoire
À ce stade du cours, pour supprimer un objet d'affichage de la mémoire, il est nécessaire de respecter les points suivants :
- l'objet ne doit pas faire partie de la liste d'affichage, c'est-à-dire qu'il ne doit pas être l'enfant d'un conteneur ;
- il ne doit plus y avoir une seule référence à cet objet.
Nous étofferons cette liste au fur-et-à-mesure du cours, lorsque nous aborderons de nouvelles notions, afin que vous soyez experts en mémoire non-saturée !
Pour supprimer un objet de la liste d'affichage, vous pouvez utiliser la méthode removeChild()
de la classe DisplayObjectContainer
:
1 2 3 4 | var maVoiture:Voiture = new Voiture(); rue.addChild(maVoiture); // Supprimons la voiture de la liste d'affichage rue.removeChild(maVoiture); |
L'objet en question doit être un enfant du conteneur, sinon, une erreur sera envoyée à l'appel de la méthode removeChild()
.
Si jamais vous voulez supprimer l'objet d'affichage depuis sa propre classe, il faut utiliser l'attribut parent
et ruser un peu :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package { import flash.display.Sprite; public class Voiture extends Sprite { public function Voiture() { } public function removeFromParent():void { // Vérifions que la voiture est sur la liste d'affichage (cad. que le parent existe) avant d'appeler removeChild() if (parent != null) { // Supprimons l'objet de la liste d'affichage parent.removeChild(this); } } } } |
Mais comment supprimer une référence à un objet ?
C'est très simple : il suffit d'affecter la variable en question avec le mot-clé null
:
1 2 3 4 5 6 | var maVoiture:Voiture = new Voiture(); rue.addChild(maVoiture); // Supprimons la voiture de la liste d'affichage rue.removeChild(maVoiture); // Supprimons la référence à la voiture ! maVoiture = null; |
À ce stade, l'objet de la classe Voiture
n'a pas encore été supprimé : il est encore dans la mémoire de votre ordinateur. Il sera effectivement supprimé lorsque le ramasse-miette (garbage-collector en anglais) sera passé pour nettoyer la mémoire. Seulement, pour que le ramasse-miette puisse faire son travail, et déterminer quels objets doivent être supprimés, il faut respecter les points énumérés ci-dessus. On dit alors que ces objets sont éligibles au nettoyage par le ramasse-miette.
Vous remarquerez ainsi qu'il n'existe aucun moyen de supprimer un objet directement de la mémoire, sauf pour quelques rares exceptions. La plupart du temps, il faut laisser le ramasse-miette le faire à notre place. Cela marche donc pour tous les objets, pas seulement les objets d'affichage ; mais dans ce cas, inutile d'essayer de retirer de la liste d'affichage des objets qui n'ont rien à voir ! Supprimer leurs références suffit pour l'instant.
En résumé
- En ActionScript 3, les couleurs sont décrites en notation hexadécimale, précédées des caractères
0x
('zéro' et 'x'). - L'affichage à un écran se fait dans un repère partant du coin supérieur gauche de l'écran. Les coordonnés en x vont de gauche à droite, et en y de haut en bas.
- Les objets d'affichage sont contenus dans un arbre : ils peuvent contenir chacun des enfants pouvant eux-même contenir d'autres enfants.
- On peut manipuler les enfants des conteneurs à l'aide des propriétés de la classe
DisplayObjectContainer
. - Une multitude de propriétés de la classe
DisplayObject
permettent de manipuler les objets d'affichage. - Il faut respecter quelques consignes pour qu'un objet d'affichage soit supprimé de la mémoire par le ramasse-miette.