Introduction
Classe de base (#1)
Classe de base (#2)
Héritage et surcharge (#1)
Héritage et surcharge (#2)
Diffuser des événements
Classe liée
Classe de document
Classe externe : une visionneuse (#1)
Classe externe : une visionneuse (#2)
Classe externe : méthodes statiques
Classe de base (#2)
Variables d'instances
Vous avez donc un code qui ressemble à ça :
package { import flash.display.MovieClip; import flash.filters.BitmapFilterQuality; import flash.filters.BlurFilter; public class AvecFlou extends MovieClip { public function AvecFlou():void { masque.filters=[new BlurFilter(10,10,BitmapFilterQuality.HIGH)]; photo.mask=masque; photo.cacheAsBitmap=true; } } }
Ainsi qu'un symbole de bibliothèque que vous avez associé à la classe. Ce symbole contient une instance nommée photo et une instance nommée masque
On remarque d'ailleurs que les instances de clip nommées masque
et photo
sont “directement utilisables”. La classe connait leur nom semble-t-il.
C'est l'effet d'un réglage par défaut des paramètres de publication de Flash (menu Fichier).
Quand la case Déclarer automatiquement les occurrences est cochée, le compilateur déclare, tout seul dans son coin, des variables pour les instances de symboles posées sur les calques. Ces variables ont le bon goût d'avoir le nom des instances auxquelles elles renvoient.
Décochez la case de la scène, enregistrez, testez.
1120: Accès à la propriété non définie masque. 1120: Accès à la propriété non définie photo.
Je ne commente pas, c'est assez parlant…
Si ?
En langage compilateur ça veut dire : “Hey gars ! Tu utilises le mot masque
et le mot photo
que je ne trouve nulle part. J'en déduis que ce sont des propriétés et que tu ne les as pas déclarées (définies)”.
A nous donc de déclarer les variables auxquelles nous ferons référence.
public class AvecFlou extends MovieClip { public var masque:MovieClip public var photo:MovieClip public function AvecFlou():void { //… suite …
Et tout rentre dans l'ordre
Je vous le signale, non pas pour que vous travailliez dans ce mode (à vous de voir) mais surtout pour que si un jour la bestiole se mettait à vous crier des insultes de ce genre, vous puissiez aller vérifier si un malintentionné quelconque n'aurait pas décoché cette fichue case à votre insu (pas vous par inadvertance, non, ça jamais…)
Et puis aussi pour répondre aux légitimes interrogations du type : mais d'où ça sort ? Pourquoi dispos-je de variables que je n'ai pas déclarées (j'en connais qui pinaillent )
Les deux variables masque et photo doivent être visibles (accessibles) partout dans la classe, c'est pourquoi elles sont déclarées dans la classe elle même (on dit qu'elles sont globales).
Les propriétés
Tout irait pour le mieux dans le meilleur des mondes si Ronchon ne s'avisait que c'est quand même dommage que le flou soit toujours de 10. Si il veut 15 ou 70 il faut écrire deux autres classes ? Il ne va pas en fabriquer une par valeur quand même ?! Il lui faudrait une propriété… Il faudrait qu'il puisse écrire dans le fla quelque chose du genre :
clipDemo.flou=25;
Et bien ! Il n'a qu'à écrire une méthode appliqueFlou à qui il passera une valeur pour le flou en x, une autre pour le flou en y, répondez vous immédiatement, maintenant que vous savez faire.
// le fla function test(me:MouseEvent) { clipDemo.appliqueFlou(25,25) }
// .as public function appliqueFlou(pFlouX:int,pFlouY:int):void { masque.filters=[new BlurFilter(pFlouX,pFlouY,BitmapFilterQuality.HIGH)]; }
Mmmoui… mais non, argumente-t-il. Pour lui c'est plus une propriété, ça caractérise son clip. C'est quand même dommage qu'on ne puisse pas écrire :
function marche(me:MouseEvent) { Demo1.flouX=25; Demo1.flouY=5; }
Qui a dit qu'on ne pouvait pas ? On ne sait pas, nuance.
Et bien découvrons
Accesseurs (setter/getter)
On le sait, on dit d'une propriété qu'elle est en lecture quand on peut l'interroger, en écriture quand on peut la valoriser. Le plus souvent elles sont en lecture/écriture (on dit aussi lecture/définition).
Au point où on en est, on va fabriquer deux propriétés (flouX et flouY) en lecture/écriture.
Ça s'obtient à l'aide de fonctions dites fonctions d'accesseur (du verbe accéder) qui ont la particularité d'utiliser le mot get ou set selon qu'elle seront utilisées en lecture (get) ou en écriture (set).
Vous rencontrerez souvent l'anglissisme getter/setter.
Ces méthodes bizarres (on les appelle méthodes accesseur) requièrent un nombre précis* de paramètres et sont utilisées comme de bêtes propriétés : DemoFlou.flouX=75;
ou var valeurFlouX:Number= DemoFlou.flouX;
* un et un seul paramètre pour set
aucun paramètres pour pour get
Pour écrire (définir) :
public function set flouX(pFlouX:Number):void { masque.filters=[new BlurFilter(pFlouX,10,BitmapFilterQuality.HIGH)]; }
Pour lire, on fait la même ou chose ou presque, la même fonction accesseur (même nom) mais cette fois utilisant le mot get et en renvoyant du Number (puisque c'est le type requis avec BlurFilter).
public function get flouX():Number { return ?????? }
Ah mais oui… retourne quoi ? Où l'a-t-on la dernière valeur appliquée au flou x ?
Nulle part
Pas grave, on le fait. Dans le setter flouX (function set flouX
) il faut non seulement appliquer le flou, mais aussi mémoriser la valeur pour pouvoir la récupérer, plus tard, à la lecture. Pour mémoriser une valeur on utilise une variable. Elle doit être visible partout, on la déclare donc en globale (dans le bloc de la classe) et privée parce que ça ne regarde que nous, c'est de la cuisine interne.
private var _flouX:Number=10;
Voilà pour la propriété flouX de Ronchon, je vous laisse faire le flouY ?
public class AvecFlou extends MovieClip { public var masque:MovieClip; public var photo:MovieClip; private var _flouX:Number=10; public function AvecFlou():void { masque.filters=[new BlurFilter(_flouX,10,BitmapFilterQuality.HIGH)]; photo.mask=masque; photo.cacheAsBitmap=true; } // Accesseurs flouX public function set flouX(pFlouX:Number):void { _flouX=pFlouX; masque.filters=[new BlurFilter(pFlouX,10,BitmapFilterQuality.HIGH)]; } public function get flouX():Number { return _flouX; }
variable publique
Une autre technique pour écrire une propriété de classe c'est de déclarer une variable globale et publique (afin qu'elle soit accessible de l'extérieur de la classe - c'est le propre d'une propriété -)
L'avantage c'est que c'est vite fait.
Les inconvénients sont au nombre de deux :
• On ne peut les restreindre à la seule lecture
• On ne peut rien en faire en même temps. Je m'explique : dans le cas qui nous occupe on aurait été contents avec une bête variable déclarée au niveau de la classe, genre :
public class AvecFlou extends MovieClip { public var masque:MovieClip; public var photo:MovieClip; private var flouX:Number=10; //[… suite ]
Et alors ? Certes elle est accessible et valorisable de l'extérieur : DemoFlou.flouX=75;
, mais ça ne change pas le filtre…
Dans le même cadre on ne peut pas non plus avec une simple variable limiter les valeurs acceptées ou les modifier.
Par exemple, un accesseur comme celui-ci, permettrait de limiter une propriété vitesse à 100…
public function set vitesse(pVitesse:int):void { _vitesse= pVitesse>100?100,pVitesse }
il est toujours préférable
d'avoir recours aux accesseurs.
Synthèse : Classe de base
package { //les imports import flash.display.MovieClip; public class NomClasse extends MovieClip { // variables d'instances, quand la case déclarer les occurrences est cochée //public var nomInstance:Type; // variables globales privées ou publiques private var _unePrivee:Type //valorisée ou non; public var unePublique:Type //valorisée ou non; // variables d'accesseur private var _uneProp:Type=… //------------------------------------------------------------- // constructeur public function NomClasse():void { // traitements } //------------------------------------------------------------- // méthodes public function uneMethode():Type { // traitements } //------------------------------------------------------------- // propriétés accesseur public function set uneProp(pUneProp:Type):void { _uneProp=…; //traitements } public function get uneProp():Type { return _uneProp; } } }
Puisqu'il s'agit d'associer cette classe à un symbole de bibliothèque de type MovieClip, tout naturellement le modèle proposé ci-dessus étend la classe MovieClip.
Sachez que si, et seulement si, le clip associé ne contient qu'une image vous pouvez vous contenter d'étendre la classe Sprite. En effet Sprite est mère de MovieClip, les méthodes et propriétés spécifiques à MovieClip ont toutes à voire avec le scénario et la gestion de la tête de lecture. En l'absence de scénario, la classe Sprite peut “suffire”. Méfiez vous cependant, vous ne pourrez pas non plus ajouter du code sur les images du clip. Essayez un simple
trace(“un truc”);
vous constaterez l'erreur suivante : 1180: Appel à une méthode qui ne semble pas définie, addFrameScript
. (Nous reviendrons sur cette méthode au chapitre classe de document)Sauf excellente raison, privilégiez donc la classe MovieClip.
Mise en œuvre
A titre de mise en œuvre et afin d'illustrer l'intérêt de la classe de base je vous propose de réfléchir à ça :
Imaginez que vous ayez ce type de chose à réaliser.
La partie graphiste de votre personnalité veut ne pas avoir à se préoccuper de quoi que ce soit d'autre que de fabriquer des clips aux visuels divers et variés pour figurer le mobilier, d'en organiser joliment une ou plusieurs instances sur la scène, quitte à les déformer (le lit, par exemple, est réduit dans l'inventaire).
C'est alors que la partie programmeur vient à la rescousse en se disant que somme toute, tous les symboles “mobilier” se comportent de la même façon…
Tous ces clips admettent le glissé/lâché, pivotent sur clic, reprennent position, orientation et échelle originales quand ils sont lâchés hors du plan…
C'est le contexte typique pour écrire une classe qui décrira ces comportements communs à chaque objet. Une seule classe pour plusieurs symboles, ce sera donc une classe de base.
Comment la penser ?
Si vous aviez du écrire le code dans le fla. (On dirait qu'on connaissait pas les classes …)
Certainement auriez-vous écrit “dans le symbole”. Vous auriez dédié un calque au code dans le symbole Mv_Fauteuil (par exemple), souscrit deux écouteurs : un sur souris enfoncée pour déclencher un startDrag, un autre sur souris relâchée pour déclencher un stopDrag (plus quelques lignes pour gérer le retour à l'inventaire en cas de position hors plan).
// [A] // mémoriser les coordonnées et l'échelle d'origine var xDep:Number=this.x; var yDep:Number=this.y; var scaleDep:Number=this.scaleX; var plan:MovieClip=MovieClip(root).plan; // [B] // Ajouter les écouteurs this.addEventListener(MouseEvent.MOUSE_DOWN,f_Enfonce); this.addEventListener(MouseEvent.MOUSE_UP,f_Lache); this.buttonMode=true // [C] // les fonctions souscrites function f_Enfonce(me:MouseEvent) { if (me.altKey&&! me.shiftKey) { this.rotation+=45; return; } if (me.altKey&&me.shiftKey) { this.rotation-=45; return; } this.scaleX=leClip.scaleY=1; this.startDrag(true); } function f_Lache(me:MouseEvent) { stopDrag(); if (this.hitTestObject(plan)) { // Touche le plan if (Plan.mouseX-this.width/2<0 || plan.mouseX+this.width/2>plan.width || plan.mouseY+this.height/2>plan.height || plan.mouseY-this.height/2<0) { // déborde du plan : retour ds l'inventaire replace(); } } else { // Hors plan : retour ds l'inventaire replace(); } } // Attribue les coordonnées, l'échelle et la rotation d'origine function replace() { this.x=this.xDep; this.y=this.yDep; this.scaleX=this.scaleY=this.scaleDep; this.rotation=0; }
Avec ou sans this
, puisque c'est l'objet par défaut et que ne rien écrire c'est écrire this
…
Et bien, (quasi) pareil dans la classe :
Les lignes [A] et [B] qui sont exécutées quoiqu'il arrive pour chaque instance posée sur la scène, doivent donc se retrouver dans le constructeur pour les unes [B] et dans le bloc classes pour les variables [A]
Quant aux fonctions [C] elles sont définies dans le corps de la classe.
Ecrire une classe associée à un symbole c'est la même chose qu'écrire dans le symbole lui même, aux imports près…
L'avantage c'est que c'est bien plus facile/rapide/maintenable de fabriquer une classe que de copier coller des blocs entiers de code d'un symbole sur l'autre…
Ici on considère que les instances seront initialement posées sur la scène ainsi qu'un clip nommé plan. On se compliquera la vie plus tard
package { import flash.display.MovieClip; import flash.events.MouseEvent; public class PlanMeubles extends MovieClip { //les variables globales [A] public function PlanMeubles() { // les écouteurs et le buttonMode [B] } // [C] private function f_Enfonce(me:MouseEvent) { // ce qui se passe quand on enfonce la souris } private function f_Lache(me:MouseEvent) { // ce qui se passe quand on relâche la souris } private function replace() { x=xDep; y=yDep; scaleX=scaleY=scaleDep; rotation=0; } } }
Voilà pour le principe et le cadre d'utilisation des classes de base. Quand vous aurez fini de jouer avec le plan, et que vous vous sentirez à l'aise avec ces quelques principes fondamentaux, je vous propose de me rejoindre page suivante pour corser un peu les choses.
