Flash AS3 - Programmer avec une classe

De EduTech Wiki
Révision datée du 22 août 2016 à 19:51 par Daniel K. Schneider (discussion | contributions) (Remplacement de texte — « <pageby nominor="false" comments="false"/> » par « <!-- <pageby nominor="false" comments="false"/> --> »)
(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Aller à la navigation Aller à la recherche

Cet article est une ébauche à compléter. Une ébauche est une entrée ayant un contenu (très) maigre et qui a donc besoin d'un auteur.


Introduction

Cet article montre comment travailler avec une ou plusieurs classes dans CS4/5/6

Prérequis

Exemple de sensibilisation

Exemple:

Les fichiers

Dans le cas le plus simple, on procède de la façon suivante:

(1) On crée un fichier *.fla

  • Exemple: MyStage.fla

Dans ce fichier on définit une classe associée par défaut

  • Exemple: MyStage

Ensuite, dans les properties, il faut rentrer ce nom de classe.

Finalement, si vous pensez utiliser des composants, ils doivent se trouver dans la librairie. Importer les packages dans le code AS3 ne suffit pas !!!

Voici une copie d'écran:

Fichier *.fla avec définition de classe par défaut et un composant

(2) On crée un fichier *.as qui a exactement le nom de la classe

(donc respecter la casse par exemple)

  • Exemple: MyStage.as

La classe

Une classe doit se trouver dans un paquetage (package) et il faut définir un constructeur, c-a-d une méthode qui sera appelée lorsque Flash crée une instance de cette classe. Cela se fait automatiquement quand le fichier swf fait avec les *.fla est chargé.


/*
 * A package represents a hierarchical directory structure used to organize the class files. 
 * This class is located in the same directory and we don't need to define a package name
 * All class definitions must begin with the definition of the package to which the class belongs.
*/
package 
{
	/*
	 * Import the libraries that will be used inside the class
	 */
	import flash.display.MovieClip;
	import flash.display.Stage;
	import fl.controls.Button;
	import flash.display.Shape;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import MyCircle;

	/*
	 * Because this class is the document class of our main .fla file it has to extend the Sprite 
	 * or the MovieClip class. By extending the MovieClip class it will inherit(have access to) 
	 * all the properties(fields) and methods defined in the MovieClip (or Sprite) class.   
	*/
	public class MyStage extends MovieClip
	{

		// Define a Button component of this class
		private var my_btn:Button;

		/*
		 * This is a special method, called a contructor. It must have the same name as the class and it be 
		 * automatically called when we publsh the .fla file
		 *
		*/
		public function MyStage()
		{
                       //Execute the existing code of MovieClip Component written by Adobe developers 
			super();
			trace("MyStage constructor called");

			// Create a button and add an event handler
			intializeInterface();
			// add the newly created button to the stage
			addChild(my_btn);
		}

		/*
		* Perform the creation and the initialization of the button
		*
		*/
		public function intializeInterface():void
		{

			my_btn   = new Button();
			my_btn.x = stage.stageWidth - 140;
			my_btn.y = stage.stageHeight - 52;
			my_btn.width = 130;
			my_btn.height = 50;
			my_btn.label = "Create circle";
			my_btn.addEventListener(MouseEvent.CLICK, btnClick);
			
			function btnClick(e:MouseEvent)
			{
				// create a new circle each time the button is pressed
				createCircle();
			}

		}

		/*
		*  Handle the creation of new circles
		*  
		*/
		public function createCircle():void
		{
			var some_width = stage.stageWidth-80;
			var some_height = stage.stageHeight-80;
			// compute x and y coordinated of the new circle
			var xCoord:Number = (Math.round(Math.random()*some_width));
			var yCoord:Number = (Math.round(Math.random()*some_height));

			// create a new object(i.e. a new instance) of the MyCircle class. 
			// The two parameters, namely xCoord and yCoord will be passed to the 
			//contructor of the class
			// The MyCircle class is defined in file MyCircle.as
			var c:MyCircle = new MyCircle(xCoord,yCoord);

			// add the circle to the stage
			addChild(c);
		}
	}
}

Importer une autre classe

Bien qu'il soit possible d'écrire pas mal de codes dans la classe de défaut, on modularise normalement le code. Dans le code ci-dessous on importe la classe MyCircle.

MyCircle possède une méthode de construction qui a deux paramètres, par exemple:

 MyCircle (10,100)

Voici le code:

/*
* If a class is not defined in a diferent directory, 
* we don't need to define a package name, so the package name
* can be left blank.
*/

package {

	/*
	 * Import the libraries that will be used inside the class
	 */
	import flash.display.Sprite;
	import flash.display.Shape;
	import flash.geom.ColorTransform;
	
		
	public class MyCircle extends Sprite {
		
		/*
		* Here we define the properties(fields) of the class. These properties will have different values
		* for each new object instance of this class.
		*/
		// Holder of the circle
		var circle:Sprite = new Sprite();
		// Color of the inner filling of the circle
		var randColor:ColorTransform = new ColorTransform();
		// Color of the border of the circle
		var borderColor:ColorTransform = new ColorTransform();

		/*
		 * This is a special method, called a contructor. It must have the same name as the class and it be 
		 * automatically called each time we create a new instance of this class.
		 *
		*/
		public function MyCircle(xCoord:Number, yCoord:Number) {
			trace("MyCircle constructor called");
			// x coordinate of the circle
			circle.x = xCoord;
			// y coordinate of the circle
			circle.y = yCoord;
			createCircle();
		}
		
		
		/*
		* Draw the circle and add it to the stage.
		*/
		public function createCircle():void {
			
			circle.transform.colorTransform = randColor;
			circle.graphics.lineStyle(1, 0x0000);
			circle.graphics.beginFill(0x0000FF, Math.random()); // random Alpha
			circle.graphics.drawCircle((Math.round((Math.random()*140) - 50)),
								(Math.round((Math.random()*140) - 50)),
								(Math.round((Math.random()*140) - 50)));
			randColor.redOffset = Math.round(Math.random() * 480) - 240;
			randColor.greenOffset = Math.round(Math.random() * 480) - 240;
			randColor.blueOffset = Math.round(Math.random() * 480) - 240;
			borderColor.redOffset = Math.round(Math.random() * 240) - 120;
			borderColor.greenOffset = Math.round(Math.random() * 240) - 120;
			borderColor.blueOffset = Math.round(Math.random() * 240) - 120;
			addChild(circle);
		}
	}
}

Utiliser des symboles dans une classe

Il est tout à fait possible de combiner une utilisation "Flash designer" avec une logique d'informaticien.

  • Dans le fichier *.fla vous devez exporter tous les clips de la librairie que vous pensez utiliser
  • Ensuite, dans une classe vous les utilisez comme d'habitude:
obj = new MyClasse;

Création d'une classe

Pour créer votre classe il suffit d'aller dans Flash: File > New... et puis choisir "ActionScript 3.0 Class" et vous pourrez lui donner un nom (e.g., "myClass"). Flash vous donnera déjà le squelette de votre classe.

Les variables

Au début de votre classe vous pouvez définir toute une série de variables qui seront accessibles depuis l'instance de votre classe.

Si vous définissez ces variables comme étant

  • public, vous pourrez y accéder en utilisant le code myClass.myVariable.
  • private, vous ne pourrez y accéder qu'à l'intérieur de la classe, elles seront protégée d'une modification intempestive extérieure.
  • protected, elles ne seront accessubles que depuis l'intérieur de la classe et des classes qui hérite de celle-ci (classes "filles", voir étendre une classe existante plus bas).

Il y a aussi internal, qui est un mot clé pour restreindre l'accès uniquement aux classes définies dans le même paquetage, mais dont l'utilisation est relativement subtile (à déconseiller aux novices, voir doc adobe).

Par exemple, si vous avez créé une classe "Personne" avec une variable "age", sila variable est "public", le code suivant est possible alors que si elle est "private" il retournera une erreur

personne:Personne = new Personne(); //creation d'une instance de la classe Personne

personne.age=20; //avec un âge de 20 ans.

//pour savoir si la peronne est mineure :
if(personne.age < 18) {
  trace("C'est un mineur");
}

Cependant, cela peut être dangereux car n'importe qui peut modifier la variable n'importe comment, sans que vous puissiez définir des conditions (par exemple, le code personne.age = -10; défini une personne avec un âge négatif !). En général, on préférera alors la déclarer "private" et utiliser une méthode pour modifier et accéder à sa valeur.


Pour plus de détails, notamment des convention de nommage (plus ou moins respectées par la communauté, semble-t-il) : tutorial Adobe sur les variables en AS3. Ces conventions sont :

  • Utiliser le camel case: commencez par une minuscule et chaque nouveau mot par une majuscule (Start with a lowercase letter and include uppercase letters, for example totalCost or dailyTotalsArray
  • Use an underscore ( _ ) only to start the name of a private variable
  • Use a dollar ( $ ) sign only to start the name of a static variable
  • Use descriptive variable names so that the content of any variable is obvious, no matter where the variable appears in code

Le constructeur

Une classe peut avoir un constructeur, c'est à dire une fonction qui est appelée lorsqu'on créée une nouvelle instance de cette classe (comme lorsque vous créez un array en faisant: var myArray:Array = new Array()). Le constructeur est utilisé lorsqu'on utilisé ce mot clé "new". C'est par exemple à cet endroit là que vous devriez donner une valeur initiale aux variables que vous avez définies au début de la classe. Le constructeur DOIT avoir le même nom que celui de la classe (faites attention aux majuscules et minuscules!).

Les méthodes

On peut créer des méthodes, qui ne sont rien d'autres que les fonctions internes aux classes. Une méthode est par exemple le fait de pouvoir utiliser myArray.pop() pour pouvoir supprimer le premier élément de l'array.


Si vous utilisez le mot clé static, votre méthode n'utilisera pas d'instance. C'est utile pour définir des fonctions générales et qui ne manipulent et n'ont pas recours à une instance bien précise. Pour comprendre cela, on peut prendre la fonction Math.round() qui sert à arrondir un nombre. Lorsqu'on utilise round(), on n'utilise aucune instance de Math, mais est juste une fonction générale qui se situe dans la classe Math. Toutes les méthodes qui n'ont pas de mot clé "static" utilisent une instance de votre classe.


Comme pour les variables, si vous les définissez des méthodes comme étant

  • public, vous pourrez y accéder en utilisant le code myClass.myMethod().
  • private, vous ne pourrez y accéder qu'à l'intérieur de la classe, elles seront protégée d'une utilisation intempestive extérieure.
  • protected, elles ne seront accessibles que depuis l'intérieur de la classe et des classes qui hérite de celle-ci (classes "filles", voir étendre une classe existante plus bas).

Il y a aussi internal, qui est un mot clé pour restreindre l'accès uniquement aux classes définies dans le même paquetage, mais dont l'utilisation est relativement subtile (à déconseiller aux novices, voir doc adobe).


En général, si vous voulez définir une variable qui correspond à une propriété "interne" de votre classe (par exemple la variable "age" de la classe "personne"), il est recommandé de définir cette variable comme étant "private" et de construire des méthodes "public" setAge et getAge pour y accéder. Comme cela vous pouvez contrôler comment vos variables sont modifiées (par exemple en imposant des conditions, comme l'âge plus grand que 0).

private var age:int = new int();

public function getAge():void {
  return age;
}

public function setAge(theAge:int):void {
  age = theAge;
}

ainsi en dehors de la classe, on pourra définir l'âge comme suit :

personne:Personne = new Personne(); //creation d'une instance de la classe Personne

personne.setAge(20); //avec un âge de 20 ans.

//pour savoir si la peronne est mineure, il suffit de faire appel a getAge :
if(personne.getAge() < 18) {
  trace("C'est un mineur");
}

Les constantes

On peut définir des constantes grace au mot clé const que l'on utilise à la place de var.

Les constantes ont pour particularité de ne pouvoir être modifiée qu'une seule fois, c'est-à-dire lors de leur déclaration. Le code

const MAX_CAPACITY:int = 12;

déclare une constante dont la valeur est 12. si on écrit après MAX_CAPACITY = 10; Flash générera une erreur.

Si vous utilisez le mot clé static, votre constante n'utilisera pas d'instance. C'est utile pour définir des constantes générales qui n'ont pas recours à une instance bien précise. Pour comprendre cela, on peut prendre l'exemple de la constante Math.PI qui contient la valeur du nombre pi (enfin un grand nombre de décimales). Toutes les constantes qui n'ont pas de mot clé "static" utilisent une instance de votre classe.

Référencement d'une propriété ou méthode statique

La différence entre une constante ou méthode "statique" et "normale" est dans la manière de la référencer.

Avec le mot clé static la constante ou la méthode est liée directement à la classe en générale alors que sinon elle est liée à une instance particulière (et il faut alors avoir créé une instance pour y accéder).

Remarquons enfin qu'il faut toujours définir le status (privé, publique, protégé).

Pour une classe "Femme" définie par

class Femme {
  public static const SEXE:String = "F";
  public const TITRE = "Madame";

  public function Femme(){
  /construteur
  }
}

voici ce qui se passe :

import Femme;

Femme.SEXE; //renvoie "F"
Femme.TITRE; //renvoie une erreur car il faut une instance

femme:Femme = new Femme();
femme.TITRE //renvoie "Madame"

Étendre une classe existante

Lorsqu'on créé une classe, elle peut souvent se situer logiquement dans une classe qui existe déjà. Par exemple, si vous êtes en train de créer un jeu avec des ballons et que vous créez la classe "ballons, il est fort probable que dans votre esprit "ballon" est une forme personnalisée d'un MovieClip ou d'un Sprite. Ainsi, vous pourrez transformer "public class ballon {...}" en "public class ballon extends MovieClip {...}". Ceci est très intéressant car ça vous permet d'accéder aux méthodes de la classe MovieClip depuis votre classe (rappelez vous quand même d'importer dans la classe flash.display.MovieClip, si vous voulez effectivement étendre la classe MovieClip).

Héritage de classe

Quand une classe étends une classe existante, on dit qu'elle "hérite" de cette classe et on l'appelle classe "fille" ou "descendante". En fait, elle contiendra toutes les variables et les méthodes public et protected de la classe "mère", c'est pour cela que l'on parle d'héritage (on ne rentrera pas dans les détails des variables internal ici).

Classe mère

Par exemple, si je définis la classe "Personne" comme plus haut :

public class Personne {

//l'âge de la personne
private var age:int = new int();

//l'âge de la majorité (18 ans, mais dépends des pays)
private majorityAge:int = new int();

public function Personne(){
  //constructeur de la classe
  //initialise l'âge de la majorité à 18 ans 
  //remarquez qu'on a un accès direct puisqu'on est à l'intérieur de la classe.
  majorityAge = 18;
}

//accès à l'âge
public function getAge():void {
  return age;
}

public function setAge(theAge:int):void {
  age = theAge;
}

//accès à l'âge de la majorité
public function getMajorityAge():void {
  return majorityAge;
}

protected function setMajorityAge(theAge):void {
  majorityAge = theAge;
}
/*cette dernière fonction est protégée 
* car on ne veux pas que n'importe qui puisse changer l'âge de la majorité
*/

Alors les classes filles n'auront pas accès directement à la variable "age" ni "majotrityAge" (puisqu'elles sont privées) mais auront accès aux méthodes "setAge", "getAge" et "getMajorityAge" (comme tous le monde puisqu'elles sont publiques) ainsi qu'a la méthode protégée "setMajorityAge".

Classe fille

Du coup, on peut définir une classe fille "Americain" qui étends "Personne" comme cela (notons qu'il faut faire un "import" de la classe "Personne" pour pouvoir accéder à ses méthodes et l'étendre) :

/* attention, il faut importer la classe mère 
* (pour faire simple, il faut que le fichier Personne.as 
* se trouve dans le même répertoire)
*/
import Personne; 
public class Americain extends Personne {

private nationality:String = new String();

public function Mineur(){
  //constructeur
  // initialise la nationalité à Américain
  nationality = "Américain";

  /* initialise l'âge à 0
  * remarquez que cette fois on doit faire appel à la méthode setAge
  * de la classe mère (car la variable age n'est pas accessible directement)
  */
  setAge(0);

  /* change l'âge de la majorité à 21 ans (aux USA)
  * remarquez que l'on peut faire appel à la méthode setMajorityAge
  * de la classe mère (car la méthode est déclarée "protected", 
  * donc accessible par la classe fille)
  */
  setMajortiyAge(21);

}

}


On pourra ainsi créer deux personnes, un suisse (personne "standard", donc instance de la classe "Personne" avec âge de la majorité à 18 ans par défaut) et un américain (donc instance de la classe "Americain" avec âge de la majorité modifié à 21 ans par la classe fille) :

suisse:Personne = new Personne();
personne.setAge(20); //avec un âge de 20 ans.

americain:Americain = new Americain();
americain.setAge(20); //avec un âge de 20 ans.

//pour savoir si le suisse est mineur, 
//il suffit de comparer l'âge et l'âge de la majorité
if(suisse.getAge() < suisse.getMajorityAge()) {
  trace("C'est un mineur");
} else {
  trace("C'est un majeur");
}

//pour savoir si l'américain est mineur, 
//c'est la même chose
if(americain.getAge() < americain.getMajorityAge()) {
  trace("C'est un mineur");
} else {
  trace("C'est un majeur");
}

Les deux ont le même âge (20 ans) mais dans le cas du suisse, le code affiche "C'est un majeur" alors que pour l'américain il affiche "C'est un mineur".

Notons que dans les deux cas, on à fait appel aux fonctions "getAge" et "getMajorityAge" de la classe mère pour faire le test.

Pour aller plus loin

Lire:

Classe DocumentClass et le passage de variables entre frames

Dans flash, il existe une classe spéciale appelée "DocumentClass" (mais vous pourrez personaliser le nom si vous voulez) qui est attachée à la scène et dont les variables et méthodes public sont accessibles partout (et les variables et méthodes protected, dans toutes les classe filles).

Cela est très utile par exemple quand il s'agit de conserver des variables d'une frame sur l'autre.

En effet, en "Timeline scripting" on peut écrire directement la variable dans la partie "action" de la frame 1 et elle sera visible partout après, mais cela n'est pas une bonne pratique (entre autres parce que si on change l'ordre des frames la variable ne sera plus visible partout...).

Créer une DocumentClass

Pour créer une DocumentClass, il suffit de sélectionner l'objet scène (stage), le plus simple étant de cliquer sur un frame vide. Puis il faut modifier le champ "Class" dans la fenêtre "Propriétés" :

FlashCS5-DocumentClass.jpg

Mettez le nom que vous voulez (je conseille "DocumentClass" mais c'est comme vous voulez) et cliquez sur l'icône du crayon (a droite). Flash vous propose de créer une définition (dites oui) puis recliquez sur le crayon. Flash vous proposera alors de modifier la classe, dites oui et il ouvrira le package pour l'édition (n'oubliez pas d'enregistrer et laisser le nom par défaut, sinon Flash ne trouvera pas le fichier la prochaine fois).

Utiliser une DocumentClass

Comme la DocumentClass est liée à l'objet scène (stage), la classe est instanciée dès le début de l'application et reste disponible sur toute les frames.

De plus, toutes les frames font parties de l'objet scène, ce qui veut dire que les variables et les méthodes publiques sont directement accessibles depuis la Timeline. Si votre code est

package  {
	import flash.display.MovieClip;	
	
	public class DocumentClass extends MovieClip {
		
		public var score = new Number();
		
		public function DocumentClass() {
			// constructor code
			score = 0;
		}		
	}	
}

Alors pour accéder à la valeur du score, il suffit de taper score et pas DocumentClass.score. On peut ainsi avoir dans un frame (ou mieux, dans un gestionnaire d'événement)

 score = 100; //attribue un score de 100

et dans la suivante

 trace(score); //affiche le score (ici 100).

Conventions de nommage

Pour plus de détails, notamment des convention de nommage (plus ou moins respectées par la communauté, semble-t-il) :

Ces conventions sont :

  • Utiliser le camel case: commencez par une minuscule et chaque nouveau mot par une majuscule ("longeurMax" ou "recevoirUnPetitCadeau")
  • Utiliser l'underscore ( _ ) pour commencer le nom d'une variable ou méthode private (et seulement pour ça)
  • Utiliser le dollar ( $ ) pour commencer le nom d'une variable ou méthode static (et seulement pour ça)
  • Utiliser des noms suffisament clairs pour que le contenu de la variable ou le but de la méthode soit évident, quelque soit l'endroit du code où elle apparaît ("longueurMax" ok, mais "l" non)

Il existe encore une autre convention pour les constantes (déclarées avec le mot clé const à la place de var et qui ne peuvent plus être modifiées une fois créées) :

  • Utiliser les majuscules séparées par un underscore ( _ ) pour les constantes (par exemple PI (le nombre) ou LONGUEUR_MAX).

Et enfin une convention pour les nom de classes :

  • Utiliser le camel case avec majuscule initiale : par exemple la classe "Math" ou "VideoClip" ou "MaBelleClasseAMoi".