Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Starfield Multidirectionnel version AS3

Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Compatible Flash CS3. Cliquer pour en savoir plus sur les compatibilités.Par kouliane (Kouliane), le 04 mars 2011

Introduction

Bonjour et bienvenue aux adeptes de l'AS3 :D

Ce tutoriel a pour but de mettre en pratique les bases de l'ActionScript 3 et de la programmation orientée objet. Pour ce faire, nous allons créer un champ d'étoiles bougeant lors d'un appui sur les touches fléchées :

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Appuyez sur les touches fléchées du clavier pour faire défiler les étoiles

Ce tutoriel fait suite à son équivalent en version AS2, lui-même écrit à partir d'un jeu créé par la communauté.

En guise de petit bonus par rapport à la version AS2 du tutoriel, nous verrons comment créer les étoiles à l'aide d'outils simples de dessin Flash (en version CS3, donc ça marche aussi pour la CS4 et la CS5). Nous utiliserons ensuite une classe de document afin de programmer la création du fond étoilé et les interactions avec l'utilisateur. Nous verrons également un ensemble de “bonnes pratiques” tout au long du tutoriel, car le but étant de vous apprendre à coder, mieux vaut que vous appreniez à coder “proprement” ;).

Pré-requis

Pour les tout-débutants en AS3, ceux qui ne connaissent vraiment rien de rien à ce qu'est la POO, la notion de classe, d'objets et de méthodes… je les invite à aller voir les tutoriels bien détaillés sur ces notions :
Les bases de la programmation orientée objet
Les pratiques de la POO

Afin de programmer d'une façon toujours plus propre, il est recommandé de lire la page: Ecrire correctement en ActionScript
Je vous invite à la lire avant de vous lancer corps et âmes dans la programmation. Vous n'êtes pas obligés de respecter toutes les conventions citées dans cet ouvrage mais c'est un plus. ;)

Vous trouverez également des références à divers tutoriels au fur et à mesure de votre lecture. ;)

Enfin, si jamais vous bloquez sur un certain morceau de code, les “codes à connaître” regroupé en fin d'article vous fourniront des liens utiles pour mieux maîtriser ces petits morceaux. Je regroupe le tout à la fin de l'article afin de ne pas trop alourdir votre lecture.

Conventions des messages pour ce tutoriel :

Ce texte est important et il vous faut sûrement retenir quelque chose.

Ce texte vous met en garde, retenez cet avertissement si vous pensez qu'il vous concerne ;)

Ceci est une petite astuce, elle vous resservira sûrement un jour.

Ce texte est purement informatif, vous pouvez le sauter en première lecture, mais il pourra sûrement vous apprendre quelque chose d'utile.

Bonne lecture !

Chapitre 1 : Dessinons !

Avant de pouvoir faire quoi que ce soit, nous avons besoin de matières premières avec lesquelles travailler.

Paramètres du document

Commencez-donc par créer un nouveau fichier Flash (AS 3.0). Donnez, au document, les propriétés suivantes :

  • Largeur = 600 px
  • Hauteur = 400 px
  • Couleur de fond = blanche
  • Cadence = 30 images / seconde
  • Classe du document = Starfield


Classe du document :
Pour ceux qui ne savent pas ce que c'est, lisez ce chapitre d'un autre magnifique tutoriel ;), ou encore le chapitre dédié du livre de Thibault Imbert.


Création des clips

Le fond

Dans la bibliothèque ajoutez un nouveau symbole de type Clip. Nommez-le Fond (avec un F majuscule). Tracez un rectangle (à l'aide de l'outil rectangle), sélectionnez-le et donnez-lui les propriétés suivantes :

  • Largeur = 600 px
  • Hauteur = 400 px
  • Couleur de fond = noire
  • Couleur de contour = noire
  • X = 0
  • Y = 0

Ce clip contiendra tous les éléments affichés de notre starfield, à savoir le rectangle noir que nous venons de dessiner plus les étoiles que nous y ajouterons plus tard. Il sera l'élément ajouté en premier dans la scène et sera donc toujours le premier élément de la liste d'affichage. Ainsi, vous pourrez ultérieurement ajouter des éléments supplémentaires par dessus (vaisseau spatial, astéroïdes, etc) en étant sûrs que le ciel étoilé sera toujours bien affiché comme un fond (ou décors).

La liste d'affichage
La liste d'affichage est une liste contenant tous les éléments ajoutés à la scène. Chaque fois que vous placez un élément à l'aide de l'instruction addChild, celui-ci vient s'ajouter à la fin de la liste d'affichage et donc au dessus de tous les éléments déjà créés.
Ainsi le premier élément créé (indicé 0 dans la liste d'affichage) sera l'élément le plus au fond dans votre scène (donc en général un fond, un décor).
Le dernier élément créé (indicé numChildren - 1 dans la liste d'affichage) sera l'élément au devant de la scène (donc en général un personnage ou un vaisseau).
Si vous voulez en savoir plus, je vous invite à lire le très détaillé chapitre 4 de l'ouvrage “Pratique d'ActionScript 3” de T.Imbert sur le sujet.


Nous ajouterons au clip Fond des étoiles à l'aide de code Actionscript. On dit alors que le fond joue un rôle de conteneur pour les étoiles.


Le clip des étoiles

Dans la bibliothèque, ajoutez un nouveau symbole de type Clip et nommez-le Etoiles et cliquez sur OK.

Création des étoiles :

Chose promise, chose due, nous allons créer des étoiles à l'aide d'outils simples que Flash propose. Commencez par modifier la couleur de la scène. Cliquez sur “Propriétés” et mettez une couleur d'arrière-plan noire.

Sélectionnez ensuite l'outil ovale (touche “o” du clavier). Prenez une couleur de remplissage autre que noire et ne mettez aucune couleur de contour. Dessinez un cercle, sélectionnez-le puis modifiez ses propriétés comme suit :

  • Largeur : 15
  • Hauteur : 15
  • X : -7.5
  • Y : -7.5


Modifier la couleur du cercle :

Faites un zoom sur votre cercle. Vérifiez qu'il est bien sélectionné puis ouvrez le panneau “Couleur”. Sélectionnez la couleur de remplissage puis prenez “Radial” comme type. Sélectionnez la première icône de couleur (un petit carré sur la barre en bas) puis allez chercher la couleur blanche.

Ajoutez une nouvelle couleur sur la barre (votre curseur affiche un ”+”), puis allez chercher une couleur bleue turquoise, modifiez sa propriété alpha à 25%. Sélectionnez enfin le troisième petit carré de couleur, allez également chercher une couleur turquoise puis modifiez sa propriété alpha à 0%. Vous pouvez jouer sur les 3 couleurs jusqu'à obtenir un résultat qui vous satisfait.

A un zoom à 2000% vous devez obtenir quelque chose du genre :
etoile_turquoise.jpg
Vous pouvez rendre l'étoile plus ou moins claire, à votre convenance.

Créer les étoiles d'autre couleur :

Sélectionnez ensuite votre étoile, copiez-la puis créez de nouvelles images clés vides en image 2, 3, 4 et 5. Copiez dans chaque nouvelle image votre étoile (clique droit, coller en place ou Ctrl-Maj-V) et modifiez ses couleurs pour en avoir une blanche, une rouge, une bleue foncée et une jaune.

Et voilà ! Vous avez vos étoiles :D

N'oubliez pas de remettre la couleur d'arrière-plan de la scène sur blanche. En fait, nous pourrions tout aussi bien la laisser noire, mais :

  1. La laisser blanche vous permettra de vérifier que votre fond noir est bien ajouté à la scène
  2. Par usage, je préfère ne pas jouer sur la couleur de la scène et uniquement modifier la couleur du fond à l'aide de clips ou de graphismes.

Nous avons maintenant toute la matière première pour réaliser notre petit champ d'étoiles. Le chapitre suivant vous apprendra, à travers notre fond étoilé, quelques notions fondamentales à savoir sur les classes :). Même si vous pensez tout savoir, je vous conseille d'y jeter un œil, vous y apprendrez sûrement quelque chose. ;)

Le .fla

Comme je suis gentille et que je sais que certains programmeurs sont réticents à l'idée de tracer ne serait-ce que le moindre cercle sur Flash, je vous fournis le .fla avec les deux classes nécessaires et tous les paramètres comme il faut :

Chapitre 2 : Quelques notions fondamentales sur les classes

Documents .fla et .as associés ?

Maintenant que nous avons tous les éléments dans la bibliothèque du .fla, nous pouvons commencer à coder. Dans le cadre de ce tutoriel nous allons créer un fichier ActionScript codant la classe associée à notre document principal (.fla) :

  • Faites “Fichier, Nouveau” (Contrôle + N) puis sélectionnez “Fichier ActionScript”.
  • Enregistrez ce fichier nouvellement créé :
    • Donnez-lui le même nom que celui de la classe que vous avez inscrit dans le champ “classe du document” des propriétés du document (normalement : Starfield.as) cela permet ainsi de lier les deux fichiers,
    • Enregistrez-le dans le même dossier que votre fichier “fla”, cela simplifie toujours la tâche pour s'y retrouver.
Organisation du disque et des chemins de classe :
Il est souvent plus simple - d'un point de vue organisation - d'enregistrer les fichiers de classe (.as) dans le même dossier que votre document principal (.fla), surtout lorsque vous ne manipulez que quelques classes. Pour les applications plus conséquentes, je vous invite à vous mettre au point sur l'organisation du disque et chemin de classe.

Par commodité et par usage, nous avons donné le même nom au fichier .fla et à sa classe. C'est également le même principe que j'utilise pour les clips : leur nom est le même que le nom de la classe qui leur est associé. Je trouve cela plus simple pour s'y retrouver. Le compilateur Flash se charge ensuite intelligemment de vérifier l'existence d'un fichier .as pour chaque classe créée.

Mieux connaître les classes
Pour en savoir plus sur les classes : Tutoriel sur les classes


Package, Classes et propriétés

Pour vous rafraîchir la mémoire, nous allons passer en revu ce que doit toujours contenir une classe et dans quel ordre on les retrouve. Pour des raisons pratiques nous prendrons comme exemple la classe que nous venons juste de créer.

Nom du package

Dans un fichier .as la première ligne de code pour une classe créée est toujours :

package {

Après cette ligne et avant la création d'une classe dans un fichier .as, il est toujours nécessaire de spécifier à Flash de quelles bibliothèques la classe aura besoin. Pour cette classe de document nous aurons besoin de la bibliothèque relative aux Sprites :

	/* Pour cette classe nous aurons besoin de la bibliothèque concernant les Sprites */
	import flash.display.Sprite;

Vous pouvez ensuite insérer des commentaires comme bon vous semble. Il est généralement bien vu d'y placer un commentaire expliquant en bref l'utilité et la fonction de la classe pour le programme où elle intervient. Par exemple :

	/* 
		Classe principale du jeu de shoot de l'espace gérant l'ensemble des éléments du jeu
	*/

Nous pouvons maintenant créer notre classe :

	public class Starfield extends Sprite {
 
	}

Notez le mot clé extends il signifie que la classe que nous créons hérite des méthodes de la classe spécifiée, ici MovieClip. La classe du document doit hériter de la classe MovieClip ou Sprite. Si votre scénario principal compte plusieurs images ce sera forcément un MovieClip, sinon préférez Sprite, c'est beaucoup plus léger.

En fait cette classe de document (aussi souvent appelée classe Main) ne va pas faire grand chose. En AS3 et surtout en POO, il est bon de prendre l'habitude de morceler le code dans différentes classes dont la fonction doit être unique et bien précise. Il en va de même pour les méthodes qui, en bonne pratique, doivent compter moins de 20 lignes de code.

Continuons… Après la déclaration de la classe, viennent les variables et constantes de cette classe dont il est préconisé de les déclarer comme ceci :

	private var _ma_variable:type;
	private static const MA_CONSTANTE:type = valeur;

Notez surtout les préconisations pour les noms le _ au début du nom de variable et les lettres majuscules pour une constante. Concernant les mots clés private et autres, je vous invite à lire le bloc d'information ci-dessous.

Chapitre concernant les attributs et propriétés : les propriétés (attributs)
Petit tutoriel concernant les types de données : types de données primitives
Pour en savoir plus sur les variables : Qu'est-ce qu'une variable ?
Aide Abode, variables
Vous pouvez également trouver de plus amples informations sur tous ces sujets dans l'aide de Flash. N'hésitez pas à y faire une recherche sur les attributs ou les types, vous y apprendrez des choses intéressantes. :)

Sachez que par défaut on n'utilise pas le mot clé public pour des variables. A la place, on les déclare en tant que private et on crée des fonctions que l'on nomme communément Getter / Setter. Autrement dit on récupère les variables et on les modifie à l'aide de fonctions et ce sont ces fonctions qui sont utilisées.

Après la déclaration des variables de classe vient la fonction constructrice de la classe.

Fonction constructrice / méthode constructeur

Pour notre tutoriel nous allons, bien sûr, utiliser une fonction constructrice. Ajoutez-la dès maintenant :

        /* 	
	* Cette fonction est la fonction constructrice de la classe Starfield.
	* Elle fait appel à une fonction initialisant notre jeu. 
        */
        public function Starfield(){
              initialiserJeu();
        }
    } // On referme l'accolade correspondant à la classe
} //et on referme bien l'accolade correspondant au package.
Fonction constructrice :
Aussi appelée méthode constructeur, cet article vous la présente très clairement.

Maintenant que nous avons le squelette de notre classe, continuons sans plus attendre et créons notre fond étoilé. :)

Pour l'instant, notre classe ne fait rien. Avant de continuer à la remplir, nous allons créer une classe Fond associée au clip Fond de la bibliothèque de votre fichier Starfield.fla. Nous aurons en effet besoin de cet élément dans notre classe Starfield.

Chapitre 3 : Des étoiles dans un fond noir !

Ce chapitre est entièrement consacré à la création du fond étoilé.

Classe Fond

En réalité c'est cette classe qui va s'occuper du gros du travail :). Créons la sans plus attendre !

Puisque dans notre bibliothèque du document fla nous avons déjà un clip Fond exporté pour ActionScript 3, nous allons créer une classe associée à ce clip et en créer une instance dans notre classe de document. Comme nous allons créer un véritable jeu et qu'il y aura pas mal de classes, nous allons utiliser le système de package.

Package ?
Pour vous rafraîchir la mémoire : notion de package. Lisez-y également le paragraphe suivant concernant la définition des classes. ;)
Classe d'un symbole :
Pour les classes associées à un symbole de la bibliothèque, je vous invite à lire ce chapitre.

Liaison du symbole avec sa classe

Dans votre dossier où se situe le .fla, créez un nouveau dossier et appelez-le decor. Retournez dans Flash puis sélectionnez votre clip Fond dans la bibliothèque. Faites clique droit puis propriétés. Dans le champ Classe insérez decor.Fond et vérifiez que le champ Classe de base pointe vers Sprite (notre Fond ne nécessitera pas de scénario).

Cliquez ensuite sur le petit crayon à côté du nom de la classe, sélectionnez Flash Professional puis cliquez sur ok. Flash va vous créer directement la nouvelle classe avec le bon nom de package decor qui correspond au chemin des dossiers où sera stocké la classe par rapport au fichier fla. N'oubliez pas d'enregistrer le nouveau fichier dans le dossier decor et avec le même nom que la classe, à savoir Fond, c'est très important. Vous noterez tout le code dors et déjà présent, c'est plutôt utile :).

Et voilà ! Vous avez créé une classe associée à votre clip Fond présent dans la bibliothèque de votre fichier fla. Lorsque vous êtes au sein d'une classe associée à un clip tout se passe comme ci vous interagissiez directement avec ce clip. Par exemple, la ligne de code :

	width = 200;

Modifiera votre clip Fond pour lui donner 200 pixels de largeur.

En réalité ici width équivaut à this.widththis correspond à la classe elle-même. Lors de la création d'une instance de la classe, cela agira directement sur les propriétés de cette instance. Comme nous sommes au sein d'une classe, le this est sous-entendu.

Variables et constantes de la classe Fond

Nous allons maintenant définir les variables de classe dont nous aurons besoin pour notre fond étoile. Il nous faudra donc :

  • D'une constante pour limiter l'augmentation de la vitesse de déplacement des étoiles,
  • D'une constante pour déterminer l'accélération et d'une autre pour la décélération,
  • D'une constante pour définir le nombre d'étoiles désiré,
  • D'un tableau stockant les étoiles créées.


En fait les constantes nous allons les déclarer dans une autre classe. Par usage il semble qu'il soit préférable de rassembler toutes les constantes dans une même classe servant uniquement à les définir et les instancier. Pourquoi préférable ? Car les constantes sont des paramètres que l'on doit pouvoir modifier facilement car très souvent ils correspondent à des caractéristiques fondamentales du jeu comme c'est le cas ici avec la vitesse limite et le nombre d'étoiles générées.

Mais tout d'abord terminons avec les variables de classe de notre fichier Fond.as. Ajoutons le tableau pour stocker les étoiles ainsi que deux variables stockant les dimensions initiales de notre clip Fond.

		/*  Le tableau suivant permettra de stocker chacune des étoiles créées pour 
		 *  y accéder plus facilement 
		 */
		private var _tableau_etoiles:Array;
 
		/* 	2 variables pour stocker la hauteur et la largeur initiale du clip "Fond" */
		private var _largeur_fond:Number, _hauteur_fond:Number;

Il nous reste donc à déclarer et initialiser les constantes.

Classe ''Constantes''

Petit aparté sur la classe chargée de répertorier toutes les constantes. Dans Flash cliquez sur Fichier / Nouveau. Sélectionnez Classe d'ActionScript 3.0. Mettez Constantes comme nom de classe et appuyez sur ok. Cette classe n'a pas besoin d'hériter d'une autre. Elle n'a même pas forcément besoin d'imports. Elle se contente juste de déclarer des constantes et de les initialiser. Donc pour l'heure, elle doit contenir le code suivant :

package  {
	/* Classe chargée de déclarer et d'initialiser les constantes utilisées pour le jeu Starfield */
	public final class Constantes {
 
		/* 	
		 * 	Nous aurons besoin de quelques constantes : 
		 *  - Une pour limiter la vitesse de déplacement
		 *  - Une pour modifier facilement l'accélération des vitesses
		 *  - Une pour modifier facilement les décélérations des vitesse
		 *  - Une pour pouvoir facilement modifier le nombre d'étoiles
		*/
		public static const VITESSE_MAX:int = 5;
		public static const ACCELERATION:Number = 0.3;
		public static const DECELERATION:Number = 0.9;
		public static const NBR_ETOILES:int = 100;
 
		public function Constantes() {
			// constructor code
		}
 
	}
 
}
Pourquoi final ? Final est un mot clé pour indiquer que la classe ne pourra être parente d'aucune autre. C'est par exemple le cas de la classe Math. Vous ne pourrez donc jamais avoir public class MaClasse extends Math. Ca vous affichera une erreur. Il est de bon ton de mettre ce mot clé à une classe comme Constantes où nous sommes sûres qu'elle ne sera jamais parente d'une autre classe.

Et c'est tout :).

Remplissons le fond avec des étoiles !

La fonction constructrice de notre classe Fond ne va pas faire de grandes choses non plus. J'ai pris pour habitude de garder le code des fonctions constructrices aussi simple que possible, j'ai lu quelque part (je ne sais plus où si vous le lisez aussi, dites le moi) que c'était rudement conseillé pour je ne sais plus quelle bonne raison.

Donc ! La fonction constructrice va prendre en paramètre le nombre d'étoiles que notre fond devra contenir et fera appel à une autre fonction qui s'occupera de la création des étoiles à proprement parlé. Nous en profitons pour lui ajouter la fonction “classique” d'initialisation, qui paramétra toutes les propriétés initiales de notre fond.

		/* La fonction constructeur délègue la création des étoiles à une autre fonction
		 * et prend en paramètre le nombre d'étoiles à créer pour faciliter les éventuelles
		 * évolutions du jeu.
		 */
		public function Fond(nbr_etoiles:int) {
			initialiserFond();
 
			remplirDEtoiles(nbr_etoiles);
			addEventListener(Event.REMOVED_FROM_STAGE, nettoyerFond);
		}


Eh mais… Attend ! Pourquoi tu nous as fait créer la constante pour le nombre d'étoiles ? Ah mais c'est une bonne question ! Le fait est que nous utiliserons cette constante dans la classe Starfield.as. Mais ici je place cette information importante en tant que paramètre pour faciliter d'éventuelles évolutions du jeu. On ne sait jamais, peut-être qu'il vous viendrait l'envie de remplir le fond d'un nombre aléatoire d'étoiles pour faire en sorte que le joueur ait l'impression qu'il ne joue jamais dans la même galaxie. De même entre deux niveaux il sera plus simple de modifier le nombre d'étoiles que contient le fond, etc.

Eh ! Moi j'ai une autre question ! C'est quoi ce addEventbiduletruc ?!
Ca aussi c'est une bonne question. La réponse est simple, c'est une bonne pratique. Il faut prendre pour habitude de nettoyer chaque clip lorsque celui-ci est retiré de la scène. Cela se gère à l'aide de cet écouteur d'événement. Lorsque notre Fond sera supprimé de la scène nous ferons en sorte de supprimer également toutes les étoiles qu'il contient. C'est pour des raisons de performances.

Pour ceux qui veulent en savoir toujours plus, en fait tout réside dans le grand mystère qui entoure le fonctionnement du ramasse-miette de Flash. Ce ramasse-miette est chargé de libérer la mémoire de votre ordinateur dès qu'un élément de l'application est supprimé. Oui mais en fait personne ne sait vraiment comment il fonctionne, et on ne sait pas jusqu'à quel niveau il libère la mémoire… Imaginez qu'un de vos éléments possèdent 1 enfant possédant lui-même 1 enfant et lui-même 1 enfant etc… Jusqu'où le ramasse-miette va ? Et qu'en est-il des écouteurs d'événement ? Sont-ils bien supprimés ? Je n'ai pas trouvé de réponses à ces questions, donc la bonne pratique c'est de tout supprimer explicitement de sorte que le ramasse-miette soit sûr qu'il est autorisé à libérer la mémoire occupée par ces éléments.

Un ciel étoilé

Continuons, et codons la fonction remplirDEtoiles. Que doit-elle faire au juste ?

Elle doit :

  • Créer autant d'étoiles que spécifié par la variable nbr_etoiles,
  • Tirer aléatoirement la couleur de l'étoile parmi toutes les couleurs présentes dans le clip Etoiles,
  • Placer aléatoirement ces étoiles de façon à ce qu'elles remplissent toute la largeur et la hauteur du fond,
  • Associer à chaque étoile une vitesse aléatoire qui lui est propre, afin de donner une impression de 3dimensions.
  • Modifier aléatoirement, pour chaque étoile, ses propriétés Alpha (transparence), scaleX et scaleY (sa taille) pour que chaque étoile soit véritablement unique et plus ou moins lumineuse,
  • Ajouter chaque étoile au fond noir puis la stocker dans le tableau tableau_etoiles.
Pourquoi stocker les étoiles dans un tableau ?
Stocker des objets créés dans un tableau est une des multiples façons de faire pour pouvoir accéder facilement à ces objets ultérieurement. Une des applications courantes de cette méthode est un nettoyage propre et net de la scène lorsqu'on arrive à la fin d'un jeu ou d'une animation.

En réalité notre fonction ne va pas faire tout ça, encore une fois, elle va déléguer la tâche. Bon nombre des lignes de code à écrire pour faire ce qui est listé ci-dessus sont plus simples à écrire au sein même de la classe Etoiles. Eh oui, vous l'avez deviné, nous allons créer une classe Etoiles associée à notre clip etoiles.

Une étoile aléatoire s'il vous plait !

Comme les étoiles font également parti du décor, le procédé pour créer la classe est quasi-identique à celui que nous avons suivi pour créer la classe associée au clip Fond. Clic droit sur le clip Etoiles dans la bibliothèque de votre fichier Flash, propriété et zou, on y va.

Dans le champ Classe remplissez decor.Etoiles. Dans le champ Classe de base laissez flash.display.MovieClip, en effet, notre clip contient plusieurs images dans son scénario, autant qu'il y a d'étoiles différentes, c'est donc forcément un MovieClip. Et hop vous êtes près à cliquer sur le petit crayon à droite du champ Classe.

Poum ! On a notre classe toute belle toute vide. Enregistrez-la sans plus attendre dans votre dossier decor. Cette fois nous allons ajouter du code un peu plus actif. Comme nous voulons que les étoiles aient chacune une vitesse propre, vous comprenez rapidement qu'il s'agit là d'une propriété de la classe et donc d'une variable globale de la classe. La fonction constructrice, quant à elle, va encore déléguer.

package decor {
 
	import flash.display.MovieClip;
 
	/*
	 * Cette class est dédiée à la création aléatoire d'une étoile. 
	 * Elle est utilisée au niveau de la classe "Fond" pour créer 
	 * une multitude d'étoiles différentes. 
	 */
	public class Etoiles extends MovieClip {
 
		/* Cette propriété stocke une vitesse propre à chaque étoile. 
		 * C'est elle qui donne une impression de 3 dimensions dans les mouvements
		 */
		private var _vitesse_propre:Number;
 
		/* Fonction constructrice, délègue son travail à la fonction "parametrerEtoile" */
		public function Etoiles(largeur_fond:Number, hauteur_fond:Number) {
			parametrerEtoile(largeur_fond, hauteur_fond);
		}

Que doit faire la fonction parametrerEtoile ? Elle doit :

  • Tirer aléatoirement la couleur de l'étoile parmi toutes les couleurs présentes dans le clip Etoiles,
  • Associer à chaque étoile une vitesse aléatoire qui lui est propre, afin de donner une impression de 3dimensions,
  • Définir aléatoirement les positions x et y de chaque étoile en fonction de la taille et la hauteur du fond, cela aura pour effet de placer aléatoirement chaque étoile,
  • Modifier aléatoirement, pour chaque étoile, ses propriétés Alpha (transparence), width et height (sa taille) pour que chaque étoile soit véritablement unique et plus ou moins lumineuse (effet obtenu grâce à une modification de la propriété alpha).

Vous voyez qu'elle se charge de 4 des fonctionnalités initialement prévues pour la fonction remplirDEtoiles de la classe Fond.

Vous pouvez ainsi comprendre que nous allégeons d'autant le code présent dans chaque classe et qu'il vous sera ainsi plus aisé de vous y retrouver et de modifier le code comme bon vous semble. Si on crée autant de classes ce n'est pas par plaisir (en fait si) mais c'est surtout pour se simplifier la vie pour plus tard (dans 3 ans que vous voudrez revoir le code).

Dans un premier temps nous voulons tirer aléatoirement une couleur d'étoile parmi toutes celles que nous avons pu créer. Pour ce faire nous aurons besoin de deux fonctions : Math.random() et gotoAndStop(). Et comme je suis gentille, je vais vous donner une fonction aléatoire très pratique et toute faite permettant de tirer un nombre entier ou non entre un minimum et un maximum et de façon inclusive ou exclusive. Suis sympa hein ? (Comment ça vous avez rien compris à ce que je viens de dire ?).

Aparté sur un tirage aléatoire

Vous pouvez créer la fonction tirageAleatoire soit dans la classe Etoiles où elle sera utilisée soit dans une autre classe que vous créerez.

Personnellement je vous conseille de créer une nouvelle classe que vous nommerez Fonctions ou FonctionsStarfield et qui contiendra toutes les fonctions que vous serez amené à réutiliser très souvent dans de multiples projets différents. J'ai même tendance à regrouper ces classes dans un dossier code commun voire dans un dossier externe et utilisé par tous mes projets Flash.

Moi je vais créer une classe spécifique, car m'est avis que cette fonction nous resservira plus tard pour notre jeu ;). Je pense que vous connaissez la marche à suivre, allez, une petite dernière fois. Sous Flash, faites fichier, nouveau, classe d'actionscript 3.0, Fonctions dans le champ Nom de classe et Ok.

Cette classe tout comme la classe Constantes ne sera parente d'aucune autre, on lui ajoute donc le mot clé final et on l'enregistre à la racine de notre projet.

Dans cette classe, j'ajoute donc ma fonction tirageAleatoire qui doit prendre en paramètre un minimum, un maximum, et deux booléens pour indiquer selon si elle doit être inclusive ou exclusive et si elle doit retourner un entier ou non. Cette fonction doit également être déclarée comme static de sorte qu'elle puisse être appelée directement sur la classe, sans avoir à créer d'instance.

Nous testons tout d'abord que le min est bien inférieur au max, dans le cas contraire on renvoie une erreur :

package  {
 
	/* Classe chargée de stocker quelques fonctions bien utiles */
	public final class Fonctions {
 
		public function Fonctions() {
 
		}
 
		/* Tirage aléatoire : prend un minimum/maximum de type Number, 
		 * La propriété "inclusive" permet d'inclure (true) ou non (false) le maximum dans le tirage. 
		 * La propriété "entier" force l'arrondi du résultat à l'entier inférieur si elle est fixée à "true". 
		 */
	public static function tirageAleatoire(min:Number, max:Number, inclusive:Boolean=true,   entier:Boolean=true):Number{
		var tirage:Number;
		if (min <= max) {
 
		}else {
			throw(new Error(''Attention mauvaise valeur pour l'aléatoire : min > max''));
		}
inclusive:Boolean=true : c'est quoi ce =true ?
C'est très simple, il permet d'indiquer une valeur par défaut au paramètre de la fonction. Ces paramètres sont alors facultatifs. Bien faire attention que tout paramètre obligatoire (sans valeur par défaut) doit se trouver avant tout paramètre facultatif, autrement vous aurez une erreur ^^.
Ici, par défaut, notre fonction est donc inclusive et elle renvoie un entier compris entre min et max.

Nous testons ensuite si le tirage doit être inclusif (inclut le max) ou exclusive (exclut le max) et si elle doit retourner un entier (on arrondit) ou non (on arrondit pas) et selon la valeur de ces booléens nous adaptons la formule pour avoir au final :

		public static function tirageAleatoire(min:Number, max:Number, inclusive:Boolean=true, entier:Boolean=true):Number{
			var tirage:Number;
			if (min <= max) {
				if (inclusive) tirage = min + Math.random() * (max - min + 1);
				else tirage = min + Math.random() * (max - min);
 
				if (entier) return Math.floor(tirage);
				else return tirage;
			}else {
				throw(new Error(''Attention mauvaise valeur pour l'aléatoire : min > max''));
			}
		}

Remarquez Math.floor qui arrondit le nombre à sa valeur inférieure, sachant que Math.random() tire un nombre entre 0 et 1 exclus, cette méthode fonctionne très bien :).

Fin de l'aparté


Dans la fonction parametrerEtoile on tire donc la couleur de l'étoile aléatoirement. Grâce à la propriété total_frames qui contient le nombre d'images totales du scénario du MovieClip associé à la classe, on obtient ainsi le maximum à passer à notre nouvelle fonction aléatoire. Le tout se fait en deux ligne de code :

		/* Fonction appelée dès la création d'une étoile et paramétrant chaque étoile 
                 * différemment grâce à des tirages aléatoires. 
                 */
		private function parametrerEtoile():void 
		{
			/* On tire un nombre aléatoire entre 1 et le nombre total d'images différentes 
			 * dans notre clip étoile, on accède ensuite à l'image correspondante à ce nombre 
			 * grâce à ''gotoAndStop'' s'opérant directement sur le clip ''étoile'' (souvenez
			 * vous du ''this'' facultatif). 
			 */
			var num_image:int = Fonctions.tirageAleatoire(1, totalFrames);
			gotoAndStop(num_image);


Bien ! Maintenant que vous avez compris le principe, les trois tâches qu'il nous reste à développer doivent vous paraître plus simples. Donc, sans plus tarder occupons-nous de donner une valeur aléatoire à la propriété _vitesse_propre de notre classe Etoile. Cette propriété étant de type Number nous nous payons le luxe de tirer un nombre non entier aléatoirement, de cette façon aucune étoile n'aura exactement la même vitesse.

		/* Fonction appelée dès la création d'une étoile et paramétrant chaque étoile 
                 * différemment grâce à des tirages aléatoires. 
                 */
		private function parametrerEtoile():void 
		{
			/* On tire un nombre aléatoire entre 1 et le nombre total d'images différentes 
			 * dans notre clip étoile, on accède ensuite à l'image correspondante à ce nombre 
			 * grâce à ''gotoAndStop'' s'opérant directement sur le clip ''étoile'' (souvenez
			 * vous du ''this'' facultatif). 
			 */
			var num_image:int = Fonctions.tirageAleatoire(1, totalFrames);
			gotoAndStop(num_image);
 
			/* On tire une vitesse propre aléatoirement comprise entre 0.1 et 5 */
			_vitesse_propre = Fonctions.tirageAleatoire(0.1, 5.0, true, false);
		}

Remarquez que le minimum pour notre tirage vaut 0.1, en effet, même si la probabilité est très faible, je préfère éviter d'avoir des étoiles statiques (mais rien ne vous empêche de mettre 0 ^^). Je passe donc le deuxième paramètre booléen à false pour récupérer une valeur de type Number.

Pour la suite, rappelons quelques propriétés fondamentales d'un MovieClip que notre classe Etoile possède également grâce à l'héritage (i.e. grâce à ce petit morceau de code : extends MovieClip) :

  • mon_clip.x : coordonnée de mon_clip selon l'axe des abscisses (x),
  • mon_clip.y : coordonnée de mon_clip selon l'axe des ordonnées (y),
  • mon_clip.alpha : propriété de transparence du clip,
  • mon_clip.scaleX : échelle de mon_clip selon l'axe des X,
  • mon_clip.scaleY : échelle de mon_clip selon l'axe des Y.

Rappelons également que l'axe des ordonnées est orienté vers le bas dans Flash. Ainsi un clip dont l'ordonnée vaut 0 se retrouvera tout en haut de la scène. Passons aux choses sérieuses, le tirage aléatoire de ces différentes propriétés ne va guère être différent de ce que nous venons de faire pour la vitesse. Seule les propriétés x et y nous demanderons un peu plus de travail.

Commençons par le plus simple :

			/* On tire une vitesse propre aléatoirement comprise entre 0.1 et 5 */
			_vitesse_propre = Fonctions.tirageAleatoire(0.1, 5.0, true, false);
			/* On tire la propriété alpha et la taille de l'étoile
			 * aléatoirement, cela donne l'impression que les étoiles sont plus ou moins lumineuses 
			 * et plus ou moins grande et renforce la sensation de 3 dimensions. 
			 */
			this.alpha = Fonctions.tirageAleatoire(0.1, 1, true, false);
			this.scaleX = this.scaleY = Fonctions.tirageAleatoire(0.1, 1, true, false);

C'est pas plus compliqué que ça. Cette fois j'ai indiqué le this pour que l'on ait bien conscience que l'on agit directement sur les propriétés de nos (futurs) clips Etoile. Notez bien que pour que nos étoiles restent parfaitement ronde nous faisons en sorte de leur donner la même échelle en largeur que en hauteur grâce à this.scaleX = this.scaleY = ….

Reste les propriétés x et y. Pour celles-ci nous allons avoir besoin de la largeur du clip de fond et de sa hauteur en pixels. Notre classe étoile a donc besoin de les récupérer. Pour ce faire, nous allons les passer en paramètre lors de la création d'une nouvelle étoile, donc au sein de la fonction constructrice :

		public function Etoiles(largeur_fond:Number, hauteur_fond:Number) {
			parametrerEtoile(largeur_fond, hauteur_fond);
		}

Nous passons tout de suite ces deux nouvelles variables à notre fonction parametrerEtoile puisque c'est elle qui est chargée de modifier les propriétés de nos étoiles. Le tirage aléatoire des propriétés x / y se fait donc entre 0 et largeur_fond / hauteur_fond. Notre fonction parametrerEtoile devient donc finalement :

		/* 
		 * Fonction chargée de paramétrer chaque étoile, elle prend en paramètre
		 * la largeur et la hauteur du fond pour tirer les coordonnées x/y
		 * aléatoirement. 
		 */
		private function parametrerEtoile(largeur_fond:Number, hauteur_fond:Number):void 
		{
			/* On tire un nombre aléatoire entre 1 et le nombre total d'images différentes 
			 * dans notre clip étoile, on accède ensuite à l'image correspondante à ce nombre 
			 * grâce à ''gotoAndStop'' s'opérant directement sur le clip ''étoile'' (souvenez
			 * vous du ''this'' facultatif). 
			 */
			var num_image:int = Fonctions.tirageAleatoire(1, totalFrames);
			gotoAndStop(num_image);
 
			/* On tire une vitesse propre aléatoirement comprise entre 0.1 et 5 */
			_vitesse_propre = Fonctions.tirageAleatoire(0.1, 5.0, true, false);
			/* On tire la propriété alpha et la taille de l'étoile
			 * aléatoirement, cela donne l'impression que les étoiles sont plus ou moins lumineuses 
			 * et plus ou moins grande et renforce la sensation de 3 dimensions. 
			 */
			this.alpha = Fonctions.tirageAleatoire(0.1, 1, true, false);
			this.scaleX = this.scaleY = Fonctions.tirageAleatoire(0.1, 1, true, false);
 
			/* On tire enfin les coordonnées aléatoirement */
			this.x = Fonctions.tirageAleatoire(0, largeur_fond, true, false);
			this.y = Fonctions.tirageAleatoire(0, hauteur_fond, true, false);
		}

Et voilà ! Nous en avons terminé pour notre classe Etoile, toutes nos étoiles seront différentes les unes des autres.

Un joli ciel étoilé, enfin !

Nous pouvons enfin nous occuper de notre fonction remplireDetoiles de la classe Fond. Alors… Comment faire pour créer autant d'étoiles que la valeur de la constante nbr_etoiles ? Une des méthodes consiste à utiliser une boucle for. Nous allons justement utiliser cette boucle pour notre programme :

		/* Fonction remplissant le fond d'étoiles */
		private function remplirDEtoiles(nbr_etoiles:int):void 
		{
			var etoile:Etoiles; //On déclare notre variable étoile
 
			/* Pour chaque étoile devant être créée : */
			for (var i:int = 0; i < nbr_etoiles; i++) {

Ici, on commence à i=0 et on augmente i de 1 à chaque étape. On arrête la boucle lorsque i vaut 100, on créera donc 100 étoiles.

On crée ensuite une nouvelle étoile. Il est important que cette ligne figure dans la boucle for, autrement vous modifieriez 100 fois la même étoile. Notez que la fonction constructrice Etoiles attend en paramètre la largeur et la hauteur du fond. Nous allons donc récupérer ces valeurs AVANT la boucle for. (Pour savoir pourquoi, lisez le TIP ci-après). Enfin nous ajoutons les étoiles à notre fond et les stockons dans notre _tableau_etoiles. Voici donc nos nouveaux morceaux de code :

Variables globales :

		/* 	Le tableau suivant permettra de stocker chacune des étoiles créées pour 
		 *  y accéder plus facilement 
		 */
		private var _tableau_etoiles:Array;
 
		/* 	2 variables pour stocker la hauteur et la largeur initiale du clip "Fond" */
		private var _largeur_fond:Number, _hauteur_fond:Number;

Fonction constructrice :

		/* La fonction constructeur délègue la création des étoiles à une autre fonction
		 * et prend en paramètre le nombre d'étoiles à créer pour faciliter les éventuelles
		 * évolutions du jeu.
		 */
		public function Fond(nbr_etoiles:int) {
			initialiserFond();
 
			remplirDEtoiles(nbr_etoiles);
			addEventListener(Event.REMOVED_FROM_STAGE, nettoyerFond);
		}

Fonction intialiserFond :

		/* Fonction initialisant quelques paramètres pour notre fond */
		private function initialiserFond():void {
			_largeur_fond = this.width;
			_hauteur_fond = this.height;
			_tableau_etoiles = new Array();
		}

Fonction remplirDetoiles :

		/* Fonction remplissant le fond d'étoiles */
		private function remplirDEtoiles(nbr_etoiles:int):void 
		{
			var etoile:Etoiles; //On déclare notre variable étoile
 
			/* Pour chaque étoile devant être créée : */
			for (var i:int = 0; i < nbr_etoiles; i++) {
				/* On crée un nouvel objet étoile, on dit qu'on crée une instance de la classe Etoiles */
				etoile = new Etoiles(_largeur_fond, _hauteur_fond);
				//trace("largeur " + this.width + " hauteur " + this.height);
				/* On ajoute enfin chaque étoile au fond et au tableau devant les stocker */
				addChild(etoile);
				_tableau_etoiles.push(etoile);
			}
		}

Fonction nettoyerFond

		/* Fonction chargée du nettoyage du fond pour libérer efficacement la mémoire
		 * lors de la suppression du clip Fond 
		 */ 
		private function nettoyerFond(e:Event):void 
		{
			removeEventListener(Event.REMOVED_FROM_STAGE, nettoyerFond);
 
			for each(var etoile:Etoiles in _tableau_etoiles) {
				removeChild(etoile);
			}
			_tableau_etoiles = [];
		}

Nous en avons profité pour terminer la fonction nettoyerFond qui supprimera toutes les étoiles lorsque le fond sera retiré de la scène.

Pourquoi avoir utilisé _largeur_fond et _hauteur_fond plutôt que directement this.width et this.height ?
Bonne question ! Et la réponse est toute simple : un clip enfant ajouté à un autre clip parent ne peut pas sortir de ce dernier. Cela signifie que le clip parent sera redimensionné pour que ses clips enfants soient toujours totalement inclus dans le clip parent. Il en va de même pour la scène. C'est pour cela qu'il vaut mieux utiliser des constantes ou des propriétés pour la largeur et la hauteur des clips si on doit les utiliser dans le code, car les propriétés width et height seront modifiées dynamiquement et vous n'aurez pas l'effet voulu.
Dans notre cas ce n'est pas gênant pour le moment mais dès que nous devrons déplacer les étoiles, si nous récupérons this.width et this.height pour les limites du fond lors du déplacement, le fond s'agrandira indéfiniment et les étoiles ne seront jamais replacées de l'autre côté de la scène. Pour vous convaincre de mes dires, enlever les slash de commentaire à la ligne : trace(largeur + this.width + hauteur + this.height); et à la place de _largeur_fond et _hauteur_fond lors de la création des étoiles mettez plutôt : etoile = new Etoiles(this.width, this.height); Vous verrez que la largeur et la hauteur du fond seront légèrement modifiées.
Faites bien attention à ces petits pièges de Flash où certaines propriétés des clips parents peuvent être modifiées selon ce qu'ils contiennent et prenez l'habitude d'utiliser des fonctions trace pour bien voir ce qu'il se passe sur la propriété à laquelle vous souhaitez accéder.



Très bien, jusque là vous me suivez ? Pour pouvoir enfin afficher notre fond étoilé il ne nous reste plus qu'à placer le fond sur notre scène dans notre classe principale. Donc, de retour dans Starfield.as nous allons créer un nouveau fond et l'ajouter à notre jeu.

La fonction constructrice de notre classe de document est donc toute simple :

		/* 	
		 * 	Cette fonction est la fonction constructrice de la classe Starfield.
		 * 	Elle fait appel à une fonction initialisant notre jeu. 
		 */
		public function Starfield(){
			initialiserJeu();
		}

La fonction initialiserJeu s'occupe de tout le boulot concernant… l'initialisation du jeu et entre autre le placement du décor. Avant de poursuivre, nous allons ajouter une variable globale de classe que je nomme objets visant à stocker tous les enfants que nous ajoutons à notre classe Starfield :

		/* Déclaration du tableau ''objets'' stockant tous les enfants de Starfield */
		private var _objets:Array; 
 
		/* 	
		 * 	Cette fonction est la fonction constructrice de la classe Starfield.
		 * 	Elle fait appel à une fonction initialisant notre jeu. 
		 */
		public function Starfield(){
			initialiserJeu();
		}
 
		/* Fonction chargée d'initialiser le jeu */
		private function initialiserJeu():void {
			var fond:Fond = new Fond(Constantes.NBR_ETOILES);
			// Initialisation du tableau objets
			_objets = new Array();
		}

J'utilise principalement le tableau objets pour supprimer tous les enfants d'une classe lorsque cette classe est supprimée. Dans le cadre de la classe Starfield le tableau objets nous servira à nettoyer notre scène entre deux phases de jeu (entre la page de présentation et le jeu, par exemple).

Il ne nous reste plus qu'à ajouter notre fond :

		private function initialiserJeu():void {
			var fond:Fond = new Fond(Constantes.NBR_ETOILES);
			// Initialisation du tableau objets
			_objets = new Array();
 
			addChild(fond);
			_objets.fond = fond;
		}

On crée donc un nouveau fond en passant notre constante NBR_ETOILES, on ajoute le fond en tant qu'enfant de notre document et on stocke le fond dans le tableau des objets. Notez cette façon particulière de stocker le fond _objets.fond. Cette méthode permet d'accéder beaucoup plus facilement à notre fond par la suite sans avoir à créer de nouvelle variable globale spécifiquement pour le fond.


Et voilà, un petit ctrl + entrée vous permettra de tester votre code. Vous devriez voir un fond étoilé s'afficher :



Bien ! Ajoutons du mouvement à tout ça !

Chapitre 4 : déplaçons les étoiles

Pour déplacer quelque chose en Flash il n'y a pas 36 manières, cela passe forcément au travers d'une interaction avec le joueur et pour gérer ça il faut des écouteurs d'événements.

Nous commencerons donc par gérer les interactions avec le clavier.

Gestion des événements clavier

Ce sera notre classe de document qui va se charger de gérer les interactions avec le joueur. Je trouve cela plus simple d'avoir toutes les gestions des interactions regroupées au même endroit dans le code et quoi de plus naturel que ce soit dans la classe principale, sommes toute elle est la mieux placée pour faire le pont entre ce que fait le joueur et ce que doit faire le jeu.

Ajoutons sans plus attendre les variables globales dont nous allons avoir besoin :

		/*
			Nous avons encore besoin de quelques variables : 
				- 1 booléen par direction, astuce couramment utilisée pour bien gérer
					les appuis sur les touches fléchées
				- 1 variable de vitesse par direction que, par ''usage (mathématique)'' on appelle 
					- dx pour la vitesse horizontale
					- dy pour la vitesse verticale
				- 1 timer pour gérer la fréquence de la mise à jour des éléments
		*/
		private var _haut:Boolean, _bas:Boolean, _gauche:Boolean, _droite:Boolean;
		private var _dx:Number, _dy:Number;
		private var _timer:Timer;

Les booléens servent à enregistrer quelles touches sont enfoncées. Les variables _dx et _dy enregistre et mettent à jour respectivement les vitesses selon l'axe des abscisses et selon l'axe des ordonnées. Enfin, la variable _timer sera un timer s'exécutant toutes les X milli-secondes pour lancer la mise à jour des différents éléments du jeu (où X est inférieur ou égal au FrameRate de votre animation).

Lors de l'initialisation du jeu nous initialisons le Timer puis ajoutons les écouteurs d'événements qui vont bien :

		private function initialiserJeu():void {
			var fond:Fond = new Fond(Constantes.NBR_ETOILES);
			// Initialisation du tableau objets
			_objets = new Array();
 
			addChild(fond);
			_objets.push(fond);
 
			_timer = new Timer(20);
			_timer.addEventListener(TimerEvent.TIMER, miseAJour);
			_timer.start();
 
			stage.addEventListener(KeyboardEvent.KEY_DOWN, toucheEnfoncee);
			stage.addEventListener(KeyboardEvent.KEY_UP, toucheRelachee);
		}

J'ai choisi 20 milli-secondes comme laps de temps entre deux mises à jour de nos éléments. C'est en général un chiffre qui va bien : ni trop long ni trop court. Faites bien attention que nous ajoutons les écouteurs d'événement à la scène. Vous pourrez essayer de les ajouter à la classe principale (sans stage) et vous verrez qu'il ne se passe rien.

Pourquoi un Timer plutôt que l'écoute de l'événement ENTER_FRAME ? Pourquoi êtes-vous si curieux ? Non mais si en fait vous avez raison, allez donc voir cette discussion (en fait dans le même sujet que celui associé à ce tutoriel) pour connaître toutes les réponses à ce sujet.

Les fonctions que nous avons appelées toucheEnfoncee et toucheRelachee ne sont pas complexes. Nous utiliserons cette méthode pour gérer l'appui des touches au clavier. Commençons par programmer la fonction toucheEnfoncee. En tant que méthode recevant un événement KeyboardEvent elle doit récupérer un paramètre de type KeyboardEvent :

		/* 	Fonction s'exécutant lors de l'appuie d'une touche
		 * 	modifie simplement les différents bouléens 
		 */
		private function toucheEnfoncee(e:KeyboardEvent):void 
		{
			switch(e.keyCode) {
				case Keyboard.LEFT: 
					_gauche = true;
					break;
				case Keyboard.RIGHT:
					_droite = true;
					break;
				case Keyboard.UP:
					_haut = true
					break;
				case Keyboard.DOWN:
					_bas = true;
					break;
				default: 
					trace ("Attention, touche " + e.charCode + " non gérée");
					break;
			}
		}
J'ai appris qu'il faut toujours mettre un default dans une instruction switch, cela fait parti des bonnes pratiques. De plus, dans notre cas, vous savez comme ça si la touche que vous appuyez est gérée ou non. Dans le cas où cette touche est sensée faire quelque chose, cela peut rapidement vous indiquer que vous avez simplement oublié d'ajouter sa gestion dans l'instruction switch ;). Donc d'une manière générale, mettez toujours au minimum une information permettant de mieux débugger votre application dans le cas default. Pour vous rafraîchir la mémoire, le cas default est celui exécuté si aucun autre cas n'est vrai.


La fonction toucheRelachee fonctionne sur le même procédé, rien de nouveau ici :

		/* 	Fonction s'exécutant lors du relâchement d'une touche
		 * 	modifie simplement les différents bouléens 
		 */
		private function toucheRelachee(e:KeyboardEvent):void 
		{
			switch(e.keyCode) {
				case Keyboard.LEFT: 
					_gauche = false;
					break;
				case Keyboard.RIGHT:
					_droite = false;
					break;
				case Keyboard.UP:
					_haut = false;
					break;
				case Keyboard.DOWN:
					_bas = false;
					break;
				default: 
					trace ("Attention, touche " + e.charCode + " non gérée");
					break;
			}
		}


Reste à créer notre fonction miseAJour. Cette dernière doit se charger de mettre à jour la vitesse à laquelle se déplace notre environnement et fera appel à la fonction deplacerEtoiles de notre classe Fond que nous n'avons pas encore créée. Nous allons faire tout ça dans la partie suivante.

Déplacement des étoiles

La fonction miseAJour est appelée à chaque diffusion de l'événement TIMER de notre timer. A notre cadence, elle s'exécute donc 20 fois par seconde. Cette fonction est la plus importante de ce code et s'occupe de beaucoup de choses. Que doit-elle faire ?

  • Tester si une touche fléchée est enfoncée et incrémenter/décrémenter _dx ou _dy selon la direction en faisant bien attention de ne pas dépasser la constante vitesse_limite que nous avons défini au tout début. Le rendu sera une accélération,
  • Faire en sorte de faire tendre _dx/_dy vers 0 si aucune touche n'est appuyée. Le rendu sera une décélération,
  • Mettre à jour tous les éléments de notre jeu en appelant les bonnes fonctions et en leur donnant toutes les informations nécessaires. Le rendu sera le déplacement d'une centaine d'étoiles ^^.

Bon ! … Nous allons attaquer chaque problème un à un. Commençons par gérer l'accélération des étoiles lors d'un appui sur une ou plusieurs touches. Avant toutes choses, initialisons les variables _dx / _dy dans la fonction d'initialisation du jeu :

		private function initialiserJeu():void {
			var fond:Fond = new Fond(Constantes.NBR_ETOILES);
			// Initialisation du tableau objets
			_objets = new Array();
 
			addChild(fond);
			_objets.fond = fond;
 
			_dx = 0;
			_dy = 0;
			//[...]
		}

Nous pouvons maintenant modifier les valeurs de ces deux variables selon les touches appuyées, que nous connaissons grâce aux booléens introduits auparavant.

		/* Fonction chargée de mettre à jour l'univers de notre jeu */
		private function miseAJour(e:TimerEvent):void 
		{
			/* 	Les 4 tests suivants regardent quelle touche a été enfoncée.
			Si la vitesse dans la direction considérée ne dépasse pas la vitesse
			limite alors on l'incrémente / décrémente (selon la direction). 
			C'est ce qui donne cette impression d'accélération rapide. */
			if(_gauche && _dx <= Constantes.VITESSE_MAX){
				_dx += Constantes.ACCELERATION;	
			}
			if(_droite && _dx >= - Constantes.VITESSE_MAX){
				_dx -= Constantes.ACCELERATION;
			}
			if(_haut && _dy <= Constantes.VITESSE_MAX){
				_dy += Constantes.ACCELERATION;
			}
			if(_bas && _dy >= - Constantes.VITESSE_MAX){
				_dy -= Constantes.ACCELERATION;
			}
		}

Tout est déjà expliqué dans le commentaire. Décortiquons le premier if pour que vous compreniez bien :
if(_gauche && _dx ⇐ Constantes.VITESSE_MAX)
Si le booléen _gauche vaut true (donc si la touche fléchée Gauche est enfoncée) et si _dx est inférieur ou égal à la vitesse limite alors :
_dx += Constantes.ACCELERATION
On augmente la variable _dx de 0,3. Cette écriture équivaut à : dx = dx + 0.3. Le principe est le même pour les autres conditions.

Astuce pour la gestion des diagonales :
Le fait d'enchaîner ainsi les blocs if plutôt que des blocs if… else… permet de tester chaque booléen et donc l'appui simultané de 2 touches fléchées. On peut alors se déplacer en diagonale.


Occupons-nous de faire ralentir les étoiles si aucune touche n'est enfoncée ou si les touches de direction opposée sont enfoncées en même temps. (Si vous allez en même temps à droite et à gauche, c'est donc que vous ne bougez plus ^^) :

			/* 	Les 2 tests suivants étudient si on n'appuie plus sur une des touches de chaque direction 
			ou si on appuie sur les touches de sens contraire. Si c'est le cas, on réduit 
			progressivement les variables _dx / _dy, c'est ce qui produit cet effet de décélération */
			if(_gauche == _droite){
				_dx *= Constantes.DECELERATION;
			}
			if(_haut == _bas){
				_dy *= Constantes.DECELERATION;
			}

Là encore j'ai tout expliqué dans les commentaires, mais nous allons tout de même décortiquer le premier if :
if(gauche == droite)
Si les booléens _gauche et _droite valent tous les deux false ou si ils valent tous les deux true alors on exécute :
_dx *= 0.9
Ce code équivaut à _dx = _dx * 0.9, on multiplie donc _dx par 0.9 ce qui permet de le faire tendre vers 0 qu'il soit négatif (déplacement vers la droite) ou positif (déplacement vers la gauche).

Pour faciliter les modifications de l'accélération des étoiles et du facteur de décélération, notez que j'ai mis ces valeurs dans ma classe Constantes :

	/* Classe chargée de déclarer et d'initialiser les constantes utilisées pour le jeu Starfield */
	public final class Constantes {
 
		/* 	
		 * 	Nous aurons besoin de quelques constantes : 
		 *  - Une pour limiter la vitesse de déplacement
		 *  - Une pour modifier facilement l'accélération des vitesses
		 *  - Une pour modifier facilement les décélérations des vitesse
		 *  - Une pour pouvoir facilement modifier le nombre d'étoiles
		*/
		public static const VITESSE_MAX:int = 5;
		public static const ACCELERATION:Number = 0.3;
		public static const DECELERATION:Number = 0.9;
		public static const NBR_ETOILES:int = 100;


Déplacements en douceur :
Rappelez-vous que la fonction miseAJour est exécutée lors de la diffusion de l'événement TimerEvent.TIMER. Les variables _dx et _dy varient donc en douceur grâce aux décrémentations/incrémentations/multiplications et grâce au fait que la fonction s'exécute régulièrement. Le fait que leur type soit Number est également très important, car nous pouvons ainsi les faire évoluer plus doucement que si elles étaient des entiers. Les valeurs ont été trouvées après plusieurs tests pour un bon rendu final.

Reste à déplacer chacune des 100 étoiles en fonction des valeurs de _dx et _dy.

Boucle principale

Pour déplacer les étoiles il faut pouvoir accéder à chacune d'elles. Heureusement, nous avons pensé à stocker chaque étoile dans un tableau dans notre classe Fond ! Il ne nous reste plus qu'à intervenir sur chaque élément du tableau _tableau_etoiles. Nous allons donc balayer ce tableau et déplacer chaque étoile en fonction de sa vitesse propre et des variables _dx, _dy.

Nous créons une fonction deplacerEtoiles au sein de la classe Fond et la déclarons comme public de sorte qu'on puisse l'appeler dans notre classe principale. Cette fonction recupère les valeurs de _dx et _dy que nous avons modifiées dans la fonction miseAJour de la classe principale. Une fois ces valeurs récupérées, il suffit de balayer le tableau des étoiles et de modifier leurs coordonnées en multipliant leur vitesse propre par dx/dy respectivement.

Vous vous souvenez que nous avions assigné une vitesse propre tirée aléatoirement à chaque étoile ? C'est ici qu'elle va donc intervenir. Le hic c'est que nous avons déclaré cette variable en tant que “private” au sein de la classe “Etoiles”. Nous allons donc créer ce que les programmeurs ont l'habitude d'appeler un “getter”. C'est rien de compliqué, c'est une fonction qui, comme son nom l'indique, permet de récupérer la valeur d'une variable. Voici sa syntaxe :

Dans le fichier Etoiles.as :

		public function get vitesse_propre():Number 
		{
			return _vitesse_propre;
		}

Vous voyez le mot clé “get” ? C'est lui qui indique que cette fonction est un “getter” et qui permet d'accéder à la valeur de la variable _vitesse_propre de la façon suivante : “etoile.vitesse_propre”. Donc, tout se passe comme si nous avions déclaré la variable comme “public” sauf que ce n'est pas le cas. La différence majeure est que si vous essayez d'assigner une valeur de cette façon : “etoile.vitesse_propre = 5”, si la variable est déclarée en tant que public, ce sera possible, ici, cela retournera une erreur. Bien entendu, c'est toujours possible mais il faut alors créer ce qu'on appelle un “setter”. Donc la syntaxe est la suivante :

		public function set vitesse_propre(vitesse:Number):Number 
		{
			_vitesse_propre = vitesse;
		}

Mais ici, nous n'en aurons pas besoin. Attelons-nous plutôt à compléter la fonction “deplacerEtoiles”, maintenant que nous avons tous les éléments :

		public function deplacerEtoiles(dx:Number, dy:Number):void {
			/* Pour chaque étoile */
			for each(var etoile in _tableau_etoiles){
                                /* on les déplace selon leur vitesse et les dx/dy actuels */
				etoile.x += etoile.vitesse_propre*dx;
				etoile.y += etoile.vitesse_propre*dy;
			}
		}

Pour chaque étoile on incrémente leur coordonnée x / y en fonction de la valeur dx / dy multiplié par leur vitesse propre que nous récupérons à l'aide du code tableau_etoiles[i].vitesse. Il reste un problème : si vous testez, vous voyez que vos étoiles partent pour ne jamais revenir… (sauf si vous allez dans la direction opposée bien sûr).

Il nous reste à gérer le mouvement perpétuel. L'astuce consiste à déplacer instantanément une étoile d'une extrémité à l'autre du fond lorsque celle-ci arrive près d'un bord. Cette manipulation doit se faire dans la boucle déplaçant chaque étoile. C'est bête mais fallait y penser ;). Il n'y a rien de nouveau ici, ce ne sont que des instructions if modifiant les coordonnées x/y des étoiles lorsque les conditions sont réalisées :

		public function deplacerEtoiles(dx:Number, dy:Number):void {
			/* Pour chaque étoile */
			for each(var etoile in _tableau_etoiles){
               		 /* on les déplace selon leur vitesse et les dx/dy actuels */
				etoile.x += etoile.vitesse_propre*dx;
				etoile.y += etoile.vitesse_propre * dy;
				/* 	Si les étoiles dépassent l'écran d'un bord ou un autre on les fait réapparaître
					de l'autre côté. C'est ce qui donne le mouvement perpétuel */
				if(etoile.x > _largeur_fond){
					etoile.x = 0;
				}else if(etoile.x < 0){
					etoile.x = _largeur_fond;
				}
 
				if(etoile.y > _hauteur_fond){
					etoile.y = 0;
				}else if(etoile.y < 0){
					etoile.y = _hauteur_fond;
				}
			}
		}

Décortiquons la première condition, c'est tout simple : si la valeur de la coordonnée x de l'étoile dépasse la largeur du fond, alors on déplace instantanément l'étoile de l'autre côté du fond, donc à x = 0. Le else if est là pour la forme… On suit juste l'idée logique que si l'étoile est à un bord de l'écran elle n'est pas en même temps sur le bord opposé de l'écran.

Conclusion

Et voilà ! Nous avons terminé le code pour cette animation. Pour les expérimentés, je suis désolée si vous vous êtes ennuyés, mais il était bien question ici d'un tutoriel ludique pour les débutants en ActionScript 3 et en programmation orientée objet.

Si tout s'est bien passé, vous pouvez tester votre animation et vous devriez avoir ceci :

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.


Fin

Merci de m'avoir lue ! :) J'espère sincèrement que cette lecture vous aura été profitable. A partir de maintenant, vous devriez être capable de créer n'importe quel fond déroulant (ou presque).

Section "Code à connaître"

addChild()

addChild() est une méthode permettant de faire apparaître à l'écran (sur la scène) l'objet d'affichage passé en paramètre. La méthode addChild() peut être appelée uniquement sur des objets d'affichage. Ceux-ci doivent donc déjà faire partie de la liste d'affichage pour que le nouvel objet s'affiche également à l'écran. Le conteneur par défaut est ''this''.
Sachez que le premier conteneur de la liste d'affichage doit forcément être ajouté en tant qu'enfant de l'objet Stage (la scène/l'écran). Vous pouvez également ajouter directement des éléments à la scène avec le code : stage.addChild(nomObjet).
Pour supprimer un objet de l'affichage, il suffit de faire : clipParent.removeChild(clipEnfant).
Pour en apprendre plus sur la liste d'affichage, je vous invite à lire ce chapitre du livre de Thibault Imbert.


Propriétés d'une instance de classe

Pour accéder aux propriétés d'une instance de classe, en AS3, la syntaxe est la suivante :
nominstance.propriété;. N'oubliez pas les méthodes getter/setter et évitez d'utiliser l'attribut public.
Programmeurs AS1/AS2, oubliez totalement le ”_”, il a tout bonnement disparu sauf pour les variables globales mais là c'est nous qui le rajoutons explicitement ^^.

addEventListener

En ce qui concerne cette méthode, je vous invite à lire les articles suivants (qui expliquent tout en détail) :
La méthode addEventListener
La gestion des touches en AS3
Lisez attentivement le deuxième article. ;)
N'oubliez pas la bibliothèque pour les événements :
Notez que vous pouvez utiliser les écouteurs d'événements dans une classe, uniquement si vous avez correctement importé la bibliothèque des événements correspondants. Exemple si vous souhaitez gérer des événements clavier : import flash.events.KeyboardEvent;

Les événements Clavier

Vous trouverez ici : Keyboard - référence, la liste complète des codes des touches.
Si vous voulez en apprendre plus sur la classe KeyboardEvent, c'est par ici : Classe KeyboardEvent

Instruction ''switch''

En ce qui concerne le switch, je vous invite à lire la documentation Adobe : Aide Adobe Switch; Documentation Switch

Les boucles en AS3

Je vous conseille de lire cet article : >utilisation des boucles en AS3. Lisez-y attentivement les chapitres concernant les boucles for each in et for in, moins connues.

La classe Math, ses amies et ses méthodes

Si vous voulez en connaître plus sur les méthodes et classes globales : Niveau supérieur d'AS3
Si vous voulez en apprendre plus sur la classe Math : Classe Math

La classe MovieClip

Pour en savoir plus sur la classe MovieClip, ses méthodes et ses propriétés publiques : Guide de référence - MovieClip.

La classe Error

Je vous invite à lire la documentation concernant cette classe : Documentation Adobe - Error.

Les Array et ses méthodes

Je ne saurais que vous conseiller ce tutoriel de Nataly : tout sur les tableaux.

Les sources

Voici les fichiers du tutoriel en téléchargement :D
starfield-cs3-v2.zip

Vos retours

Je vous invite à participer à la discussion autour de ce tutoriel, d'apporter vos retours, de poser des questions, etc. :
Discussion

Pour des demandes de correction dans le tutoriel en lui-même ou dans le code, ce sera plutôt par ici :
Corrections

Annexe: Histoire de Starfield

Ce tutoriel a une longue histoire en réalité, que je vais tenter de romancer ici par respect pour tous ceux qui se sont investis dans ce projet (et surtout parce que c'est grâce à eux que je peux continuer aujourd'hui ce tutoriel) ;) :

Histoire de Starfield, chapitre 1 :
La création de ce tutoriel est partie d'une folle envie de plusieurs habitués de Flash MediaBox de créer en commun un projet de jeu d'astéroïde avec une discussion transformée (malheureusement) en monologue à cause d'un incident technique.

Histoire de Starfield, chapitre 2 :
Face à l'intérêt marqué envers ce projet des divers protagonistes et de nouveaux venus, M.Spi (que je salue) a désiré en tirer un petit tutoriel qui reprendrait la petite pierre que chacun a apporté. Faute de temps il ne put totalement mener à bien ce vaste projet mais en a tout de même tiré un excellent tutoriel qui constitue la version AS2 que j'ai introduite précédemment.

Histoire de Starfield, chapitre 3 :
Thade arrive ensuite avec sa nouvelle version du logiciel Flash et propose un portage AS3 du projet. Nataly s'est ensuite occupée d'ajouter une version AS3 du code pour enrichir le tutoriel de M.Spi.

Histoire de Starfield, chapitre 4 :
Puis, un beau jour, alors que je traine dans les rubriques du forum je découvre le magnifique tutoriel de M.Spi apprenant à réaliser un fond étoilé tout beau avec une impression de 3D, exactement ce qu'il me fallait pour un de mes jeux. En utilisant son travail, j'ai ensuite trouvé dommage qu'il n'existe pas une version AS3 du tutoriel. Certes, le code AS3 existait, mais il manquait une page complète sur le sujet. Je me suis alors proposée pour reprendre le projet de tutoriel de M.Spi, et avec son accord je me suis mise au travail. Aujourd'hui, grâce à la grande communauté de Flash Mediabox, je peux m'amuser à créer un vaste tutoriel autour d'un jeu sympathique (une sorte de rêve qui devient réalité :P je sais, j'ai des rêves bizarres). Pour me permettre de réaliser ce projet, je remercie tous les auteurs et acteurs s'étant investis dans ce projet avant, pendant et après moi.

Fin de la petite histoire,
Merci à ceux qui ont pris le temps de la lire. Comprenez bien que vous avez entre les mains un travail collaboratif entre de multiples acteurs qu'il convient de respecter comme il se doit. :)


Remerciements

Je remercie toute l'équipe de MédiaBox parce qu'ils sont géniaux :D mais également Monsieur_Spi qui m'a permis de faire une version AS3 de son tutoriel ainsi que Lilive notre fameux bibliothécaire et Nataly qui m'a bien accueillie et puis toute la communauté qui s'est déjà investi dans ce vaste projet ! :D

Et puis je remercie surtout ceux qui ont eu le courage de tout lire ! :D