Séparation du design et du code dans Flash – part 1

Ces trois tutoriels font suite à un séminaire donné chez Adobe France le 23 novembre 2009, par Nicolas Gans, responsable pédagogique plate-forme Flash au centre de formation Regart.net.

Ils reprennent les slides et les exemples montrés lors de cette journée.

Le séminaire est divisé en trois parties :

- Séparation du design et du code dans Flash – part 1 : séparation .fla et .as
- Séparation du design et du code dans Flash – part 2 : compiler des .fla différents
- Séparation du design et du code dans Flash – part 3 : coder et compiler avec Flex Builder

Problématiques abordées :

Comment développer un projet en Flash et fluidifier les échanges au sein d’une équipe composée de designer Flash et de développeurs AS3 .

Quelles sont les bonnes pratiques (séparation et communication entre éléments du design et code), les techniques (librairies partagées, SWC),  outils (FlashDevelop, Flex Builder) ?

Première partie : séparation .fla et .as

Permettre à chacun de travailler avec ses propres outils.

Historique

ActionScript 1

  • Code sur la timeline ou sur les objets (MC et BTN)
  • Code disséminé un peu partout dans l’appli : difficile à maintenir et à comprendre.
  • Le codeur et le designer étaient souvent une seule et même personne.
  • Avec le temps : spécialisation des tâches, codeur et designer sont devenues deux personnes différentes.
  • Mise au point de bonnes pratiques : centralisation du code sur le scénario principal, utilisation de la directive #include pour placer le code dans des fichiers .as externes.

ActionScript 2

  • Apparition de la POO : une meilleure structuration du code en classes est alors permise
  • L’éditeur de code de Flash montre ses limites pour les codeurs
  • Utilisation de SEPY, Flashdevelop par les codeurs

ActionScript 3

  • Langage clairement POO
  • Prérequis en programmation sont importants
  • Outils des codeurs : Flashdevelop, Flex Builder, FDT (plugin Eclipse)

Classe du document

  • Représente le root (la scène principale) de l’application.
  • Point d’entrée de l’application, permet d’externaliser tout le code.
  • Hérite d’un displayObjectContainer (MovieClip, Sprite)

stage_root_classe_document

Associer un symbole et une classe

  • Bonne séparation logique et vue.
  • Transparent pour le designer qui peut créer des occurrences sur la scène avec une logique implémentée.
  • Instanciation se fait par new MaClasse()
  • Liaison avec Clip, Boutons, Bitmap, Police, Sons

Communication TimeLine <> fichier .as

  • Occurrences posées sur la scène deviennent des propriétés publiques de la classe.
  • Paramètres AS3 : « déclarer automatiquement les occurrences de la scène »
  • Appel  de méthodes de la classe depuis le scénario.
  • Ciblage d’occurrences depuis la classe.

Déclencher des instructions en fonction de la position de la tête de lecture

3 méthodes :

  • Appel de méthodes de la classe depuis le scénario
  • Diffusion d’événement depuis le scénario
  • Utilisation d’addFrameScript (combinée avec étiquettes via la propriété currentLabels)

Exemple :  01_balle_rebond [Télécharger l'exemple]

This movie requires Flash Player 9

?View Code ACTIONSCRIPT3
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package 
{
	import flash.display.FrameLabel;
	import flash.display.MovieClip;
	import flash.events.Event;
 
	/**
	 * ...
	 * @author Nicolas Gans
	 */
	public class Animation extends MovieClip
	{
 
		public function Animation():void
		{
			trace("Animation instanciée");
 
			// méthode 2
			addEventListener(Event.COMPLETE, ecouteurAnimationTerminee);
 
			// méthode 3
			// utilisation de la fonction non-documentée addFrameScript
			// ATTENTION : les numéros d'image commencent à 0
			// donc 89 = 90ème image
			// ATTENTION 2 : tout le code présent sur la frame est écrasé !
			addFrameScript(89, frameAnimationTerminee);
 
			// méthode 3bis
			// on peut combiner tout cela avec des étiquettes
			// currentLabels renvoie un tableau d'objets FrameLabel
			var etiquettes:Array = currentLabels;
 
			for each (var label:FrameLabel in etiquettes)
			{
				trace(label.frame, " : ", label.name);
			}
 
			// ATTENTION : le numéro de frame d'un FrameLabel commence à 1 et non à 0 !!
			addFrameScript(etiquettes[0].frame - 1, frameAnimationMilieu);
		}
 
		// METHODE 1
		// méthode appellée depuis le scénario de l'animation
		// plus performant et moins gourmand en mémoire
		private function animationTerminee():void
		{
			trace("Animation terminée");
		}
 
		// METHODE 2
		// diffusion d'un événement depuis le scénario
		private function ecouteurAnimationTerminee(e:Event):void
		{
			trace("Evénement COMPLETE diffusé depuis le scénario");
		}
 
		// METHODE 3
		// addFrameScript : une fonction se déclenche automatiquement quand la tête de lecture
		// parvient à telle image
		private function frameAnimationTerminee():void
		{
			trace("Animation terminéee avec addFrameScript");
			stop();
		}
 
		// METHODE 3bis
		// addFrameScript avec étiquette
		private function frameAnimationMilieu():void
		{
			trace("Animation milieu");
		}
 
	}
 
}

Piloter la timeLine depuis la classe

Il est courant d’utiliser le scénario pour créer des écrans ou des états d’un clip et de déplacer la tête de lecture en fonction des actions de l’utilisateur pour afficher tel ou tel écran/état.

timeline_etiquette

Problème  de l’asynchronie code/tête de lecture

Player 9 : gros soucis de synchronisation code/tête de lecture. Le code s’exécute avant le déplacement effectif de la tête de lecture, d’où impossibilité de cibler des occurrences placées sur la scène :

?View Code ACTIONSCRIPT3
1
2
3
4
gotoAndStop(10);
// occurrence placée à l’image 10
monClip.play();
Error #1009: Il est impossible d'accéder à la propriété ou à la méthode d'une référence d'objet nul.

Exemple : 02_piloter_timeLine_depuis_classe [Télécharger l'exemple]

This movie requires Flash Player 9

?View Code ACTIONSCRIPT3
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
41
42
43
44
45
46
47
48
49
50
package 
{
	import flash.display.MovieClip;
	import flash.events.MouseEvent;
	import flash.system.*;
 
	/**
	 * ...
	 * @author Nicolas Gans
	 */
	public class Main extends MovieClip 
	{
 
		public function Main():void
		{
 
			stop();
			etape1_btn.addEventListener(MouseEvent.CLICK, etape1);
			etape2_btn.addEventListener(MouseEvent.CLICK, etape2);
			etape3_btn.addEventListener(MouseEvent.CLICK, etape3);
 
		}
 
		private function etape1(e:MouseEvent):void
		{
			// Fonctionne avec le Flash Player 10
			// mais pas avec le Flash Player 9
			gotoAndStop("etape1");
			etape_txt.text = "Etape 1";
			perso_mc.alpha = Math.random();
 
		}
 
		private function etape2(e:MouseEvent):void
		{
			gotoAndStop("etape2");
			etape_txt.text = "Etape 2";
			tete_mc.alpha = Math.random();
		}
 
		private function etape3(e:MouseEvent):void
		{
			gotoAndStop("etape3");
			etape_txt.text = "Etape 3";
			couronne_mc.alpha = Math.random();
		}
 
	}
 
}
Solution 1 :
?View Code ACTIONSCRIPT3
1
2
3
4
5
6
7
8
9
10
11
12
// écouter l’événement RENDER
addEventListener(Event.RENDER, onRender);
// déplacer la tête de lecture
gotoAndStop(10);
// invalidation du Stage
stage.invalidate();
 
// ciblage du clip dans l’écouteur
private function onRender(e:Event):void
{
monClip.play();
}

Rend très complexes les allers-retours sur la timeline.

Solution 2 :

Utiliser un addFrameScript.

?View Code ACTIONSCRIPT3
1
2
3
4
5
6
7
addFrameScript(9, imageDix);
gotoAndStop(10);
 
private function imageDix() :void
{
monClip.play();
}

Beaucoup plus souple, surtout si on a plein d’étapes avec beaucoup d’allers-retours.

ATTENTION : pas nécessaire avec le player 10 lorsqu’on travaille dans une Classe, mais le problème persiste si on code sur la TimeLine.

Player 10 : si on code sur la TimeLine, il faut appeler la méthode de déplacement de tête de lecture sur un enfant du scénario dans lequel on code pour que ça fonctionne.

?View Code ACTIONSCRIPT3
1
2
3
4
5
6
7
// ne fonctionne pas
gotoAndStop(10);
monClip.alpha = Math.random();
 
// fonctionne
monConteneur.gotoAndStop(10);
monConteneur.monClip.alpha = Math.random();

Gestion des états d’un MovieClip : horizontalité vs verticalité

  • Une alternative au travail avec la timeline (horizontalité) est de travailler avec la displayList (verticalité).
  • Chaque état est un DisplayObject, on gère alors les changements d’état en les ajoutant/retirant de la liste d’affichage.
  • Intérêt : on peut se passer du gotoAndStop() asynchrone et eviter les bugs liés au player en mode horizontal. On travaille de manière plus « naturelle » pour le player.

Exemple : 03_gestion_etats_MC_avec_displayList_Mario [Télécharger l'exemple]

This movie requires Flash Player 9

?View Code ACTIONSCRIPT3
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package 
{
	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.ui.Keyboard;
 
	/**
	 * ...
	 * @author Nicolas Gans
	 */
	public class Mario extends Sprite 
	{
 
		private var touches:Object = new Object();
		private var sens:Number;
		private var vitesse:Number = 8;
 
		public var isJumping:Boolean;
 
		private var _displayed:DisplayObject;
 
		public static const JUMP_FINISHED:String = "jumpFinished";
 
		public function Mario():void
		{
			sens = scaleX;
 
			// on nettoie la displayList
			while (numChildren > 0) removeChildAt(0);
 
			// clip affiché par défaut
			displayed = repos_mc;
 
			addEventListener(Event.ADDED_TO_STAGE, activation);
		}
 
		private function activation(e:Event):void
		{
			stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
			stage.addEventListener(KeyboardEvent.KEY_UP, keyUp);
			addEventListener(Event.ENTER_FRAME, loop);
			// événement diffusé sur la dernière frame du clip saute_mc
			saute_mc.addEventListener(JUMP_FINISHED, onJumpFinished);
		}
 
		// gestion affichage / retrait des états
		private function set displayed(value:DisplayObject):void
		{
			// on supprime l'état en cours
			if (_displayed != null && contains(_displayed)) removeChild(_displayed);
			// on le remplace par le nouvel état
			_displayed = addChild(value);
		}
 
		// gestion des actions en fonction des touches enfoncées
		private function loop(e:Event):void 
		{
 
			if (touches[Keyboard.SPACE] && !isJumping)
			{
				saute();
			}
 
			if (touches[Keyboard.LEFT])
			{
				deplace(Keyboard.LEFT);
			}
			else if (touches[Keyboard.RIGHT])
			{
				deplace(Keyboard.RIGHT);
			}
 
			if (touches[Keyboard.DOWN] && !isJumping)
			{
				displayed = assis_mc;
			}
 
		}
 
		private function keyDown(e:KeyboardEvent):void 
		{
			touches[e.keyCode] = true;
		}
 
		private function keyUp(e:KeyboardEvent):void 
		{
			touches[e.keyCode] = false;
			if (!isJumping) displayed = repos_mc;
		}
 
		private function saute():void
		{
			isJumping = true;
			saute_mc.gotoAndPlay(2);
			displayed = saute_mc;
		}
 
		private function onJumpFinished(e:Event):void 
		{
			isJumping = false;
			displayed = repos_mc;
		}
 
		private function deplace(pSens:int):void
		{
			if (_displayed == assis_mc) return;
			if (!isJumping) displayed = marche_mc;
 
			if (pSens == Keyboard.LEFT)
			{
				scaleX = -sens;
				x -= vitesse;
			}
			else
			{
				scaleX = sens;
				x += vitesse;
			}
		}
	}
 
}
 
}
Bookmark and Share

2 réponses Souscrire aux commentaires


  1. Alama

    Bravo et merci pour le tuto ;-)

    27 nov 2009 @ 17:17


  2. Meteore

    Merci*1000 !!

    02 juin 2010 @ 11:45

Répondre