NyrOS

Réalisé par Cédric Nirousset

Projet Flash

SRC Montbéliard



Sommaire

^^ Retour ^^

Introduction

Dans le cadre de la formation SRC à Montbéliard, nous devons rélaiser un projet Flash, avec les conraintes imposées par M. Roxin.

Beaucoup réalisent leur portfolio. Pour ma part, j'ai décider de me lancer dans la programmation d'un genre de système d'exploitation en ligne, avec Flash. Bien sûr, ce projet n'a en aucun cas vocation de remplacer Windows ou autre, mais c'est la navigation par icônes et fenêtres qui est intéressante.

Pour la programmation, j'ai décider de tout faire avec des classes, pour pouvoir modifier rapidement et facilement n'importe quel élément de mon projet. Lorsque l'on regarde dans les fla, il n'y a en fait qu'une ligne ou deux maximum qui en fait vont cherché la classe dont il a besoin, et toute l'animation est faite par cette classe.

Le projet final est visible à cette adresse : http://src.pu-pm.univ-fcomte.fr/public/src2004/mini_flash/nirousset//public/src2004/nirousset/-Flash-/.

^^ Retour ^^

NyrOS.fla

C'est l'animation centrale de l'application, c'est par cette animation qu'on appelle toutes les classes et toutes les autres animations.

Beaucoup réalisent leur portfolio. Pour ma part, j'ai décider de me lancer dans la programmation d'un genre de système d'exploitation en ligne, avec Flash. Bien sûr, ce projet n'a en aucun cas vocation de remplacer Windows ou autre, mais c'est la navigation par icônes et fenêtres qui est intéressante.

Pour la programmation, j'ai décider de tout faire avec des classes, pour pouvoir modifier rapidement et facilement n'importe quel élément de mon projet. Lorsque l'on regarde dans les fla, il n'y a en fait qu'une ligne ou deux maximum qui en fait vont cherché la classe dont il a besoin, et toute l'animation est faite par cette classe.

var monBureau:Bureau = new Bureau(this);
var monMenu:Menu = new Menu(this);
var maBarre:Barre = new Barre(this);

On définit ici une objet 'monBureau' de type Bureau, c'est à dire de notre classe Bureau créée dans le fichier Bureau.as décrit un peu plus loin. En paramètre, on donne la référence à l'endroit où il est appelé, ceci pour permettre à la classe de créer les clips au bon endroit dans l'appli.

On fait la même chose pour l'objet 'monMenu' qui est du type Menu et l'obet 'maBarre' dy type Barre.

La bibliothèque de ce fla contient tous les composants Flash utilisez dans les autres animations swf, car sinon ca ne marche pas. De plus, elle contient les différents éléments graphiques suivants : 'barre' qui est la barre qui permet de déplacer les applis, contient un champ texte pour le titre et 2 boutosn pour fermer (rouge) et cacher (bleu), 'loading' avec comme nom de liaison 'loading' qui est l'animation affichée lors du chargement d'une animation swf, 'vide' qui sert à chargé l'animation swf dans la fenêtre, 'fenetre' (liaison: 'fenetre' et classe AS2 : 'Fenetre') contient la barre, le clip loading et le clip vide et 'icone' (liaison: 'icone' et classe AS2 : 'Icone') qui permet de créer les icones.

^^ Retour ^^

icones.xml

Ce fichier XML est le premier fichier lu par l'application. Il permet de définir le nom de l'animation, l'image utilisé, le nom du fichier swf et la taille de l'animation swf. Il ressemble à cela :

<application>
 <icone nom="Dessin" swf="dessin.swf" icone="icones/dessin.jpg" haut="400" larg="550" />
 <icone nom="Sokobaide" swf="sokobaide.swf" icone="icones/sokobaide.jpg" haut="400" larg="550" />
</application>
^^ Retour ^^

Bureau.as

C'est la classe principale de l'application, car toutes les autres dépendent d'elle, mis à part menu (mais cette dernière effectue des actions sur le bureau)

import mx.utils.Delegate;
import mx.controls.Alert;

Dans cette classe, on a besoin des classe 'Delegate' et 'Alert' fourni avec Flash. La 1ère est utilisé pour faire passer des événements (onLoad, onRelease etc...) comme s'ils étaients exécutés au sein même de l'objet en cours, et non sur la scène; ceci est utile pour pouvoir notamment garder un accès sur les variables privées de la classe et l'accès aux fonctions de cette classe. la 2nde sert à afficher des alertes, composant de Flash

class Bureau {
  private var scene:MovieClip;
  private var monShare:SharedObject;
  private var buro:MovieClip;
  private var fondCl:MovieClip;
  private var fondImg:MovieClip;
  public var param:Object;
  private var mesIcones:XML;
  public var img:String;
  public static var FENETREPROF:Number;   public static var ESPACEICONE:Number = 90;
  public static var TAILLEICONE:Number = 50;
  public static var NOMBRECOL:Number = 5;

On crée ici véritablement notre classe, et on y déclare toutes les variables utilisé dedans. 'scene' est la référence à l'endroit où est appelé notre classe (élément passé en paramètre au constructeur de la classe), 'monShare' est mon ShareObject contenant les préférences enregistré de l'utilisateur, 'buro' est la référence au clip qui contient tous les éléments de notre application, 'fondCl' est le clip utilisé pour le filtre coloré du fond d'écran, 'fondImg' est le clip dans lequel on charge l'image du fond d'écran, 'param' est l'objet qui contient les préférences actuelles de l'utilisateur, 'mesIcones' est l'objet XML qui chargera le fichier icones.xml précédement documenté, 'img' est une chaîne de caratères utilisé pour ne pas rechargé l'image de fond s'il s'agit de celle en cours, 'FENETREPROF' est utilisé par la classe Icone pour savoir sur quelle profondeur crée une nouvelle fenêtre, 'ESPACEICONE' est un paramètre utilisé pour la création des icones qui correpont à l'espace entre 2 icones, 'TAILLEICONE' est aussi un paramètre qui indique la taille de l'icone et 'NOMBRECOL' indique le nombre d'icone maximum par colone.

  public function Bureau(scen:MovieClip) {
    scene = scen;
    monShare = SharedObject.getLocal("nyros");
    param = new Object();
    if (monShare.data.param == null) {
      // Aucun sauvegarde de paramètres du bureau, on initialise les valeurs.
      param.fondCl = "333333";
      param.fondClAlpha = 50;
      param.fondImg = "images/04.jpg";
      param.dateDiff = 0;
    } else {
      // On remet ses préférences
      param.fondCl = monShare.data.param.fondCl;
      param.fondClAlpha = monShare.data.param.fondClAlpha;
      param.fondImg = monShare.data.param.fondImg;
      param.dateDiff = monShare.data.param.dateDiff;
    }
    if (monShare.data.dejavu == null) {
      // Première visite
      Alert.show("Pour naviguer, cliquer sur les icônes.\nEt penser au clic droit sur le Bureau !\nBonne visite.", "Bienvenue sur NyrOS !", Alert.OK);
      monShare.data.dejavu = 1;
      monShare.flush();
    }
    // Création du bureau
    scene.createEmptyMovieClip("bureau", 1);
    buro = eval(scene+".bureau");
    buro._x = 0;
    buro._y = 0;
        // Création de l'image de fond
    buro.createEmptyMovieClip("fondImg", 2);
    fondImg = eval(buro+".fondImg");
    fondImg._x = 0;
    fondImg._y = 0;
    changefond();
    // Chargement des icones
    mesIcones = new XML();
    mesIcones.ignoreWhite = true;
    // Ici on dit que notre onload sera la fonction chargementIcones
    //    de note objet bureau, via Delegate.create
    mesIcones.onLoad = Delegate.create(this, chargementIcones);
    mesIcones.load("icones/icones.xml");
    var sonClic:Sound = new Sound();
    sonClic.loadSound("clic-icone.mp3", false);
    sonClic.setVolume(100);
    Icone.sonClic = sonClic;
  }

Il s'agit ici du constructeur (en effet, il a le même nom que la classe et est 'public'). On commence par attribuer des valeurs à un maximum de variable, à savoir scene (variable passé en paramètre à notre constructeur), monShare que l'on initialise sur le nom de cookie à "nyros", l'objet des paramètres 'param' : si des paramètres existent dans le shareObject "nyros" alors on les réaplique, sinon, on applique des valeurs par défaut. On applique ici les 4 seuls paramètres que l'on peut changer dans l'application, c'est à dire la couleur du filtre coloré, son alpha, l'image de fond et la différence de date. Le paramètrage de ses éléments se fait dans paramBureau et paramDate vu plus bas.
S'il s'agit de la première visite de l'utilisateur (si le shareObject a la valeur dejavu non défini) alors on affiche une alerte, grace au composant précompilé de Flash pour lui indiqué comment il doit/peut utiliser l'application. On défini aussi la la variable dejavu du shareObject pour qu'il n'est plus cette alert les prochianes fois et on écrit tout de suite notre cookie (flush).
Ensuite, on crée nos éléments graphique : on commence par créer notre clip vide bureau qui contiendra tout le reste. on défini la variable buro après sa création avec 'eval' pour ensuite le positionner tout en haut à gauche. Ensuite, on crée dans 'buro' le clip vide pour contenir l'image de fond. Ensuite, on dessine et charge l'image de fond suivant l'objet 'param' dans la fonction changefond() que l'on appelle ici. Cette fonction est aussi utilisé lors du changement par l'utilisateur des paramètres d'affichage.
Pour finir, on charge le fichier xml des icones dans mesIcones. On dit ici que l'onLoad de cet objet est en fait la fonction chargementIcones de notre objet, via Delegate.create();
Enfin, on crée notre objet 'Sound' 'sonClic', on charge le mp3 que l'on veut, on définit le son au maximum et on donne la référence au son à la variable 'sonClic' public static de la classe 'Icone'.

  private function changefond(Void):Void {
    var cl:String = param.fondCl;
    _global.style.setStyle("themeColor", "0x"+cl);
    var alpha:Number = param.fondClAlpha;
    buro.createEmptyMovieClip("fondCl", 3);
    fondCl = eval(buro+".fondCl");
    fondCl._x = 0;
    fondCl._y = 0;
    with (fondCl) {
      moveTo(0, 0);
      linestyle(0, "0x"+cl, alpha);
      beginFill("0x"+cl, alpha);
      lineTo(800, 0);
      lineTo(800, 600);
      lineTo(0, 600);
      endFill();
    }
    if (img != param.fondImg) {
      // Il y a changement d'image de fond, on recharge
      img = param.fondImg;
      fondImg.loadMovie(img);
    }
  }

Cette fonction commence par appliquer la couleur de l'objet 'param' au themeColor global de l'application pour avoir toujours la même couleur dans les composants de toutes les animations. Puis la fonction crée dans 'buro' un clip vide qui contient le filtre coloré et ensuite, remplis ce clip avec la couleur et l'alpha qui sont actutellement dans l'objet 'param'. Ensuite, si l'image actuelle est différente de celle de l'objet 'param', alors on lance un loadMovie pour la changer, et on change notre variable 'img' pour pouvoir faire notre test la prochaine fois correctement.

  private function chargementIcones(succes:Boolean):Void {
    if (succes) {
      var monIcone:XMLNode = mesIcones.firstChild;
      var col:Number = 0;
      var lin:Number = 0;
      for (var i = 0; i<monIcone.childNodes.length; i++) {
        var objInit:Object = new Object();
        if (i%NOMBRECOL == 0 && i>0) {
          col++;
          lin = 0;
        }
        objInit._x = col*ESPACEICONE+30+TAILLEICONE/2;
        objInit._y = lin*ESPACEICONE+30+TAILLEICONE/2;
        buro.attachMovie("icone", "icone"+i, 10+i, objInit);
        eval(buro+".icone"+i).init(monIcone.childNodes[i].attributes.nom, monIcone.childNodes[i].attributes.icone, monIcone.childNodes[i].attributes.swf, monIcone.childNodes[i].attributes.haut, monIcone.childNodes[i].attributes.larg);
        lin++;
      }
      FENETREPROF = 10+i+1;
    } else {
      trace("Erreur de chargement du xml des icones.");
    }
  }

Cette fonction (appelé lors de l'onLoad du XML des icones grace à Delegate.create()) vérifie d'abord si le fichier xml est bien chargé puis si c'est le cas, crée autant de clip qu'il y a d'élément dans le xml. Elle les crée dans le 'buro', en les nommant 'icone'+N, N commençant à 0. Quand les clips sont créés, on initialise l'icone en appelant la fonction 'init' de la classe Icone, en donnant en paramètre le nom, l'adresse de l'image, l'adresse du swf, la hauteur et la taille de l'animation. C'est véritablement cette fonction 'init' qui se charge de l'affichage des icones.
Dasn cette fonction, on fait bien attention à ne pas dépasser le nombre maximal d'icone par colonne, grace au test sur le modulo de i par rapport au nombre d'icone maxi par colonne.
Enfin on affecte la profondeur 'FENETREPROF' de début pour les fenêtres.

Les 3 dernière fonctions sont 'public' car elles sont appelés par d'autres animations swf de l'appli.

  public function setParam(cl:String, alpha:Number, fond:String):Void {
    param.fondCl = cl;
    param.fondClAlpha = alpha;
    param.fondImg = fond;
    changefond();
  }

Fonction appelé lors d'un changement de paramètre actuel : on passe en paramètres tous les nouvelles propriétés, on les applique à notre objet 'param', puis on appelle la fonction 'changeFond' qui redessine correctement le fond d'écran.

  public function setParamDate(diff:Number):Void {
    param.dateDiff = diff;
  }

Fonction appelé lors du changement des préférences de date de l'application, on donne en paramètres juste le décalage du temps par rapport à la date réelle et on enregistre dans notre objet 'param'.

  public function saveParam(Void):Void {
    monShare.data.param = param;
    monShare.flush();
  }

Fonction qui applique au shareObject les paramètres actuels contenu dans 'param' et qui écrit le shareObject tout de suite.

^^ Retour ^^

Icone.as

Cette classe est appliquée au clip 'icone' de l'animation NyrOS.fla; elle est utilisée pour afficher l'icone et créer les actions au survol de la souris et au clic.

import mx.utils.Delegate;

Comme pour la classe précédente, on a besoin de la classe 'Delegate' de Flash, donc on l'iporte avec la création de la classe.Ce phénomène ne sera plus expliqué pour les prochaines classes et importations suivantes.

class Icone extends MovieClip {
  private var iconeI:MovieClip;
  private var nom_txt:TextField;
  private var fenetreact:MovieClip;
  public var monMCL:MovieClipLoader;
  private static var ALPHA:Number = 30;
  private static var SCALE:Number = 70;
  public static var HAUTBAR:Number = 30;
  public static var sonClic:Sound;

On crée la classe 'Icone'. On l'a fait héritée de la classe MovieClip car on aura besoin de modifier son scale, son alpha son onRollOver, son onRollOut et son onRelease pour créer les différentes actions sur l'icone.
On définit aussi les variables de l'objet : 'iconeI' pour chargé l'image de l'icone, 'nom_txt' pour afficher le texte de l'icone, 'fenetreact' pour charger l'animation lors du clic et 'monMCL' pour faire le MovieClipLoader lors du clic.
Les variables static sont utilisé pour définir les paramètres fixes des icones et des fenêtres de l'application, que l'on utilisera lors du clic sur l'icone : 'ALPHA' définit l'aplpha de l'icone quand elle est inactive, 'SCALE' définit la proportion de l'icone quand elle est inactive, 'HAUTBAR' définit la hauteur de la barre des fenêtres.
Enfin, on a la variable public static 'sonClic' qui est la référence au son que l'on jouera lors du clic sur une icone, qui est définit par la classe 'Bureau'.

  public function init(nom:String, image:String, swf:String, haut:Number, larg:Number):Void {
    var prof:Number = this.getDepth();
    // Affichage de l'icone
    this.createEmptyMovieClip("icone", prof+1);
    var iconeI = eval(this+".icone");
    iconeI.loadMovie(image);
    iconeI._x = -Bureau.TAILLEICONE/2;
    iconeI._y = -Bureau.TAILLEICONE/2;
    // Affichage du nom
    this.createTextField("nom_txt", prof+2, -40, 30, 80, 25);
    nom_txt = eval(this+".nom_txt");
    nom_txt.text = nom;
    var mon_fmt:TextFormat = new TextFormat();
    mon_fmt.align = "center";
    mon_fmt.color = 0xFFFFFF;
    mon_fmt.size = 15;
    nom_txt.setTextFormat(mon_fmt);
    this._alpha = ALPHA;
    this._xscale = SCALE;
    this._yscale = SCALE;
    this.onRollOver = function(Void) {
      this._alpha = 100;
      this._xscale = 100;
      this._yscale = 100;
    };
    this.onRollOut = function(Void) {
      this._alpha = ALPHA;
      this._xscale = SCALE;
      this._yscale = SCALE;
    };
    this.onRelease = function(Void) {
      creerFenetre(nom, swf, haut, larg);
      if (sonClic.getBytesLoaded() == sonClic.getBytesTotal()) {
        sonClic.start();
      }
    };
  }

Cette fonction est en fait la fonction constructeur de la classe, appelée par Bureau.as lorsqu'il trouve une nouvelle icone dans le fichier xml.
Elle crée ici la clip pour charger l'image et la charge, affiche le texte de l'icone en lui appliquant un format de texte, définit les actions sur le bouton qui permetront de faire l'animation lors du survol (onRollOver) et de remettre les propriétés lorsque la souris s'en va (onRollOut).
On crée aussi l'action lors du clic sur l'icone qui applera la fonction creerFenetre de cette même classe, vu juste après, en passant en paramètres les mêmes que ceux passés à la classe 'init' qui définissent, dans l'ordre, le nom de l'animation, l'adresse de l'icone, l'adresse du swf, la hauteur de l'animation et la largeur de l'animation.
Ensuite, on vérifie que le son est complètement chargé et, si c'est le cas, on le joue une fois.

  private function creerFenetre(nom:String, swf:String, haut:Number, larg:Number):Void {
    // Création de la fenêtre
    var prof:Number = Bureau.FENETREPROF++;
    _parent.attachMovie("fenetre", "fenetre"+prof, prof);
    fenetreact = eval(_parent+".fenetre"+prof);
    _parent._parent.maBarre.ajouterBtn(fenetreact,nom);
    // On centre la fenêtre
    fenetreact._x = (Stage.width-larg)/2;
    fenetreact._y = (Stage.height-haut)/2;
    // Redimensionnement de ma barre et desboutons
    fenetreact.barre_mc._height = HAUTBAR;
    fenetreact.barre_mc._width = larg;
    fenetreact.fermer_btn._height = HAUTBAR;
    fenetreact.fermer_btn._y = HAUTBAR/2-2;
    var largbut:Number = 30/550*(larg);
    fenetreact.fermer_btn._width = largbut;
    fenetreact.fermer_btn._x = largbut/2+2;
    fenetreact.cacher_btn._height = HAUTBAR;
    fenetreact.cacher_btn._y = HAUTBAR/2-2;
    fenetreact.cacher_btn._width = largbut;
    fenetreact.cacher_btn._x = larg-(largbut/2+2);
    fenetreact.clip._y = HAUTBAR;
    fenetreact.init(this);
    fenetreact.barre_mc.titre_txt.text = nom;
    monMCL = new MovieClipLoader();
    var ecouteur:Object = new Object();
    // Ici on dit que notre onLoadComplete sera la fonction finiCharger
    //    de note objet Icone, via Delegate.create
    ecouteur.onLoadComplete = Delegate.create(this, finiCharger);
    monMCL.addListener(ecouteur);
    monMCL.loadClip(swf, fenetreact.clip);
  }

Cette classe est appelée lors du clic sur l'icone. Elle permet de créer la fenêtre avec les paramètres qui lui sont passés, ceux décris juste avant, en récupérant la profondeur à laquelle il doit créer la fenêtre, en incrémentant la valeur de la classe 'Bureau'.
Elle commence par attacher le clip 'fenetre' de la bibliothèque de l'animation principale et attribue à 'fenetreact' l'adresse de ce clip dans l'animation et ajoute le bouton de la fenêtre dans la barre de l'application. Puis on centre la nouvelle fenêtre sur l'animation. Ensuite, on redimensionne les boutons fermer et cacher de la barre suivant la largeur de la fenêtre. Une fois la fenêtre crée, on appelle la fonction 'init' de la classe 'Fenetre', qui lui ai associé.
Enfin, on crée le MovieClipLoader, on lance le chargement de l'animation et on crée son ecouteur pour pouvoir enlever le clip loading une fois l'animation chargé. C'est ici qu'on utilise le 'Delegate.create' pour appler la fonction 'finiCharger'.

  private function finiCharger(Void):Void {
    fenetreact.loading._visible = false;
  }

Cette fonction sert simplement à enlever le clip 'loading' une fois que l'animation est chargée.

^^ Retour ^^

Fenetre.as

Cette classe est appliquée au clip 'fenetre' de l'animation NyrOS.fla; elle est utilisée pour gérer les différentes action de la barre : la fermeture, le cachage et le déplacement.

class Fenetre extends MovieClip {
  private var icone:MovieClip;
  private var clip:MovieClip;
  private var barre_mc:MovieClip;
  private var fermer_btn:Button;
  private var cacher_btn:Button;
  private static var ALPHA:Number = 50;

On crée la classe 'Fenetre'. On l'a fait héritée de la classe MovieClip car on aura besoin de modifier son alpha lors du déplacement de la fenêtre et sa profondeur (depth) et aussi faire le déplacement.
On définit aussi les variable de l'objet : 'icone' qui est la référence à l'icone qui l'a créée, 'clip' qui est la référence au clip dans lequel on a chargé l'animation, 'barre_mc' qui est la référence à la barre de l'animation, 'fermer_btn' qui est la référence au bouton fermer de la fenêtre, 'cacher_btn' qui est la référence au bouton cacher de la fenêtre et la variable static 'ALPHA' qui définit l'alpha qu'on attribue à la fenêtre lors de son déplacement.

  public function init(ico:MovieClip):Void {
    icone = ico;
    dessus();
    barre_mc.onPress = function(Void) {
      _parent.drag();
    };
    barre_mc.onRelease = function(Void) {
      stopDrag();
      _parent._alpha = 100;
    };
    fermer_btn.onRelease = function(Void) {
      _parent.fermer();
    };
    cacher_btn.onRelease = function(Void) {
      _parent.cacher();
    };
  }

Cette fonction est en fait la fonction constructeur de la classe, applée par Icones.as lors du clic sur l'icone, juste après la création de la fenêtre.
On appelle la fonction 'dessus' de cette même classe pour mettre la fenêtre au 1er plan. Ensuite, on définit toutes les actions de notre barre : le 'onPress' pour commencer le déplacement, le 'onRelease' pour l'arrêter. On définit aussi les actions sur les 2 boutons de la barre pour fermer l'application et la cacher.
Toutes les actions sont des appels à des fonctions de la classe donc définies juste après, sauf le 'onRelease' de la barre, qui arrête le drag (stopDrag();) et remet l'alpha de l'animation à 100%.

  private function dessus(Void):Void {
    var nivMax:Number = this._parent._parent.getNextHighestDepth()-1;
    var monNiv:Number = this.getDepth();
    if (monNiv<nivMax) {
      this.swapDepths(nivMax);
    }
  }

Cette fonction sert à placer la fenêtre au 1er plan de l'application. On commence par récupérer la plus haute profondeur et la profondeur actuelle de la fenêtre. Si le niveau maximal actuel est strictement supérieur à celui actuel, alors on intervertit les 2 profondeur. Ainsi on obtient un vrai système qui, lorsqu'on clique sur une fenêtre, la mets au 1er plan.

  private function drag(Void):Void {
    startDrag(this);
    _alpha = ALPHA;
    dessus();
  }

Cette fonction appelée lorsqu'on presse sur la barre. Elle démarre simplement de drag, change l'alpha de la fenêtre et appelle la fonction 'dessus' pour remettre au 1er plan.

  private function fermer(Void):Void {
    if (this.clip.detruire != undefined) {
      this.clip.detruire();
    }
    icone.monMCL.unloadClip(this.clip);
    this._parent._parent.maBarre.enleverBouton(this);
    removeMovieClip(this);
  }

Cette fonction est appelée lorsqu'on clique sur le bouton fermer de la barre de la fenêtre. Avant de détruire le clip, on vérifie si une fonction 'detruire' n'est pas définie dans le clip et si c'est le cas, on l'appelle. Ceci sert à faire comme si dans notre animation on avait un destructeur (ca n'existe pas encore dans les classe Flash). J'ai été obligé de l'utiliser dans 2 animations, le sokoban et la vidéo; plus de détail le moment venu.
Ensuite, on enleve le bouton de la barre de l'application et on détruit notre clip charger en passant par le MovieClipLoader qui l'a chargé, et enfin, on détruit notre objet fenêtre.

  private function cacher(Void):Void {
    this.clip._visible = !this.clip._visible;
  }

Cette fonction sert juste à inverser le '_visible' du clip chargé, lorsqu'on clique sur le bouton cacher de la fenêtre.

^^ Retour ^^

Menu.as

Cette classe est appelée, au même titre que 'Bureau', directement sur NyrOs.fla. Elle sert à redéfinir le menu contextuel de l'application.

class Menu {
  public function Menu(scene:MovieClip) {
    // On crée notre objet de menu
    var menuPerso:Object = new ContextMenu();
    // On commence par tout cacher
    menuPerso.hideBuiltInItems();
    // Ajout dans le menu des paramètres
    menuPerso.customItems.push(new ContextMenuItem("Propriétés d'Affichage", proprietes));
    menuPerso.customItems.push(new ContextMenuItem("Propriétés de Date et Heure", propDate));
    menuPerso.customItems.push(new ContextMenuItem("Enregistrer les propriétés", saveParametres));
    scene.menu = menuPerso;
  }

On crée la classe 'Menu' et son constructeur, qui prend en paramètre la référence à l'endroit où est appelé la classe.
Le constructeur crée un nouvel objet et y ajoute les 3 éléments du menu de l'application, qui appeleront les fonctions de cette même classe. Enfin, on applique véritablement le menu à la scene.

  private function proprietes(obj, item) {
    obj.bureau.icone0.creerFenetre("Propriétés d'Affichage", "paramBureau.swf", 400, 300);
  }

Cette fonction est appelé lors du clic sur 'Propriétés d'Affichage' du menu contextuel que l'on a défini dans le constructeur. Son effet et d'appler la fonction 'creerFenetre' de la 1ère icone du 'bureau' présent sur la scene. On lui passe en paramètres les mêmes que l'on aurait mis dans le fichier 'icones.xml'

  private function propDate(obj, item) {
    obj.bureau.icone0.creerFenetre("Propriétés de Date et Heure", "paramDate.swf", 270, 450);
  }

Cette fonction est une jumelle de la précédente, sauf qu'elle sert lors du clic sur 'Propriétés de Date et Heure' du menu contextuel.

  private function saveParametres(obj, item) {
    obj.monBureau.saveParam();
  }

Cette fonction, appelée par le clic sur 'Enregistrer les propriétés' du menu contextuel, appele la fonction 'saveParam' de l'objet Bureau sur la scene.

^^ Retour ^^

Barre.as

Cette classe est appelée, au même titre que 'Bureau' et 'Menu', directement sur NyrOs.fla. Elle sert à afficher les boutons en bas de l'application.

import mx.controls.Button;
class Barre {
  private var scene:MovieClip;
  private var barre_mc:MovieClip;
  private var mesBouts:Array = new Array();
  private static var HAUTEUR:Number = 22;
  private static var LIMITELARG:Number = 120;
  public function Barre(scen:MovieClip) {
    scene = scen;
  }

On importe le classe 'Button' car on s'en sert au sein de la classe. Ensuite, on crée notre classe 'Barre', puis on crée nos variables : 'scene' qui est une référence à l'endroit où est appelé la classe, 'barre_mc' qui est le clip dans lequel on place nos boutons, 'mesBouts' est un tableau qui contiendra tous les boutons (nom et adresse du clip), 'HAUTEUR' qui est la configuration de la hauteur des boutons et 'LIMITELARG' qui est la configuration de la largeur maximale des boutons.
Ensuite, la fonction constructeur qui applique le paramètre passé à la variable 'scene'.

  public function ajouterBtn(fenetre:MovieClip, nom:String):Void {
    var obj:Object = new Object();
    obj.fenetre = fenetre;
    obj.nom = nom;
    mesBouts.push(obj);
    affBoutons();
  }

Cette fonction est appelée par la classe 'Icone' lors de la création d'une nouvelle fenêtre. Elle sert à ajouter dans le tabeau mesBouts un objet contenant le nom de la fenêtre et la référence au clip de la fenêtre. Puis, on appelle 'affBoutons' pour regénérer l'affichage de la barre.

  private function affBoutons(Void):Void {
    scene.createEmptyMovieClip("barre_mc", 5);
    barre_mc = scene.barre_mc;
    barre_mc._x = 0;
    barre_mc._y = Stage.height-HAUTEUR;
    var initObj:Object = new Object();
    initObj._x = 0;
    initObj._y = 0;
    initObj._height = HAUTEUR;
    var larg:Number = Stage.width/mesBouts.length;
    if (larg>LIMITELARG) {
      // On applqie la limite maxi du bouton
      larg = LIMITELARG;
    }
    initObj._width = larg;
    var monEcouteur:Object = new Object();
    monEcouteur.click = clicBouton;
    for (var i = 0; i<mesBouts.length; i++) {
      // On crée nos boutons
      initObj.label = mesBouts[i].nom;
      initObj.fenetre = mesBouts[i].fenetre;
      barre_mc.createClassObject(Button, "bouton"+i, 10+i, initObj);
      eval(barre_mc+".bouton"+i).addEventListener("click", monEcouteur);
      initObj._x += initObj._width;
    }
  }

Cette fonction est simple, on crée un clip vide 'barre_mc' que l'on place en bas de l'application. Puis on crée un objet pour initialiser les boutons : le x, y, la hauteur et la largeur seront tous identiques.
Ensuite, on fait une boucle sur notre tableau mesBouts et on crée nos boutons, en décalant à chaque fois le x de la largeur d'un bouton.
A noter aussi que l'on crée un ecouteur 'monEcouteur' qui écoute tous les click de tous les boutons et qui exécute la fonction 'clicBouton' de notre classe.

  private function clicBouton(evt:Object):Void {
    evt.target.fenetre._visible = !evt.target.fenetre._visible;
    if (evt.target.fenetre._visible) {
      // Si elle est visible, on la fait passer dessus !
      evt.target.fenetre.dessus();
    }
  }

Cette classe est éxécuter lors d'un clic sur un bouton : elle inverse la propriété '_visible' de la fenêtre et si on la rend visible, on la remet au 1er plan en appelant la fonction 'dessus' de la classe 'Fenetre'

  public function enleverBouton(fenetre:MovieClip):Void {
    for (var i = 0; i<mesBouts.length; i++) {
      if (mesBouts[i].fenetre == fenetre) {
        // On est sur celui à enlever !
        mesBouts.splice(i,1);
        break;
      }
    }
    affBoutons();
  }

Cette fonction est appelée apr la classe 'Fenetre' lorsqu'une fenêtre est fermée : on parcourt le tableau 'mesBouts' et quand on est sur le bouton qui appelle la fenêtre passé en paramètre, alors on enlève l'entrée du tableau et on sort de la boucle.Ensuite, on appelle 'affBoutons' pour refaire l'affichage de la barre.

^^ Retour ^^

paramBureau.fla

Cette animation, appelée par le menu contextuel de l'application, est utilisé pour paramétrer la couleur du filtre, son alpha et l'image du fond d'écran.
Dans sa bibliothèque, il y a tous les composants Flash utlisé dans cette animation : 'Button', 'List' et 'NumericStepper'.
Au niveau du code, seulement 2 lignes :

var param:Object = this._parent._parent._parent.monBureau.param;
var monParamBureau:ParamBureau = new ParamBureau(this, param.fondCl, param.fondClAlpha, param.fondImg);

Avec la 1ère ligne, on va chercher l'objet 'param' actuel du bureau. Ensuite, on crée notre objet 'monParamBureau' avec la classe 'ParamBureau', en lui passant en paramètre la référence d'où est appelé l'objet (this), la couleur du fond actuelle, l'alpha du fond actuel et l'image de fond actuelle.

^^ Retour ^^

fonds.xml

Ce fichier xml permetr de savoir quels sont les fonds d'écran disponibles, en donnant un tritre, une adresse pour le fond et une adresse pour la vignette à chaque fond disponible. Il ressemble à cela :

<fonds>
 <image nom="Pièce couchée" vign="images/01-v.jpg" img="images/01.jpg" />
 <image nom="Pièce debout" vign="images/02-v.jpg" img="images/02.jpg" />
</fonds>
^^ Retour ^^

ParamBureau.as

C'est la classe qui permet d'afficher tous les éléments dont l'utilisateur a besoin pour faire les changements d'affichage du bureau et les appliquer.

import mx.utils.Delegate;
import mx.controls.Button;
class ParamBureau {
  private var scene:MovieClip;
  public var clActuelle:String;
  public var alphaActuel:Number;
  public var fondActuel:String;
  private var loadImages:XML;

On commence par importer la classe 'Delegate' et 'Button' puisqu'on en a besoin dans la classe. Ensuite on crée la classe 'ParamBureau' et on définit les variables qu'elle utilisera : 'scene' qui est une référence à l'endroit où est appelé la classe, 'clActuelle' pour savoir avec quelle couleur on est arrivé sur la classe, 'alphaActuel' pour savoir avec quel alpha on est arrivé sur la classe, 'fondActuel' pour savoir avec quelle image de fond on est arrivé sur la classe et 'loadImages' qui nous permettra de charger fonds.xml.

  public function ParamBureau(scen:MovieClip, cl:String, alpha:Number, fond:String) {
    scene = scen;
    clActuelle = cl;
    alphaActuel = alpha;
    fondActuel = fond;
    creerElementFixe();
  }

Il s'agit ici de la classe constructeur, qui prend en paramètres ceux donnés et décrits dans 'paramBureau.fla'; on les récupère et on affecte les variable de la classe avec ces valeurs là. Ensuite, on appelle la fonction 'creerElementFixe' décrite juste après.

  private function creerElementFixe(Void):Void {
    // Création du fond de couleur gris
    scene.createEmptyMovieClip("fondGris", 1);
    var fondGris:MovieClip = eval(scene+".fondGris");
    var cl:String = "A0A0A0";
    with (fondGris) {
      moveTo(0, 0);
      lineStyle(0, "0x"+cl, 100);
      beginFill("0x"+cl, 100);
      lineTo(300, 0);
      lineTo(300, 400);
      lineTo(0, 400);
      endFill();
    }
    // Création du texte pr la couleur
    scene.createTextField("couleur_txt", 2, 20, 10, 145, 20);
    scene.couleur_txt.textColor = "0xFFFFFF";
    scene.couleur_txt.selectable = false;
    scene.couleur_txt.text = "Choix de la couleur de filtre :";
    // Création du bouton pour choisir la couleur
    scene.createEmptyMovieClip("chxCol_mc", 3);
    scene.chxCol_mc._x = 170;
    scene.chxCol_mc._y = 10;
    scene.chxCol_mc.clAct = clActuelle;
    var monEcouteur:Object = new Object();
    // Ici on dit que notre choix sera la fonction newColor
    //    de notre objet Param, via Delegate.create
    monEcouteur.choix = Delegate.create(this, newColor);
    scene.chxCol_mc.onRelease = function(Void) {
      this._parent.createEmptyMovieClip("choix", 100);
      var choix:MovieClip = eval(this._parent+".choix");
      var maCouleur:ChxCouleur = new ChxCouleur(choix, 15, 50, this.clAct);
      maCouleur.addEventListener("choix", monEcouteur);
    };

Cette fonction (découpée en plusieurs morceaux pour les explications) commence par crée un clip vide pour le fond gris, et dessine ce fond gris (beginFill). Ensuite, on crée un champ texte pour écrire 'Choix de la couleur de filtre :' qui est non sélectionnable avec une couleur blanche. Puis, on crée le bouton qui permettra d'afficher un colorChooser : il s'agit en fait d'un clip vide que l'on remplis de la couleur actuelle du fond et auquel on met un 'onRelease'. Lors du clic sur ce bouton, on crée d'abord un clip vide 'choix' dans lequel la classe 'ChxCouleur' détaillée plus bas dessinera tous les élements pour le choix de la couleur.
A noter aussi la création de l'écouteur de l'événement 'choix' qui sera diffusé par la classe 'ChxCouleur' lorsque l'utilisateur aura fait son choix de couleur. Cet évenement appellera la fonction 'newColor' de la classe 'ParamBureau', grace à 'Delegate.create', déjà vu plus haut.

    // Création du texte pr l'alpha
    scene.createTextField("alpha_txt", 4, 20, 40, 145, 20);
    scene.alpha_txt.textColor = "0xFFFFFF";
    scene.alpha_txt.selectable = false;
    scene.alpha_txt.text = "Choix de l'alpha de filtre :";
    // Création du NumericStepper pour l'alpha
    var initObj:Object = new Object();
    initObj._x = 170;
    initObj._y = 40;
    initObj._width = 58;
    initObj._height = 22;
    initObj.maximum = 100;
    initObj.value = alphaActuel;
    scene.attachMovie("NumericStepper", "alphaRegl", 5, initObj);
    // Ici on dit que notre change sera la fonction newAlpha
    //    de notre objet Param, via Delegate.create
    monEcouteur.change = Delegate.create(this, newAlpha);
    scene.alphaRegl.addEventListener("change", monEcouteur);

Ensuite, on passe à la création du texte pour le choix de l'alpha du filtre coloré, de la même manière que le 1er texte. Puis, on crée un 'NumericStepper' avec un attachMovie, car le composant se trouve dans la bibliothèque avec l'identifiant de liaison 'NumericStepper'. On crée un écouteur se ce dernier qui, lorsqu'on changera sa valeur, appelera la fonction 'newAlpha', vie 'Delegate.create'.

    // Création du texte pr l'image
    scene.createTextField("image_txt", 6, 20, 70, 145, 20);
    scene.image_txt.textColor = "0xFFFFFF";
    scene.image_txt.selectable = false;
    scene.image_txt.text = "Choix de l'image de fond :";
    // Création de la List pour l'image
    var initObj:Object = new Object();
    initObj._x = 20;
    initObj._y = 90;
    initObj._width = 250;
    initObj._height = 90;
    scene.attachMovie("List", "chxImage", 7, initObj);
    // Ici on dit que notre change sera la fonction newImage
    //    de notre objet Param, via Delegate.create
    var monEcouteur2:Object = new Object();
    monEcouteur2.change = Delegate.create(this, newImage);
    scene.chxImage.addEventListener("change", monEcouteur2);
    loadImages = new XML();
    loadImages.ignoreWhite = true;
    loadImages.load("images/fonds.xml");
    // Ici on dit que notre onLoad sera la fonction chargeXML
    //    de notre objet Param, via Delegate.create
    loadImages.onLoad = Delegate.create(this, chargeXML);
    // Création du cadre pour l'aperçu
    scene.createEmptyMovieClip("cadre_mc", 8);
    scene.cadre_mc._x = 50;
    scene.cadre_mc._y = 190;
    scene.cadre_mc.loadMovie("images/cadre.jpg");
    // Création du clip pour l'image
    scene.createEmptyMovieClip("apercu_mc", 9);
    scene.apercu_mc._x = 50+12;
    scene.apercu_mc._y = 190+16;
    // Affichage de l'aperçu
    traceFiltre();

Toujours de la même façon, on crée un champ texte pour afficher 'Choix de l'image de fond :' et on crée un composant 'List', toujours importé depuis la bibliothèque. On lui applique aussi un écouteur, via 'Delegate.create' qui appelera la fonction 'newImage'.
Ensuite, on charge le fichier xml (en ignorant les espaces blancs), et lorsqu'il sera fini de charger, la fonction 'chargeXML'sera appelée.
Puis, on crée notre clip pour charger l'image du cadre de prévisualisation et un autre clip vide pour charger la vignette du fond sélectionné. Et on peut appeller la fonction 'traceFiltre' qui se chargera de l'affichage de la prévisualisation.

    //Création du bouton Annuler
    var initObj:Object = new Object();
    initObj.label = "Annuler";
    initObj._x = 160;
    initObj._y = 370;
    initObj._width = 60;
    // Ici on dit que notre onRelease sera la fonction annuler
    //    de notre objet Param, via Delegate.create
    initObj.onRelease = Delegate.create(this, annuler);
    scene.createClassObject(Button, "annuler_btn", 10, initObj);
    initObj.label = "Appliquer";
    initObj._x = 230;
    initObj._width = 60;
    // Ici on dit que notre onRelease sera la fonction annuler
    //    de notre objet Param, via Delegate.create
    initObj.onRelease = Delegate.create(this, appliquer);
    scene.createClassObject(Button, "valider_btn", 11, initObj);
    initObj.label = "OK";
    initObj._x = 90;
    initObj._width = 60;
    // Ici on dit que notre onRelease sera la fonction annuler
    //    de notre objet Param, via Delegate.create
    initObj.onRelease = Delegate.create(this, ok);
    scene.createClassObject(Button, "ok_btn", 12, initObj);
  }

Ici, on crée simplement nos 3boutons de l'animation : annuler, appliquer et ok, avec chacun une fonction qui lui est attribuer lors de son clic.

  private function newColor(evt:Object) {
    clActuelle = evt.couleur;
    traceFiltre();
  }

Cette fonction, appelée lors du choix d'une nouvelle couleur, attribue la nouvelle couleur à la variable de la classe, puis appelle la fonction 'traceFiltre' pour refaire la prévisualisation et le bouton pour choisir la couleur.

  private function newAlpha(evt:Object) {
    alphaActuel = evt.target.value;
    traceFiltre();
  }

Idem que newColor sauf qu'elle réagit au changement de l'alpha.

  private function traceFiltre(Void):Void {
    // On remet la bonne couleur au petit carré de choix de la couleur
    scene.chxCol_mc.clAct = this.clActuelle;
    with (scene.chxCol_mc) {
      moveTo(0, 0);
      lineStyle(0, "0x000000", 100);
      beginFill("0x"+this.clActuelle, 100);
      lineTo(20, 0);
      lineTo(20, 20);
      lineTo(0, 20);
      endFill();
    }
    // On applique à l'apercu
    scene.apercu_mc.createEmptyMovieClip("filtre_mc", 2);
    with (scene.apercu_mc.filtre_mc) {
      moveTo(0, 0);
      lineStyle(0, "0x"+this.clActuelle, this.alphaActuel);
      beginFill("0x"+this.clActuelle, this.alphaActuel);
      lineTo(160, 0);
      lineTo(160, 120);
      lineTo(0, 120);
      endFill();
    }
  }

Cette fonction sert à redessiner le filtre coloré, avec la couleur et l'alpha actuellement sélectionné; on fait cela grâce aux méthodes de dessin traditionnels d'un MovieClip.

  private function newImage(evt:Object) {
    fondActuel = evt.target.selectedItem.img;
    affVign(evt.target.selectedItem.vign);
  }

Cette fonction est appelé lorsque l'on sélectionne un autre fond d'écran dans la liste. Elle redéfinie simplement la variable 'fondActuel' avec le nouveau fond sélectionné et appelle la fonction 'affVign' pour afficher la vignette, en passant en paramètre, l'adresse de la vignette à affciher.

  private function affVign(vign:String) {
    scene.apercu_mc.createEmptyMovieClip("image_mc", 1);
    scene.apercu_mc.image_mc.loadMovie(vign);
  }

Cete fonction crée un clip vide 'image_mc' dans 'apercu_mc' pour ensuite chargée dedans la nouvelle vignette, qu'on lui a passée en paramètre.

  private function chargeXML(succes) {
    if (succes) {
      var mesFonds:Array = loadImages.firstChild.childNodes;
      for (var i = 0; i<mesFonds.length; i++) {
        if (fondActuel == mesFonds[i].attributes.img) {
          affVign(mesFonds[i].attributes.vign);
        }
        var initObj:Object = new Object();
        initObj.label = mesFonds[i].attributes.nom;
        initObj.img = mesFonds[i].attributes.img;
        initObj.vign = mesFonds[i].attributes.vign;
        scene.chxImage.addItem(initObj);
      }
    } else {
      trace("Erreur de chargement du XML");
    }
  }

Cette fonction est appelée lorsque 'fonds.xml' est fini de charger. Elle permet de remplir la liste des fond disponibles, en parcourant ligne par ligne le fichier xml. A chaque ligne de la liste, on donne le 'label' (ce qu'on affiche dans la liste) et 2 autres paramètres personnalisés qui permettre de connaitre les adresses de l'image et de la vignette : 'img' et 'vign'.
Si on voit que la ligne qu'on parcourt est celle qui correspond à 'fondActuel' (donc le fond d'écran actuel de l'application), alors on appelle la fonction 'affVign' pour afficher la vignette correspondante.

  private function annuler(Void):Void {
    scene._parent.fermer_btn.onRelease();
  }

Cette fonction, appelée lors du clic sur le bouton annuler, exécute le 'onRelease' du bouton 'fermer_btn' de la fenêtre qui contient l'application, ce qui aura donc pour effet final de fermer l'animation.

  private function appliquer(Void):Void {
    scene._parent._parent._parent.monBureau.setParam(clActuelle, alphaActuel, fondActuel);
  }

Cette fonction, appelée lors du clic sur le bouton appliquer, appelle la fonction 'setParam' de l'objet 'monBureau' (objet créé dans 'NyrOS.fla') en passant en paramètres les paramètres sélectionnés actuellement dans la fenêtre. Ceci aura donc pour effet de changer le fond d'écran comme configurer.

  private function ok(Void):Void {
    scene._parent._parent._parent.monBureau.setParam(clActuelle, alphaActuel, fondActuel);
    scene._parent.fermer_btn.onRelease();
  }

Cette dernière fonction, appelée lors du clic sur le bouton ok, est en fait une combinaison des 2 dernières : on applique les paramètres et on ferme la fenêtre.

^^ Retour ^^

ChxCouleur.as

Cette classe est très intéressante car elle permet de dessiner et de créer toutes les actions nécessaires à un colorChooser, mais entièrement par le code. A noter que cette classe est aussi utiliser dans 'Dessin.as', et pourrait l'être dans tout autre classe ou fla.

// Pour pouvoir faire un écouteur
import mx.events.EventDispatcher;
import mx.controls.Button;
class ChxCouleur extends MovieClip {
  private var dispatchEvent:Function;
  private var dispatchQueue:Function;
  public var addEventListener:Function;
  public var removeEventListener:Function;
  private var scene:MovieClip;
  private var couleur:MovieClip;
  private var couleurs:MovieClip;
  private var cadreCl:MovieClip;
  private var clActuelle:String;
  private static var TAILLE:Number = 15;
  private static var NBLIN:Number = 18;
  private static var HAUTVOIR:Number = 18;
  private static var LARGVOIR:Number = 18;
  private static var POSYVOIR:Number = 185;

On commence par importer les classes qui nous serviront, a savoir 'EventDispatcher' pour émettre des écouteurs personnalisés et 'Button'. Ensuite, on crée la classe 'ChxCouleur', en la faisant héritée de MovieClip pour pouvoir utilisé 'this._parent' à différents endroits. Ensuite, on crée et on type les variables qui nous seront utiles au sein de la classe : 'dispatchEvent' et 'dispatchQueue' sont des fonctions pour l'évenement personnalisé, 'addEventListener' et 'removeEventListener' pour permettre aux codes extérieurs de créer et enlever les évenements, 'scene' pour avoir une référence à l'endroit où est appelé l'objet, 'couleur' pour notre clip qui contiendra tous nos éléments du colorPicker, 'couleurs' qui contiendra tous nos carrés de choix de couleur, 'cadreCl' permettra d'afficher la couleur actuelement sélectionnée et 'clActuelle' pour savoir qu'elle est la couleur actuellement sélectionnée.
Ensuite, on définit des variables statiques qui définissent les tailles et l'affichage du colorPicker : 'TAILLE' définit la taille d'un carré de choix de couleur, 'NBLIN' définit le nombre de carrés qu'on met sur une ligne, 'HAUTVOIR' et 'LARGVOIR' définissent la hauteur et la largeur du rectangle de visualisation de la couleur sélectionnée et 'POSYVOIR' définit la position en Y par rapport au haut du rectangle de visualistaion.

  public function ChxCouleur(scen:MovieClip, posX:Number, posY:Number, clAct:String) {
    // Initialisation pour l'écouteur
    mx.events.EventDispatcher.initialize(this);
    scene = scen;
    clActuelle = clAct;
    // Création du clip pour les couleurs
    scene.createEmptyMovieClip("couleur", 1);
    couleur = eval(scene+".couleur");
    couleur._x = posX;
    couleur._y = posY;
    couleur.cetObjet = this;
    couleur.createEmptyMovieClip("couleurs", 2);
    couleurs = eval(couleur+".couleurs");
    // Création de tous mes carrés de couleur
    var ligne:Number = 0;
    var colonne:Number = 0;
    var maCol:String;
    var act:Number = 0;
    for (var r = 0; r<256; r=r+51) {
      for (var v = 0; v<256; v=v+51) {
        for (var b = 0; b<256; b=b+51) {
          maCol = formatHex(r)+formatHex(v)+formatHex(b);
          if (colonne>NBLIN-1) {
            ligne++;
            colonne = 0;
          }
          traceCarre(maCol, ligne, colonne, act);
          colonne++;
          act++;
        }
      }
    }

C'est la fonction constructeur (découpée en plusieurs morceaux) qui prend comme paramètres dans l'ordre : la référence d'où est appelée le colorPicker, la position en X où l'on doit le placer, la position en Y où l'on doit le placer et la couleur que l'on attribue au départ.
Ensuite, la fonction initialise les événements de la classe, puis réatribue les variables passées en paramètres à celle de l'objet. Après on commence par crée le MovieClip vide 'couleur' en le plaçant comme demandé par les paramètres (on fait : "couleur.cetObjet = this" pour pouvoir retrouver dans les boutons carrés la référence à cette classe), puis on crée dans couleur le MovieClip 'couleurs' qui lui contiendra tous les carrés de choix de couleur. Enfin, on crée nos carré 1 par 1, avec 3 boucles imbriqués l'une dans l'autre pour ne donner que des couleurs web. On utilise les fonction 'formatHex' et 'traceCarre' de cette même classe, vues plus loin.

    // Création du champ texte
    couleur.createTextField("affichage_txt", 3, LARGVOIR+5, POSYVOIR, 50, TAILLE+5);
    couleur.affichage_txt.type = "input";
    couleur.affichage_txt.textColor = 0x000000;
    couleur.affichage_txt.background = 0xFFFFFF;
    couleur.affichage_txt.border = true;
    couleur.affichage_txt.restrict = "a-f A-F 0-9";
    couleur.affichage_txt.maxChars = 6;
    couleur.affichage_txt.text = clAct;
    couleur.affichage_txt.onChanged = chgText;

On crée ici notre champ texte de saisie en spécifiant ses aspects graphiques, ses caractères autorisés et son nombre maximal de caratères, son texte par défaut et son action lorsqu'on change son contenu, l'appel de la fonction 'chgText'.

    // on définit la fonction traceVoir de couleur identique à traceVoir de notre objet
    couleur.traceVoir = traceVoir;
    // idem pour valider et choisi
    couleur.valider = valider;
    couleur.choisi = choisi;

Ici, on crée en quelques sortes des alias de fonction dans couleur, afin que les boutons carrés, le champs texte et les boutons puissent y accéder.

    // Création du cadre pour voir la couleur sélectionnée
    couleur.createEmptyMovieClip("cadreCl", 4);
    cadreCl = eval(couleur+".cadreCl");
    cadreCl._x = 0;
    cadreCl._y = POSYVOIR;
    couleur.traceVoir();
    var initObj:Object = new Object();
    initObj.label = "Valider";
    initObj._x = LARGVOIR+5+100;
    initObj._y = POSYVOIR;
    initObj._width = 60;
    initObj.onRelease = function() {
      this._parent.cetObjet.choisi(this._parent.affichage_txt.text);
    };
    couleur.createClassObject(Button, "valider_btn", 5, initObj);
    var initObj:Object = new Object();
    initObj.label = "Annuler";
    initObj._x = LARGVOIR+5+100+80;
    initObj._y = POSYVOIR;
    initObj._width = 60;
    initObj.onRelease = function() {
      this._parent.cetObjet.annuler();
    };
    couleur.createClassObject(Button, "annuler_btn", 6, initObj);
  }

Ce genre de code a déjà été vu dans d'autres classes, on crée un clip vide 'cadreCl' puis on appelle la fonction 'couleur.traceVoir' pour faire l'affichage. Ensuite, on crée 2 boutons valider et annuler, avec des événement qui exécutent des fonctions de cette classe.

  private function formatHex(num:String):String {
    num = num.toString(16);
    if (num.length == 1) {
      num = "0"+num;
    }
    return num;
  }

Cette fonction sert à transformer un nombre décimal en un nombre hexadécimal.

  private function traceCarre(cl:String, ligne:Number, colonne:Number, act:Number):Void {
    couleurs.createEmptyMovieClip("cl"+act, 10+act);
    var mAct:MovieClip = eval(couleurs+".cl"+act);
    mAct._x = colonne*TAILLE;
    mAct._y = ligne*TAILLE;
    mAct.couleur = cl;
    var maCl:Number = parseInt(cl, 16);
    mAct.moveTo(0, 0);
    mAct.lineStyle(0, maCl, 100);
    mAct.beginFill(maCl, 100);
    mAct.lineTo(TAILLE, 0);
    mAct.lineTo(TAILLE, TAILLE);
    mAct.lineTo(0, TAILLE);
    mAct.endFill();
    mAct.onRollOver = affCl;
    mAct.onRelease = choixClic;
  }

Cette fonction crée un clip et dessine dedans un carré dans 'couleurs' avec les paramètres qui lui sont donnés : la couleur, son emplacement et son numéro. On définit la variable 'couleur' au sein de ce clip et on applique à chaque carré la fonction 'affCl' lors du survol et la fonction 'choixClic' lors du clic.

  private function chgText() {
    this._parent.affichage_txt.text = this._parent.affichage_txt.text.toUpperCase();
    this._parent.traceVoir();
  }

Cette fonction est appelée quand l'utilisateur change manuellement le contenu de la zone de saisie pour la couleur. On remet tout en majuscule, puis on redessine le carré de visualisation.

  private function affCl(Void):Void {
    this._parent._parent.affichage_txt.text = this.couleur.toUpperCase();
    this._parent._parent.traceVoir();
  }

Cette fonction, appelée lors du survol d'un carré de couleur, change la valeur du champ texte en mettant la sienne transformé en majuscule, puis retrace le carré de visualisation.

  private function choixClic(Void):Void {
    this._parent._parent.valider(this.couleur);
  }

Cette fonction, appelée lors du clic sur un carré de couleur, appelle la fonction 'valider' de la classe pour fermer le colorPicker et renvoyer la couleur sur laquelle on a cliquée, qui est ici envoyer en paramètre.

  private function traceVoir(Void):Void {
    var maCl:Number = parseInt(this._parent.couleur.affichage_txt.text, 16);
    cadreCl.moveTo(0, 0);
    cadreCl.lineStyle(0, 0x000000, 100);
    cadreCl.beginFill(maCl, 100);
    cadreCl.lineTo(LARGVOIR, 0);
    cadreCl.lineTo(LARGVOIR, HAUTVOIR);
    cadreCl.lineTo(0, HAUTVOIR);
    cadreCl.endFill();
  }

Cette fonction sert simplement à redessiner le carré de visualisation avec les méthodes de dessin habituelles de MovieClip, en récupérant la couleur qui est indiqué dans le champ texte.

  private function valider(cl:String):Void {
    this._parent.couleur.cetObjet.choisi(cl);
  }

Cette fonction sert simplement à appler la fonction choisi pour indiquer que l'utilisateur à fait son choix de couleur, en envoyant en paramètre la couleur choisie.

  private function annuler(Void):Void {
    choisi(clActuelle);
  }

Cette fonction fait la même chose que la précédente, mis à pat qu'elle envoie en paramètre la couleur avec laquelle on est arrivée, 'clActuelle'.

  private function choisi(cl:String):Void {
    // Préparation de l'objet pour envoyer notre Evt
    var objEvt:Object = new Object();
    // Nom de l'évenement
    objEvt.type = "choix";
    objEvt.target = this;
    // Variable supplémantaire
    objEvt.couleur = cl;
    // Envoi de l'Evt !
    dispatchEvent(objEvt);
    // Suppression du clip crée pour le choix des couleurs
    couleur.removeMovieClip();
  }

Cette fonction est celle qui va distribué l'événement 'choisi', lorsque l'utilisateur aura choisi une couleur, soit en cliquant sur le bouton valider ou sur un carré de couleur, soit en annulant pour revenir à la couleur de départ.
on crée un objet qui nous permettra de faire notre événement : le type est en fait le nom que l'écouteur devra écouter, target est une référence à l'occurrence qui émet l'événement, couleur est la couleur choisie. Ensuite, on distribue notre événement avec 'dispatchEvent'. Enfin, on détruit notre colorPicker.

^^ Retour ^^

paramDate.fla

Cette animation, appelée par le menu contextuel de l'application, est utilisé pour paramétrer la date et l'heure de l'application.
Dans sa bibliothèque, il y a tous les composants Flash utlisé dans cette animation : 'Button', 'DateChooser' et 'NumericStepper'. Il y a aussi les 3 aiguilles pour les heures minutes secondes ayant respectivement pour nom et nom de laision : 'aiguilleH', 'aiguilleM' et 'aiguilleS'.
Au niveau du code, seulement 2 lignes :

var param:Object = this._parent._parent._parent.monBureau.param;
var monParamDate:ParamDate = new ParamDate(this, param.dateDiff);

Avec la 1ère ligne, on va chercher l'objet 'param' actuel du bureau. Ensuite, on crée notre objet 'monParamDate' avec la classe 'ParamDate', en lui passant en paramètre la référence d'où est appelé l'objet (this) et la différence actuelle pour le réglage du temps.

^^ Retour ^^

ParamDate.as

C'est la classe qui permet d'afficher tous les éléments dont l'utilisateur a besoin pour faire les changements de date et heure et les appliquer.

import mx.utils.Delegate;
import mx.controls.Button;
class ParamDate {
  private var scene:MovieClip;
  private var horloge:MovieClip;
  var maDate:Date;
  private var diffAct:Number;

On commence par importer les classes qui nous serviront dans la classe : 'Delegate' et 'Button', déjà commenté plus haut. Ensuite on crée notre classe 'ParamDate' et on déclare nos varibles utilisés dans la classe : 'scene' est une référence à l'endroit où est appelé la classe, 'horloge' est le clip dans lequelon affichera le cadran de l'horloge, les aiguilles et les 'NumericStepper', 'maDate' sera l'objet Date dans lequel on enregistrera la date réelle de l'utilisateur et 'diffAct' et la différence en secondes par rapport à la date réelle actuelle que l'utilisateur a paraméter.

  public function ParamDate(scen:MovieClip, diff:Number) {
    scene = scen;
    diffAct = diff;
    creerElementFixe();
    // Création du setInterval dans notre objet ParamDate
    setInterval(this, "refaireDate", 1000);
    refaireDate();
    scene.chxDate.selectedDate = maDate;
  }

Cette fonction est le constructeur de la classe. Elle commence par affecter les valeurs passés en paramètres aux variables de la classe, puis on appelle la fonction 'creerElementFixe' pour faire l'affichage. Ensuite, on initialise un setInterval dans l'objet actuel (this) sur la fonction 'refaireDate' toutes les secondes (1000 milisecondes). On appelle tout de suite la fonction 'refaireDate' et on affecte la date actuelle (pas celle de l'ordinateur mais celle avec les paramètres de l'utilisateur) au 'DateChooser'.

  private function creerElementFixe(Void):Void {
    // Création du fond de couleur gris
    scene.createEmptyMovieClip("fondGris", 1);
    var fondGris:MovieClip = eval(scene+".fondGris");
    var cl:String = "A0A0A0";
    with (fondGris) {
      moveTo(0, 0);
      lineStyle(0, "0x"+cl, 100);
      beginFill("0x"+cl, 100);
      lineTo(450, 0);
      lineTo(450, 270);
      lineTo(0, 270);
      endFill();
    }
    // Création du DateChooser
    var initObj:Object = new Object();
    initObj._x = 10;
    initObj._y = 10;
    scene.attachMovie("DateChooser", "chxDate", 2, initObj);
    // On francise le DateChosser
    scene.chxDate.firstDayOfWeek = 1;
    scene.chxDate.dayNames = ["D", "L", "M", "M", "J", "V", "S"];
    scene.chxDate.monthNames = ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"];
    var monEcouteur:Object = new Object();
    scene.chxDate.addEventListener("change", monEcouteur);
    // Ici on dit que notre change sera la fonction newDate
    //    de notre objet ParamDate, via Delegate.create
    monEcouteur.change = Delegate.create(this, newDate);
    // Création de l'horloge
    scene.createEmptyMovieClip("horloge", 3);
    horloge = eval(scene+".horloge");
    horloge._x = 240+90;
    horloge._y = 10+90;
    // Création et load du fond de l'horloge
    horloge.createEmptyMovieClip("fond", 1);
    horloge.fond.loadMovie("images/horloge.jpg");
    horloge.fond._x = -90;
    horloge.fond._y = -90;
    // Création des aiguilles
    initObj._x = 0;
    initObj._y = 0;
    horloge.attachMovie("aiguilleH", "heure", 2, initObj);
    horloge.attachMovie("aiguilleM", "minute", 3, initObj);
    horloge.attachMovie("aiguilleS", "seconde", 4, initObj);
    // Création des numericStepper
    initObj._x = 260;
    initObj._y = 200;
    initObj._width = 48;
    initObj._height = 22;
    initObj.maximum = 23;
    scene.attachMovie("NumericStepper", "heureStep", 4, initObj);
    var ecoutHeure:Object = new Object();
    // Ici on dit que notre change sera la fonction newHeure
    //    de notre objet ParamDate, via Delegate.create
    ecoutHeure.change = Delegate.create(this, newHeure);
    scene.heureStep.addEventListener("change", ecoutHeure);
    initObj._x = 310;
    initObj._width = 48;
    initObj._height = 22;
    initObj.maximum = 59;
    scene.attachMovie("NumericStepper", "minuteStep", 5, initObj);
    var ecoutMinute:Object = new Object();
    // Ici on dit que notre change sera la fonction newMinute
    //    de notre objet ParamDate, via Delegate.create
    ecoutMinute.change = Delegate.create(this, newMinute);
    scene.minuteStep.addEventListener("change", ecoutMinute);
    initObj._x = 360;
    initObj._width = 48;
    initObj._height = 22;
    initObj.maximum = 59;
    scene.attachMovie("NumericStepper", "secondeStep", 6, initObj);
    var ecoutSeconde:Object = new Object();
    // Ici on dit que notre change sera la fonction newSeconde
    //    de notre objet ParamDate, via Delegate.create
    ecoutSeconde.change = Delegate.create(this, newSeconde);
    scene.secondeStep.addEventListener("change", ecoutSeconde);
    //Création du bouton Annuler
    initObj.label = "Annuler";
    initObj._x = 310;
    initObj._y = 240;
    initObj._width = 60;
    // Ici on dit que notre onRelease sera la fonction annuler
    //    de notre objet Param, via Delegate.create
    initObj.onRelease = Delegate.create(this, annuler);
    scene.createClassObject(Button, "annuler_btn", 7, initObj);
    initObj.label = "Appliquer";
    initObj._x = 380;
    initObj._width = 60;
    // Ici on dit que notre onRelease sera la fonction annuler
    //    de notre objet Param, via Delegate.create
    initObj.onRelease = Delegate.create(this, appliquer);
    scene.createClassObject(Button, "valider_btn", 8, initObj);
    initObj.label = "OK";
    initObj._x = 240;
    initObj._width = 60;
    // Ici on dit que notre onRelease sera la fonction annuler
    //    de notre objet Param, via Delegate.create
    initObj.onRelease = Delegate.create(this, ok);
    scene.createClassObject(Button, "ok_btn", 9, initObj);
  }

Cette fonction, bien que longue n'est faite que de choses simples, déjà vues auparavant. C'est pourquoi elle ne sera pas détaillé, il s'agit en fait que d'attachMovie avec des initObjet, des 'Delegate.create' et autre subtilité. La seule nouveauté consiste dans la paramétrage des noms des jours et des mois du 'DateChooser'.

  private function refaireDate(Void):Void {
    // On prend la date actuelle
    maDate = new Date();
    // On applique notre différence de secondes
    diffAct = Math.round(diffAct);
    maDate.setTime(maDate.getTime()+diffAct*1000);
    // Mise à jour de l'affichage
    horloge.heure._rotation = (maDate.getHours()/12)*360;
    horloge.minute._rotation = (maDate.getMinutes()/60)*360;
    horloge.seconde._rotation = (maDate.getSeconds()/60)*360;
    scene.heureStep.value = maDate.getHours();
    scene.minuteStep.value = maDate.getMinutes();
    scene.secondeStep.value = maDate.getSeconds();
    if (maDate.getHours() == 0 && maDate.getMinutes() == 0 && maDate.getSeconds() == 0) {
      // On vient de changer de jour, on met à jour le DateChooser
      scene.chxDate.selectedDate = maDate;
    }
  }

Cette fonction est appelée toutes les secondes par un setInterval et dès que l'utilisateur redéfinie une nouvelle heure. Elle commence par remetre la date réelle à la variable 'maDate'. Ensuite, on applique la différence actuelle en secondes à notre date pour récuper le temps que l'utilisateur à de paramétrer en ce moment. Ensuite, par rapport à cette nouvelle date, on change tous les affichages relatifs aux heures, minutes et secondes. De plus, s'il on vient de changer de jour, on applique le changement au 'DateChooser'.

  private function newHeure(evt:Object) {
    // On calcule la différence d'heure entre celle d'avant et la nouvelle
    var diffHeure:Number = evt.target.value-maDate.getHours();
    // On change la différence diffAct;
    diffAct = diffAct+diffHeure*60*60;
  }

Cette fonction, appelée lorsque l'utilisateur change l'heure, calcule d'abord la différence en heures entre l'ancienne date et la nouvelle, puis modifie 'diffAct' suivant cette différence.

  private function newMinute(evt:Object) {
    // On calcule la différence de minutes entre celle d'avant et la nouvelle
    var diffMinute:Number = evt.target.value-maDate.getMinutes();
    // On change la différence diffAct;
    diffAct = diffAct+diffMinute*60;
  }

Idem que la précédente, sauf que c'est pour les minutes.

  private function newSeconde(evt:Object) {
    // On calcule la différence de secondes entre celle d'avant et la nouvelle
    var diffSeconde:Number = evt.target.value-maDate.getSeconds();
    // On change la différence diffAct;
    diffAct = diffAct+diffSeconde;
  }

Idem que la précédente, sauf que c'est pour les secondes.

  private function annuler(Void):Void {
    scene._parent.fermer_btn.onRelease();
  }

Cette fonction, appelée lors du clic sur le bouton annuler, exécute le 'onRelease' du bouton 'fermer_btn' de la fenêtre qui contient l'application, ce qui aura donc pour effet final de fermer l'animation.

  private function appliquer(Void):Void {
    scene._parent._parent._parent.monBureau.setParamDate(diffAct);
  }

Cette fonction, appelée lors du clic sur le bouton appliquer, appelle la fonction 'setParamDate' de l'objet 'monBureau' (objet créé dans 'NyrOS.fla') en passant en paramètres la différence de temps actuellement dans la fenêtre. Ceci aura donc pour effet de changer l'objet 'param' de 'monBureau'.

  private function ok(Void):Void {
    scene._parent._parent._parent.monBureau.setParamDate(diffAct);
    scene._parent.fermer_btn.onRelease();
  }

Cette dernière fonction, appelée lors du clic sur le bouton ok, est en fait une combinaison des 2 dernières : on applique les paramètres et on ferme la fenêtre.

^^ Retour ^^

dessin.fla

Cette animation permet à l'utilisateur de faire des dessins, en traçant des lignes avec la souris. On lui permet de chosir le mode de dessin : trait simple, trait fermé et trait rempli fermé. Il peut aussi choisir la couleur du trait et son épaisseur, la couleur de remplissage et son alpha. On permet aussi à l'utilisateur d'exporter son image en PNG, grace à 'dessin.php'.

var monDessin:Dessin = new Dessin(this);

Comme sur tous les autres fla, une seule ligne qui permet d'appler la classe correspondante, en passant en paramètres la référence de l'endroit où l'appelle.

^^ Retour ^^

Dessin.as

Cette la classe qui permet de créer toute l'interface de dessin et les actions de dessin.

import mx.utils.Delegate;
/*
modeDessin :
0 : Déplacement ou Suppression
1 : Trait normal
2 : Trait fermé
3 : trait fermé rempli
*/
class Dessin {
  private var scene:MovieClip;
  private var fondDessin:MovieClip;
  private var dessin:MovieClip;
  private var outils:MovieClip;
  private var monEcoutMode:Object;
  private var monEcoutEpaiss:Object;
  private var monEcoutAlpha:Object;
  private var monEcoutTrait:Object;
  private var monEcoutRempl:Object;
  private var dessine:Object;
  private var clTrait:String;
  private var clRempl:String;
  private var alpha:Number;
  private var modeDessin:Number = 1;
  private var epaisseur:Number = 1;
  private var nb:Number = 1;
  private static var CLFOND:String = "EEF1FD";
  private static var CLOUTILS:String = "CCCCCC";
  private static var CLDEFTRAIT:String = "000000";
  private static var CLDEFREMPL:String = "A0A0A0";
  private static var DEFALPHA:Number = 75;

On commence par importer la classe 'Delegate' car on en a besoin dans la classe. Ensuite, on crée la classe 'Dessin' et on définit les variables utilisés dans la classe : 'scene' qui est la référence à l'endroit où est appellé la classe, 'fondDessin' est la référence au clip de fond de l'animation, 'dessin' est le clip dans lequel on mettra tous les dessins de l'utilisateur, 'outils' est le clip dans lequel on mettra tous nos éléments de paramètres de dessin, 'monEcoutMode' est l'objet écouteur sur le ComboBox de choix du mode de dessin, 'monEcoutEpaiss' est l'écouteur sur le NumericStepper du choix de l'épaisseur, 'monEcoutAlpha' est l'écouteur sur le NumericStepper du choix de l'alpha de la couleur de remplissage, 'monEcoutTrait' est l'écouteur sur le choix de la couleur du trait, 'monEcoutRempl' est l'écouteur sur le choix de la couleur de remplissage, 'dessine' est l'écouteur sur la souris, 'clTrait' est la couleur actuelle du trait, 'clRempl' est la couleur actuelle de remplissage, 'alpha' est l'alpha actuel de la couleur de remplissage, 'modeDessin' est le code pour savoir le mode de Dessin actuel, 'modeDessinLast' est le mode de Dessin précédemment choisi utilisé lorsque on était en mode déplacement ou suppression, 'epaisseur' est l'épaisseur actuelle et 'nb' est le nombre de clip crée par l'utilisateur.
Ensuite on définit des variables statiques qui sont les paramètres de l'application : 'CLFOND' définit la couleur du fond, 'CLOUTILS' définit la couleur de fond de la palette d'outils, 'CLDEFTRAIT' définit la couleur par défaut des traits, 'CLDEFREMPL' définit la couleur par défaut de la couleur de remplissage et 'DEFALPHA' définit l'alpha de la couleur de remplissage par défaut.

  public function Dessin(scen:MovieClip) {
    scene = scen;
    creerElements();
    // Notre écouteur de dessin
    dessine = new Object();
    Mouse.addListener(dessine);
    // Ici on dit que notre onMouseDown sera la fonction debutDessin
    //    de notre objet Dessin, via Delegate.create
    dessine.onMouseDown = Delegate.create(this, debutDessin);
    // Ici on dit que notre onMouseUp sera la fonction stopDessin
    //    de notre objet Dessin, via Delegate.create
    dessine.onMouseUp = Delegate.create(this, stopDessin);
    var monDessin:Object;
    scene.detruire = function(Void) {
      this.monDessin.detruire();
    };
  }

Le constructeur de la classe, qui commence par définir la variable 'scene' avec la variable passée en paramètre, puis on appelle la fonction 'creerElements' pour créer tous les clips et composants de l'animation. Ensuite on définit l'écouter dessine sur la souris qui réagira aux évenement 'onMouseDown' et 'onMouseUp' en appellant les fonctions de la classe (via 'Delegate.create') 'debutDessin' et 'stopDessin'. Enfin, on crée la fonction 'detruire' à l'endroit où est appellé la classe pour faire un destructeur de la classe, indispensable pour détruire les écouteurs; cette fonction appellera en fait la fonction 'detruire' de la classe 'Dessin' (le 'monDessin' est indispensable pour pouvoir générer le swf, car sinon, on a une erreur, alors que le 'this' fait ici référence à l'endroit d'où est appellé la classe et non à la classe elle-même...)

  private function creerElements(Void):Void {
    // Création du fond de couleur
    scene.createEmptyMovieClip("fondDessin", 1);
    fondDessin = eval(scene+".fondDessin");
    var cl:String = CLFOND;
    with (fondDessin) {
      moveTo(0, 0);
      lineStyle(0, "0x"+cl, 100);
      beginFill("0x"+cl, 100);
      lineTo(550, 0);
      lineTo(550, 400);
      lineTo(0, 400);
      endFill();
    }
    // Création du clip vide pour mettre les dessins
    scene.createEmptyMovieClip("dessin", 2);
    dessin = eval(scene+".dessin");
    // Création du clip outils et dessin du rectangle de couleur
    scene.createEmptyMovieClip("outils", 3);
    outils = eval(scene+".outils");
    outils._x = 550-150;
    outils._y = 10;
    cl = CLOUTILS;
    with (outils) {
      moveTo(0, 0);
      lineStyle(1, "0x000000", 100);
      beginFill("0x"+cl, 100);
      lineTo(140, 0);
      lineTo(140, 190);
      lineTo(0, 190);
      endFill();
    }
    var initObj:Object = new Object();
    // Combobox pour le mode de dessins
    initObj._x = 10;
    initObj._y = 10;
    initObj._width = 120;
    initObj._height = 22;
    outils.attachMovie("ComboBox", "chxModeDessin", 1, initObj);
    outils.chxModeDessin.addItem("Trait normal", 1);
    outils.chxModeDessin.addItem("Trait fermé", 2);
    outils.chxModeDessin.addItem("Trait fermé rempli", 3);
    outils.chxModeDessin.addItem("Déplacement", 4);
    outils.chxModeDessin.addItem("Suppression", 5);
    monEcoutMode = new Object();
    // Ici on dit que notre change sera la fonction chgMode
    //    de notre objet Dessin, via Delegate.create
    monEcoutMode.change = Delegate.create(this, chgMode);
    outils.chxModeDessin.addEventListener("change", monEcoutMode);
    // Taille du trait
    initObj._y += 30;
    outils.createTextField("epais_txt", 2, initObj._x, initObj._y, 100, 22);
    outils.epais_txt.selectable = false;
    outils.epais_txt.text = "Epaisseur :";
    initObj._width = 50;
    initObj._x += 70;
    initObj.value = 1;
    initObj.maximum = 50;
    outils.attachMovie("NumericStepper", "chxEpaisseur", 3, initObj);
    monEcoutEpaiss = new Object();
    // Ici on dit que notre change sera la fonction chgEpais
    //    de notre objet Dessin, via Delegate.create
    monEcoutEpaiss.change = Delegate.create(this, chgEpais);
    outils.chxEpaisseur.addEventListener("change", monEcoutEpaiss);
    // Couleur de trait
    clTrait = CLDEFTRAIT;
    initObj._y += 30;
    initObj._x -= 70;
    outils.createTextField("trait_txt", 4, initObj._x, initObj._y, 100, 22);
    outils.trait_txt.selectable = false;
    outils.trait_txt.text = "Trait :";
    // Création du bouton pour choisir la couleur
    outils.createEmptyMovieClip("chxTrait_mc", 5);
    outils.chxTrait_mc._x = initObj._x+75;
    outils.chxTrait_mc._y = initObj._y;
    dessineCarrTrait();
    // Ecouteur sur le choix de la couleur
    monEcoutTrait = new Object();
    // Ici on dit que notre choix sera la fonction chxClTrait
    //    de notre objet Dessin, via Delegate.create
    monEcoutTrait.choix = Delegate.create(this, chxClTrait);
    // Ici on dit que notre onRelease sera la fonction affChxTrait
    //    de notre objet Dessin, via Delegate.create
    outils.chxTrait_mc.onRelease = Delegate.create(this, affChxTrait);
    // Couleur de remplissage
    clRempl = CLDEFREMPL;
    initObj._y += 30;
    outils.createTextField("rempl_txt", 6, initObj._x, initObj._y, 100, 22);
    outils.rempl_txt.selectable = false;
    outils.rempl_txt.text = "Remplissage :";
    // Création du bouton pour choisir la couleur
    outils.createEmptyMovieClip("chxRempl_mc", 7);
    outils.chxRempl_mc._x = initObj._x+75;
    outils.chxRempl_mc._y = initObj._y;
    dessineCarrRempl();
    // Ecouteur sur le choxi de la couleur
    monEcoutRempl = new Object();
    // Ici on dit que notre choix sera la fonction chxClTrait
    //    de notre objet Dessin, via Delegate.create
    monEcoutRempl.choix = Delegate.create(this, chxClRempl);
    // Ici on dit que notre onRelease sera la fonction affChxTrait
    //    de notre objet Dessin, via Delegate.create
    outils.chxRempl_mc.onRelease = Delegate.create(this, affChxRempl);
    // Alpha du Remplissage
    initObj._y += 30;
    outils.createTextField("alpha_txt", 8, initObj._x, initObj._y, 100, 22);
    outils.alpha_txt.selectable = false;
    outils.alpha_txt.text = "Opacité :";
    initObj._width = 50;
    initObj._x += 70;
    alpha = DEFALPHA;
    initObj.maximum = 100;
    outils.attachMovie("NumericStepper", "chxAlpha", 9, initObj);
    // Si je passe le value dans l'init, ca ne passe pas...
    outils.chxAlpha.value = DEFALPHA;
    monEcoutAlpha = new Object();
    // Ici on dit que notre change sera la fonction chgEpais
    //    de notre objet Dessin, via Delegate.create
    monEcoutAlpha.change = Delegate.create(this, chgAlpha);
    outils.chxAlpha.addEventListener("change", monEcoutAlpha);
    // Bouton exporter en PNG
    initObj.label = "Exporter en PNG";
    initObj.value = undefined;
    initObj._x -= 70;
    initObj._y += 30;
    initObj._width = 120;
    // Ici on dit que notre onRelease sera la fonction annuler
    //    de notre objet Param, via Delegate.create
    initObj.onRelease = Delegate.create(this, exporter);
    outils.attachMovie("Button", "exporter_btn", 10, initObj);
    // Boutons pour cacher la palette d'outils
    initObj._x = 550-20;
    initObj._y = 5;
    initObj._width = 20;
    initObj._height = 20;
    scene.attachMovie("pushButton", "cache_btn", 4, initObj);
    // Ici on dit que notre onRelease sera la fonction affChxTrait
    //    de notre objet Dessin, via Delegate.create
    scene.cache_btn.onRelease = Delegate.create(this, chgOutils);
  }

Cette fonction, appellée dans le constructeur, sert simplement à créer tous les clips, composants et écouteurs dont l'animation se sert. Comme toutes les fonctions vues ici ont déjà été détaillées dans d'autres classes, je ne vais pas le refaire.

  private function debutDessin(Void):Void {
    var xmouse:Number = scene._xmouse;
    var ymouse:Number = scene._ymouse;
    if (modeDessin>0 && testoutils() && testzone()) {
      dessin.createEmptyMovieClip("dessin"+nb, nb);
      var clipAct:MovieClip = eval(dessin+".dessin"+nb);
      clipAct.modeDessin = modeDessin;
      clipAct.epaisseur = epaisseur;
      clipAct.clTrait = clTrait;
      clipAct.clRempl = clRempl;
      clipAct.alpha = alpha;
      clipAct.remplir = new Array();
      clipAct.remplir.push(Array(xmouse, ymouse));
      clipAct.moveTo(xmouse, ymouse);
      clipAct.lineStyle(clipAct.epaisseur, parseInt(clTrait, 16), 100);
      // Ici on dit que notre onMouseDown sera la fonction dessinAct
      //    de notre objet Dessin, via Delegate.create
      dessine.onMouseMove = Delegate.create(this, dessinAct);
    }
  }

Cette fonction est appellée lorsqu'on clique sur la souris, c'est à dire au commencement d'un dessin. On commence par vérifier que l'on peut véritablement commencé le dessins en s'assurant que le mode de Dessin actuel est bien un mode de dessin et non déplacement ou suppression, que la souris ne se trouve pas sur la palette d'outils (via 'testoutils') et que la souris se trouve bien dans le cadre de l'animation (via 'testzone').
Si c'est le cas, alors on crée un nouveau clip vide dans le clip 'dessin' et on lui attribue tous les paramètres de dessin actuels : le mode de dessin, l'épaisseur du trait, la couleur du trait, la couleur de remplissage et l'alpha. Ensuite on définit un nouveau tableau 'remplir' dans ce tableau qui stockera les coordonnées de tous les points utilisées pour dessiner le clip, et on y met la 1ère valeur qui ets la position actuelle de la souris.
Enfin, on déplace la position de dessin du clip que l'on vietn de crée à alposition actuelle de la souris, on applique le style de trait actuel et on ajoute un événement à écouté à dessine, 'onMouseMove' qui sera la fonction 'dessinAct' qui dessinera le contour en suivant la souris lors de son déplacement.

  private function dessinAct(Void):Void {
    if (testzone()) {
      var xmouse:Number = scene._xmouse;
      var ymouse:Number = scene._ymouse;
      var clipAct:MovieClip = eval(dessin+".dessin"+nb);
      clipAct.remplir.push(Array(xmouse, ymouse));
      clipAct.lineTo(xmouse, ymouse);
    }
  }

Cette fonction est appelé lorsqu'on a commencé un dessin (et qu'on ne la pas terminé), lorsque la souris bouge. Elle commence par vérifier si on est bien dans sur le fondpour dessiner et, si c'est le cas, ajoute dans le tableau du clip en cours de dessin un nouveau point, puis dessine le nouveau segment vers ce nouveau point.

  private function stopDessin(Void):Void {
    var clipAct:MovieClip = eval(dessin+".dessin"+nb);
    if (modeDessin == 2) {
      clipAct.lineTo(clipAct.remplir[0][0], clipAct.remplir[0][1]);
    } else if (modeDessin == 3) {
      clipAct.moveTo(clipAct.remplir[0][0], clipAct.remplir[0][1]);
      clipAct.beginFill(parseInt(clipAct.clRempl, 16), clipAct.alpha);
      for (var i = 1; i<clipAct.remplir.length; i++) {
        clipAct.lineTo(clipAct.remplir[i][0], clipAct.remplir[i][1]);
      }
      clipAct.lineTo(clipAct.remplir[0][0], clipAct.remplir[0][1]);
      clipAct.endFill();
    }
    if (modeDessin>0) {
      nb++;
    }
    dessine.onMouseMove = null;
  }

Cette fonction, appelée lorsque le clic est relâché, arrête le dessin, et suivant le mode de dessin actuel, ferme le contoure et/ou rempli avec les paramètres définis dans le clip. Puis on incrément la variable nb pour créer le prochain clip sur une autre profondeur, avec un autre nom d'instance. Enfin, on enlève la fonction lors du déplacement de la souris.

  private function chgMode(Evt:Object):Void {
    var clipAct:MovieClip;
    if (Evt.target.value == 4) {
      // Mode Déplacement
      modeDessin = 0;
      for (var i = 1; i<nb; i++) {
        clipAct = eval(dessin+".dessin"+i);
        clipAct.onPress = function() {
          startDrag(this);
        };
        clipAct.onRelease = function() {
          stopDrag();
        };
      }
    } else if (Evt.target.value == 5) {
      // Mode Suppression
      modeDessin = 0;
      for (var i = 1; i<nb; i++) {
        clipAct = eval(dessin+".dessin"+i);
        clipAct.onRelease = function() {
          removeMovieClip(this);
        };
      }
    } else {
      // Mode de dessin réel
      if (modeDessin == 0) {
        // On avait des actions sur nos clips, donc on les enlèves
        for (var i = 1; i<nb; i++) {
          clipAct = eval(dessin+".dessin"+i);
          clipAct.onPress = null;
          clipAct.onRelease = null;
        }
      }
      // On met notre mode
      modeDessin = Evt.target.value;
    }
  }

Cette fonction, appellée lors du changement de mode de dessin par le comboBox, vérifie d'abord dans quel mode on se place. S'il s'agit du mode déplacement, on ajoute à tous les clips de dessin déjà créés le 'onPress' et le 'onRelease' qui permettent de les déplacer. S'il s'agit du mode de suppression, on ajoute le 'onRelease' qui fera simplement la suppression du clip. Enfin s'il s'agit d'un mode de dessin véritable et que le précédent mode était déplacement ou suppression, alors on supprime les 'onPress' et 'onRelease' de tous les clips de dessin.
S'il s'agit d'un mode de dessin véritable, on attribue la valeur du comboBox à 'modeDessin', sinon (déplacement et suppression) on attribue 0 qui n'est pas un vrai mode de dessin, mais un mode d'édition en fait.

  private function chgEpais(Evt:Object):Void {
    epaisseur = Evt.target.value;
  }

Simple fonction qui attribue la valeur choisie par l'utilisateur à l'épaisseur du trait actuel : 'epaisseur'.

  private function chgAlpha(Evt:Object):Void {
    alpha = Evt.target.value;
  }

Simple fonction qui attribue la valeur choisie par l'utilisateur à l'alpha de la couleur de remplissage actuel : 'alpha'.

  private function chgOutils(Void):Void {
    outils._visible = !outils._visible;
  }

Simple fonction qui inverse la propriété '_visible' de la palette d'outils : 'outils'.

  private function dessineCarrTrait(Void):Void {
    var cl:String = "0x"+clTrait;
    with (outils.chxTrait_mc) {
      moveTo(0, 0);
      lineStyle(1, 0x000000, 100);
      beginFill(cl, 100);
      lineTo(20, 0);
      lineTo(20, 20);
      lineTo(0, 20);
      endFill();
    }
  }

Cette fonction dessine le carré de séléction de couleur du trait avec la couleur de trait sélectionnée.

  private function dessineCarrRempl(Void):Void {
    var cl:String = "0x"+clRempl;
    with (outils.chxRempl_mc) {
      moveTo(0, 0);
      lineStyle(1, 0x000000, 100);
      beginFill(cl, 100);
      lineTo(20, 0);
      lineTo(20, 20);
      lineTo(0, 20);
      endFill();
    }
  }

Cette fonction dessine le carré de séléction de couleur de remplissage avec la couleur de remplissage sélectionnée.

  private function affChxTrait(Void) {
    outils.createEmptyMovieClip("choix", 100);
    var choix:MovieClip = eval(outils+".choix");
    var maCouleur:ChxCouleur = new ChxCouleur(choix, outils.chxTrait_mc._x-200, outils.chxTrait_mc._y+25, clTrait);
    maCouleur.addEventListener("choix", monEcoutTrait);
  }
  private function chxClTrait(evt:Object) {
    clTrait = evt.couleur;
    dessineCarrTrait();
  }

Ces 2 fonctions sont utilisées pour afficher le colorPicker de la couleur du trait et crée l'écouteur qui va avec pour la 1ère fonction, récupérer et appliquer la nouvelle couleur pour la seconde.

  private function affChxRempl(Void) {
    outils.createEmptyMovieClip("choix", 100);
    var choix:MovieClip = eval(outils+".choix");
    var maCouleur:ChxCouleur = new ChxCouleur(choix, outils.chxRempl_mc._x-200, outils.chxRempl_mc._y+25, clRempl);
    maCouleur.addEventListener("choix", monEcoutRempl);
  }
  private function chxClRempl(evt:Object) {
    clRempl = evt.couleur;
    dessineCarrRempl();
  }

Ces 2 fonctions sont utilisées pour afficher le colorPicker de la couleur de remplissage et crée l'écouteur qui va avec pour la 1ère fonction, récupérer et appliquer la nouvelle couleur pour la seconde.

  private function testoutils(Void):Boolean {
    var retour:Boolean = true;
    if (outils._xmouse>0 && outils._xmouse<outils._width && outils._ymouse>0 && outils._ymouse<outils._height && outils._visible) {
      retour = false;
    }
    return retour;
  }

Cette fonction teste si la souris se trouve au dessus de la palette d'outils. On test avec les coordonnées de la souris et non un hitTest car, de cette façon, on est sûr que la souris est (ou n'est pas) au dessus de la palette, car si elle est au dessus d'un composant, le test n'était bon.
On retourne 'true' ou 'false' suivant le cas.

  private function testzone(Void):Boolean {
    var retour:Boolean = false;
    if (fondDessin._xmouse>0 && fondDessin._xmouse<fondDessin._width && fondDessin._ymouse>0 && fondDessin._ymouse<fondDessin._height) {
      retour = true;
    }
    return retour;
  }

Idem que précédente, sauf que l'on teste pour le fond coloré.

  private function exporter() {
    if (nb>1) {
      var dessins:Array = new Array();
      var clipAct:MovieClip;
      for (var i = 1; i<nb; i++) {
        clipAct = eval(dessin+".dessin"+i);
        if (clipAct != undefined) {
          // Le clip n'a pas été supprimé, on l'ajoute à notre chaine
          var dessinTab:Array = new Array();
          dessinTab.push(clipAct.modeDessin);
          dessinTab.push(clipAct.epaisseur);
          dessinTab.push(clipAct.clTrait);
          dessinTab.push(clipAct.clRempl);
          dessinTab.push(clipAct.alpha);
          dessinTab.push(clipAct.remplir.join("-"));
          dessins.push(dessinTab.join("_"));
        }
      }
      if (dessins.length>0) {
        getURL("javascript:envForm('"+CLFOND+"','"+dessins.join("@")+"');");
      }
    }
  }

Cette fonction, appelé lors du clic sur le bouton 'exporter en PNG', commence par vérifier si l'utilisateur a déjà dessinée quelque chose. Si c'est le cas, on parcourt tous les clips dessinées, et on construit une variable pour envoyer au javascript de l'html d'où est appellée l'animation.
En fait, on ajoute chaque variable correspondant à chaque clip créés dans un tableau 'dessins'. Dans le contenu de ce tableau, on y met tous les paramètres du clip, séparés par un '_'. Pour faire cela, on commence par mettre tous les paramètres dans un tableau 'dessinTab', puis on transforme en chaîne séparé par '-' en utilisant la méthode 'join' du tableau. A noter aussi qu'on insére les coordonnées des points avec un 'join' du tableau 'remplir' du clip, mais séparé par '-'.
Une fois le tableau 'dessins' remplis avec tous les paramètres de tous les clips, on appelle avec 'getURL' la fonction javascript 'envForm' (décrite à peine plus loin) 2 variables : la 1ère est la couleur de fond de l'animation 'CLFOND' et la 2nde, le tableau 'dessins' que l'on vient de construire, transformé en chaîne avec 'join', séparé par des '@'.

  public function detruire(Void):Void {
    dessine.onMouseDown = undefined;
    dessine.onMouseUp = undefined;
    monEcoutMode = undefined;
    monEcoutEpaiss = undefined;
    monEcoutAlpha = undefined;
    monEcoutTrait = undefined;
    monEcoutRempl = undefined;
  }

Cette fonction, est en fait la fonction destructrice de la classe. Elle permet, lors de la fermeture de la fenêtre, de supprimer tous les écouteurs de la classe.

^^ Retour ^^

ChxCouleur.as

CF ChxCouleur.as, qui est la même classe.

^^ Retour ^^

envDessin.js

Ce fichier est intégré dans l'html qui appelle 'NyrOS.swf', et contient 2 fonctions qui permettent l'envoi des données vers un iframe.

function creerForm() {
 document.write('<div style="position:absolute; top:-600px;">');
 document.write('<form name="enregImage" action="dessin.php" method="POST" target="rien">');
 document.write('<input type="hidden" name="clfond" value="de" \>');
 document.write('<input type="hidden" name="dessins" value="dede" \>');
 document.write('</form>');
 document.write('<iframe name="rien"></iframe>');
 document.write('</div>');
}

Cette fonction, appellée dans le body de la page, sert à créer en javascript le formulaire dans lequel on mettra les données et l'iframe dans laquelle on enverra le formulaire, que l'on place plus haut que le haut de la page, pour la rendre invisible.

function envForm(clfond,dessins) {
 form = document.enregImage
 form.clfond.value = clfond;
 form.dessins.value = dessins;
 form.submit();
}

Cette fonction, appellée directement par l'animation flash, applique simplement les variables qui lui sont passées en paramètres aux champs du formulaire et envoi le formulaire.

^^ Retour ^^

dessin.php

Ce fichier php crée l'image png selon les paramètres qui lui sont envoyés par le formulaire vu juste plus haut et renvoi l'image, en proposant le téléchargement à l'utilisateur.

$nomImage = "image.png";
$dirup = "imageSWF/";

On définit l'endroit où l'on enregistre l'image sur le serveur.

function hexa2dec($coul) {
 $retour[] = base_convert(substr($coul,0,2),16, 10);
 $retour[] = base_convert(substr($coul,2,2),16, 10);
 $retour[] = base_convert(substr($coul,4,2),16, 10);
 return $retour;
}

Fonction qui retroune les valeurs Rouge, Verte et Bleue en décimal dans un tableau, d'une couleur Hexadécimal.

// Création de l'image
$image=ImageCreatetruecolor(550,400);

On commence par crée notre ressource '$image' des dimensions identique à l'animation flash.

// Création du fond
$clfond = hexa2dec($_POST['clfond']);
$fond=ImageColorAllocate($image,$clfond[0],$clfond[1],$clfond[2]);
ImagefilledRectangle($image,0,0,550,400,$fond);

On dessine un rectangle de la couleur de fond passé en paramètres qui prend toute la surface de l'image.

// Dessin de tous les polygones
$dessins = explode("@",$_POST['dessins']);
foreach($dessins as $dessin) {
 $dessin = explode("_",$dessin);
 $modeDessin = $dessin[0];
 $epaisseur = $dessin[1];
 $clTrait = hexa2dec($dessin[2]);
 $clRempl = hexa2dec($dessin[3]);
 $alpha = $dessin[4];
 $points1 = explode("-",$dessin[5]);
 $points = Array();
 // On remet notre tableau de points comme le veux PHP
 foreach($points1 as $point) {
  $point = explode(",",$point);
  $points[] = $point[0];
  $points[] = $point[1];
 }
 $tout = count($points)/2;
 if ($modeDessin == 3) {
  // Trait fermé rempli, on commence par faire le remplissage
  $cl = ImageColorAllocateAlpha($image,$clRempl[0],$clRempl[1],$clRempl[2],$alpha);
  imagefilledpolygon($image,$points,$tout,$cl);
 }
 $cl = ImageColorAllocate($image,$clTrait[0],$clTrait[1],$clTrait[2]);
 imagesetthickness($image,$epaisseur);
 if ($modeDessin == 1) {
  // Le contour n'est pas fermé, donc on le fait ligne par ligne
  for($i=2;$i<($tout-1)*2;$i+=2) {
   imageline ($image,$points[$i-2],$points[$i-1],$points[$i],$points[$i+1],$cl);
  }
 } else {
  imagepolygon($image,$points,$tout,$cl);
 }
}

Cette partie est la plus important du script PHP puisque c'est là que l'on dessine tous les dessins. On commence par transformer la valeur reçue en paramètre en un tableau 'dessins'. Ensuite on parcourt ce tableau, et pour chaque tableau, on crée un second tableau qui contient tous les paramètres du dessin qu'on est en train de parcourir; on fait encore la même chose pour récupérer la liste de tous les points. On crée un second tableau de points, pour pouvoir le passer directement aux fonctions PHP (2 entrées du tableau = 1 point X,Y).
On compte le nombre de points que l'on a et on attribue cette valeur ) '$tout'. Si le mode de dessin du dessin que l'on parcourt, alors on dessine un polygone avec la couleur de remplissage du dessin et son alpha.
Ensuite, on change l'épaisseur du trait avec 'imagesetthickness' pour mettre celle du dessin en cours, pour ensuite dessiner le contour du polygone, en tenant compte de s'il est fermé ou non (d'après le mode de dessin).

Imagepng($image,$dirup.$nomImage);
Imagedestroy($image);

On enregistre l'image à l'endroit indiqué au début du script et on détruit l'objet '$image' sur le serveur.

header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Content-type: application/force-download");
header('Pragma: public');
header("Pragma: no-cache");// HTTP/1.0
header('Last-Modified: '.gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate'); // HTTP/1.1
header('Cache-Control: pre-check=0, post-check=0, max-age=0'); // HTTP/1.1
header('Content-Transfer-Encoding: none');
header('Content-Type: application/octet-stream; name="' . $nomImage . '"');
header('Content-Type: application/octetstream; name="' . $nomImage . '"');
header('Content-Type: application/download; name="' . $nomImage . '"');
header('Content-Disposition: attachment; filename="'.$nomImage.'"');
header("Content-Description: File Transfer");
header("Content-length: ".filesize($dirup.$nomImage)."bytes");

Cette partie de 'header' est utilisé pour forcé le téléchargement de l'image, pour tous les navigateurs.

@readfile($dirup.$nomImage);

On lit notre fichier image que l'on vient de créer pour l'envoyer et que l'utilisateur puisse le télécharger.

^^ Retour ^^

sokoban.fla

Cette animation permet à l'utilisateur de jouer au Sokoban, le célèbre jeu de réflexion. Comme toutes les autres, elle n'a qu'une seule image clée avec une seule ligne code.
Dans sa bibliothèque, il y a tous les éléments qui seront utilisés pour le jeu : 'boule' avec 2 images-clés (une lorsque la boule n'est pas placée et l'autre non), 'heros', 'mur', 'bmc' pour le bouton lors de la demande du pseudo, 'sol' et 'trou'.
La seule ligne de code de l'animation qui permet d'appeller la class 'Sokoban' :

var monSokoban:Sokoban = new Sokoban(this, "niveaux.xml");

On passe en paramètre la référence d'où l'on appelle la classe et l'url du fichier XML des niveaux.

^^ Retour ^^

niveaux.xml

Ce fichier XML est extrait de ce site web : http://www.sourcecode.se/sokoban/levels.php. Il contient tous les niveaux du jeu. Au besoin, on peut changer ce fichier xml par un autre du site.
Il se présente sous cette forme :

<LevelCollection Copyright="Thinking Rabbit" MaxWidth="20" MaxHeight="17">
 <Level Id="1" Width="19" Height="11">
  <L>    #####</L>
  <L>    #   #</L>
  <L>    #$  #</L>
  <L>  ###  $##</L>
  <L>  #  $ $ #</L>
  <L>### # ## #   ######</L>
  <L>#   # ## #####  ..#</L>
  <L># $  $          ..#</L>
  <L>##### ### #@##  ..#</L>
  <L>    #     #########</L>
  <L>    #######</L>
 </Level>
 <Level Id="2" Width="14" Height="10">
  <L>############</L>
  <L>#..  #     ###</L>
  <L>#..  # $  $  #</L>
  <L>#..  #$####  #</L>
  <L>#..    @ ##  #</L>
  <L>#..  # #  $ ##</L>
  <L>###### ##$ $ #</L>
  <L>  # $  $ $ $ #</L>
  <L>  #    #     #</L>
  <L>  ############</L>
 </Level>
</LevelCollection>

Pour chaque niveau, le xml précise son nom 'Id', sa largeur 'Width' et sa hauteur 'Height'. Ensuite, on a les lignes 1 par 1, avec chaque caractère qui représente :
'#' pour un mur
' ' pour un sol ou un espace vide
'.' pour un trou
'$' pour une boule sur un sol
'*' pour une boule sur un trou
'@' pour un Héros
'+' pour un Héros sur un trou

^^ Retour ^^

Sokoban.as

Cette classe est celle qui crée le jeu et toutes les actions qui lui sont attribuées. Voici la création de la classe :

import mx.utils.Delegate;
class Sokoban {
  private var scene:MovieClip;
  private var mon_fmt:TextFormat;
  private var pseudo:String;
  private var plateau:MovieClip;
  private var mesNiveaux:XML;
  private var niveau:Number;
  private var niveaux:String;
  private var nbBoules:Number;
  private var nbBoulesMises:Number;
  private var nbCoups:Number;
  private var nbCoupsTot:Number;
  private var nomNiveau:String;
  private var tabNiveau:Array;
  private var tabBoules:Array;
  private var posHerosX:Number;
  private var posHerosY:Number;
  private var objAnnule:Object;
  private var enregistrement:Number;
  private var monEnvoi:LoadVars;
  // Déclaration de mes variables de classe pour paramétrer mon jeu
  private static var DEBUTX:Number = 20;
  private static var DEBUTY:Number = 50;
  private static var TXTX:Number = 5;
  private static var TXTY:Number = -45;
  private static var TXTLARG:Number = 450;
  private static var TXTHAUT:Number = 40;
  private static var TAILLE:Number = 20;
  private static var PROFHEROS:Number = 5000;
  private static var NOMSHARE:String = "sokonyro";
  private static var CLFOND:String = "000000";

On importe déjà la classe 'Delegate' car on en servira dans la classe, puis on crée notre classe et on déclare toutes les variables de la classe : 'scene' est la référence à l'endroit où est appellée la classe, 'mon_fmt' est le format de texte à appliqué au texte de saisie du pseudo, 'plateau' est la référence au clip où l'on crée tous les éléments du jeu, 'mesNiveaux' est l'objet dans lequel on charge les niveaux, 'niveau' définit le niveau actuel du joueur, 'niveaux' définit l'url du fichier xml des niveaux, 'nbBoules' définit le nombre de boules dans le niveau, 'nbBoulesMises' définit le nombre de boules placées dans le niveau, 'nbCoups' définit le nombre de coup du niveau, 'nbCoupsTot' définit le nombre de coup depuis le niveau 1, 'nomNiveau' définit le nom du niveau actuel, 'tabNiveau' définit le tableau du jeu actuel, 'tabBoules' définit le tableau qui sert à identifier les références au clips des boules, 'posHerosX' définit la position en X dans le tableau du héros, 'posHerosY' définit la position en Y dans le tableau du héros, 'objAnnule' est l'objet utilisé pour enregistrer le dernier déplacement de boule (et ensuite l'annuler), 'enregistrement' pour connaître le nombre d'enregistrement restant du niveau et 'monEnvoi' est le LoadVars qui permettra l'enregisrement du score.
Ensuite, on définit les variables de paramétrages de l'animation : 'DEBUTX' est l'endroit en X où l'on commence à placer les éléments du jeu, 'DEBUTY' est l'endroit en Y où l'on commence à placer les éléments du jeu, 'TXTX' et TXTY' est la position en X,Y où l'on place le texte d'affichage, 'TXTLARG' et 'TXTHAUT' sont les largeurs et hauteurs du champ texte, 'TAILLE' est la taille des différents éléments du jeu, 'PROFHEROS' est la profondeur du héros dans l'animation, 'NOMSHARE' est le nom du ShareObject et 'CLFOND' est la couleur de fond de l'animation.

  public function Sokoban(scen:MovieClip, niveau:String) {
    scene = scen;
    niveaux = niveau;
    // Création du fond
    scene.createEmptyMovieClip("fond_mc", -10);
    scene.fond_mc._x = 0;
    scene.fond_mc._y = 0;
    scene.fond_mc.moveTo(0, 0);
    scene.fond_mc.lineStyle(0, "0x"+CLFOND, 100);
    scene.fond_mc.beginFill("0x"+CLFOND, 100);
    scene.fond_mc.lineTo(550, 0);
    scene.fond_mc.lineTo(550, 400);
    scene.fond_mc.lineTo(0, 400);
    scene.fond_mc.endFill();
    scene.createEmptyMovieClip("debut", -9);
    // Création du champ texte d'info
    scene.debut.createTextField("info_txt", 1, 70, 70, 410, 45);
    scene.debut.info_txt.text = "Entrez votre Pseudo :";
    scene.debut.info_txt.selectable = false;
    mon_fmt = new TextFormat();
    mon_fmt.font = "Verdana";
    mon_fmt.size = 35;
    mon_fmt.color = 0xFFCC33;
    mon_fmt.align = "center";
    scene.debut.info_txt.setTextFormat(mon_fmt);
    // Création du champ texte pour le pseudo
    scene.debut.createTextField("pseudo_txt", 2, 70, 140, 410, 45);
    scene.debut.pseudo_txt.type = "input";
    scene.debut.pseudo_txt.background = 0xDDDDDD;
    changePseud();
    scene.debut.pseudo_txt.onChanged = Delegate.create(this, changePseud);
    var initObj:Object = new Object();
    initObj._x = 350;
    initObj._y = 230;
    initObj.onRelease = Delegate.create(this, pressBouton);
    scene.debut.attachMovie("bmc", "bouton_btn", -7, initObj);
  }

La fonction constructeur de la classe, qui applique les paramètres passés en paramètres aux variables de la classe et qui met en place le 1er écran de demande de pseudo. Il s'agit ici que de choses déjà vues dans d'autres classes, donc je ne vais pas le détaillé.

  private function changePseud(Void):Void {
    scene.debut.pseudo_txt.setTextFormat(mon_fmt);
  }

Cette fonction est utilisé pour réaffecter le style au texte de saisie du pseudo, à chaque fois que l'on change le texte, ceci car sinon le texte n'a pas le format que l'on veut, il reprend du noir.

  private function pressBouton(Void):Void {
    if (scene.debut.pseudo_txt.text.length>0) {
      pseudo = scene.debut.pseudo_txt.text;
      // On efface les éléments
      scene.debut._visible = false;
      // On lance le jeu
      jeu();
    }
  }

Cette fonction, appellée lors du clic sur le bouton du 1er écran de saisi du pseudo via 'Delegate.create', commence par vérifier que le pseudo est bien rentré, puis, si c'est le cas, attribue la variable 'pseudo' avec ce qui est entré dans le champ texte, efface l'écran d'accueil (le 'removeMovieClip' ne marche pas ici...) et on lance le jeu en appellant la fonction 'jeu'.

  private function jeu(Void):Void {
    mesNiveaux = new XML();
    mesNiveaux.ignoreWhite = true;
    niveau = 1;
    // Ici on dit que notre onload sera la fonction chargementNiveaux
    //    de note objet Jeu, via Delegate.create
    mesNiveaux.onLoad = Delegate.create(this, chargementNiveaux);
    mesNiveaux.load(niveaux);
    creerPlateau();
    nbCoupsTot = 0;
    var monEcouteur:Object = monEcouteur=new Object();
    Key.addListener(monEcouteur);
    // Ici on dit que la fonction bouge (appelé lors de la pression d'une touche)
    //    sera bouge, via Delegate.create
    monEcouteur.onKeyDown = Delegate.create(this, bouge);
    // On place monEcouteur sur la scene pour le détruire après
    scene.monEcouteur = monEcouteur;
    // Création de la fonction de destruction
    scene.detruire = function(Void) {
      Key.removeListener(monEcouteur);
    };
  }

Cette fonction crée le jeu en lui même, en commençant par charger le xml des niveaux, le 'onLoad' se faisant dans la fonction 'chargementNiveaux', on crée le plateau en appellant la fonction 'creerPlateau', on initialise le nombre de coups total et on crée l'écouteur sur le clavier et la fonction 'detruire', qui sera notre destructeur.

  private function chargementNiveaux(succes:Boolean):Void {
    if (succes) {
      constructionJeu();
    } else {
      trace("Chargement échoué");
    }
  }

Cette fonction, appellée lorsque le fichier xml des niveaux a fini de charger, appelle juste la fonction 'constructionJeu' en cas de succès.

  private function constructionJeu(Void):Void {
    // Initialisation des variable du niveau
    nbBoules = 0;
    nbBoulesMises = 0;
    nbCoups = 0;
    enregistrement = 1;
    objAnnule = undefined;
    // Ici, on met dans un tableau toutes les données du jeu
    //    Et on affiche nos éléments sur la scène
    var monNiveau:XMLNode = mesNiveaux.firstChild.childNodes[niveau-1];
    nomNiveau = monNiveau.attributes.Id;
    var hauteur:Number = monNiveau.attributes.Height;
    var largeur:Number = monNiveau.attributes.Width;
    tabNiveau = new Array(hauteur);
    tabBoules = new Array(hauteur);
    var ligne:String;
    var cellule:String;
    var ident:Number;
    var initObj:Object = new Object();
    var debut:Boolean;
    var aff:String;

Cette fonction (découpée en partie pour plus de lisibilité) sert à crée le niveau actuel. Ici, on commence par initialiser nos variables spécifiques au niveau (les boules, les coups, le nombre d'enregistrement, l'objet d'annulation, et les différents tableaux).

    for (var i = 0; i<hauteur; i++) {
      tabNiveau[i] = new Array(largeur);
      tabBoules[i] = new Array(largeur);
      ligne = monNiveau.childNodes[i].firstChild.nodeValue;
      debut = false;
      for (var j = 0; j<largeur; j++) {
        cellule = ligne.charAt(j);
        tabNiveau[i][j] = cellule;
        aff = "";
        initObj._x = TAILLE*j;
        initObj._y = TAILLE*i;
        if (cellule == "#") {
          // mur
          aff = "mur";
          debut = true;
        } else if (cellule == " " && debut) {
          // sol
          aff = "sol";
        } else if (cellule == "$") {
          // Boule
          nbBoules++;
          aff = "sol";
          plateau.attachMovie("boule", "boule"+nbBoules, PROFHEROS+nbBoules, initObj);
          tabBoules[i][j] = eval(plateau+".boule"+nbBoules);
        } else if (cellule == "@") {
          // Heros
          aff = "sol";
          plateau.attachMovie("heros", "heros_mc", PROFHEROS, initObj);
          posHerosX = j;
          posHerosY = i;
        } else if (cellule == ".") {
          // trou
          aff = "trou";
        } else if (cellule == "*") {
          // Boule placée
          nbBoules++;
          aff = "trou";
          plateau.attachMovie("boule", "boule"+nbBoules, PROFHEROS+nbBoules, initObj);
          tabBoules[i][j] = plateau.eval("boule"+nbBoules);
          eval("boule"+nbBoules).gotoAndStop(2);
        } else if (cellule == "+") {
          // Héros sur trou
          aff = "trou";
          plateau.attachMovie("heros", "heros_mc", PROFHEROS, initObj);
          posHerosX = j;
          posHerosY = i;
        }
        if (aff != "") {
          ident = (100*i+j);
          plateau.attachMovie(aff, "obj"+ident, ident, initObj);
        }
      }
    }
    affichage();
  }

Ensuite, on fait une boucle sur toutes les lignes du niveau, puis sur tous les caractère de chaque ligne. Ensuite, suivant le caractère, on crée les clips qui conviennent, suivant la codification donnée dans le fichier 'niveaux.xml', tout en créant les tableaux du jeu et des boules (ce tableau à 2 entrées x y contient les références de toutes les boules), la position du héros. Ensuite, on initialise l'affichage du texte.

  private function affichage(Void):Void {
    var affiche:String = nomNiveau+" ("+niveau+") - Boules : "+nbBoulesMises+"/"+nbBoules;
    affiche += "\nCoups : "+nbCoups+" - Coups Totals : "+nbCoupsTot;
    plateau.affichage_txt.text = affiche;
  }

Cette fonction sert à mettre à jour l'affichage, on change juste le contenu du 'text' du champ texte que l'on a crée au début.

  private function creerPlateau(Void):Void {
    var tempx:Number = plateau._x;
    var tempy:Number = plateau._y;
    if (tempx == undefined) {
      tempx = DEBUTX;
    }
    if (tempy == undefined) {
      tempy = DEBUTY;
    }
    scene.createEmptyMovieClip("plateau", 1);
    plateau = eval(scene+".plateau");
    plateau._x = tempx;
    plateau._y = tempy;
    plateau.onPress = function() {
      startDrag(this);
    };
    plateau.onRelease = function() {
      stopDrag();
    };
    plateau.createTextField("affichage_txt", PROFHEROS-1, TXTX, TXTY, TXTLARG, TXTHAUT);
    plateau.affichage_txt.textColor = "0xFFFFFF";
  }

Cette fonction sert à crée le clip vide 'plateau', le placé comme paramétré dans les variables 'static', en créant les fonction de déplacement sur le plateau, et crée le champ texte pour l'affichage.

  private function bouge(Void):Void {
    var touche:Number = Key.getCode();
    var bgX:Number = 0;
    var bgY:Number = 0;
    if (touche == 69) {
      // touche E
      enregistre();
    } else if (touche == 83) {
      // touche S
      sauvegarde();
    } else if (touche == 82) {
      // touche r
      recov();
    } else if (touche == 32) {
      // Barre d'espace
      annule();
    } else if (touche == 37) {
      // Gauche
      bgX = -1;
    } else if (touche == 38) {
      // Haut
      bgY = -1;
    } else if (touche == 39) {
      // Droite
      bgX = 1;
    } else if (touche == 40) {
      // Bas
      bgY = 1;
    }

Cette fonction (découpé en morceaux pour plus de lisibilté) est appelé lorsqu'une touche est enfoncée. On commence par définir la variable 'touche' à laquelle on donne la valeur de la touche enfoncée, puis les variables 'bgX' et 'bgY' qui sont les variables utilisées pour savoir dans quel direction le joueur veut aller. Ensuite, suivant la touche enfoncée, on appelle les fonctions correspondante, ou on change les valeurs de 'bgX' ou 'bgY'.

    if (bgX != 0 || bgY != 0) {
      var dir:String = tabNiveau[posHerosY+bgY][posHerosX+bgX];
      if (dir == " " || dir == ".") {
        if (dir == ".") {
          // On va vers un trou
          tabNiveau[posHerosY+bgY][posHerosX+bgX] = "+";
        } else {
          // On va vers un endroit vide
          tabNiveau[posHerosY+bgY][posHerosX+bgX] = "@";
        }
        if (tabNiveau[posHerosY][posHerosX] == "+") {
          tabNiveau[posHerosY][posHerosX] = ".";
        } else {
          tabNiveau[posHerosY][posHerosX] = " ";
        }
        posHerosX = posHerosX+bgX;
        posHerosY = posHerosY+bgY;
        plateau.heros_mc._x = TAILLE*posHerosX;
        plateau.heros_mc._y = TAILLE*posHerosY;
        nbCoups++;
        nbCoupsTot++;

Ici, on regarde si c'est bien un déplacement et, si c'est le cas, on regarde vers quel type d'endroit il veut aller. S'il se dirige vers un trou ou un endroit vide, on fait le déplacement en changeant le tableau du jeu, la position du héros et on incrémente 1 coup aux variable de la classe.

      } else if (dir == "$" || dir == "*") {
        // On essaie de pousser une boule
        var bgX2:Number = bgX*2;
        var bgY2:Number = bgY*2;
        var dir2:String = tabNiveau[posHerosY+bgY2][posHerosX+bgX2];
        if (dir2 == " " || dir2 == ".") {
          // La boule peut-être poussée !
          enregAnnule(bgX, bgY, bgX2, bgY2);
          var boule:MovieClip = tabBoules[posHerosY+bgY][posHerosX+bgX];
          tabBoules[posHerosY+bgY2][posHerosX+bgX2] = boule;
          tabBoules[posHerosY+bgY][posHerosX+bgX] = "";
          boule._x = TAILLE*(posHerosX+bgX2);
          boule._y = TAILLE*(posHerosY+bgY2);

Si on se dirige vers une boule (placée ou pas) alors, on regarde ce qu'il y a 1 cran plus loin que la boule. Puis si l'endroit vers lequel la bouel est censée aller, alors on pourra bouger, donc on enregistre tout de suite notre objet d'annulation dans la fonction 'enregAnnule'.
Puis on récupère la référence de la boule qu'on veut pousser et on l'a bouge.

          if (dir2 == ".") {
            // La boule va vers un trou
            tabNiveau[posHerosY+bgY2][posHerosX+bgX2] = "*";
            boule.gotoAndStop(2);
            if (dir == "$") {
              // On va vers un endroit vide
              tabNiveau[posHerosY+bgY][posHerosX+bgX] = "@";
              // Et donc, une boule de plus au compteur !
              nbBoulesMises++;
            } else {
              // On va vers un trou
              tabNiveau[posHerosY+bgY][posHerosX+bgX] = "+";
            }
          } else {
            // La boule va vers un endroit vide
            tabNiveau[posHerosY+bgY2][posHerosX+bgX2] = "$";
            boule.gotoAndStop(1);
            if (dir == "$") {
              // On va vers un endroit vide
              tabNiveau[posHerosY+bgY][posHerosX+bgX] = "@";
            } else {
              // On va vers un trou
              tabNiveau[posHerosY+bgY][posHerosX+bgX] = "+";
              // Et donc, une boule de moins au compteur !
              nbBoulesMises--;
            }
          }

Suivant où la boule se dirige (un endroit vide ou un trou) et l'endroit où elle était, on modifie les valeurs du tableau et les valeurs des boulesMises.

          if (tabNiveau[posHerosY][posHerosX] == "+") {
            tabNiveau[posHerosY][posHerosX] = ".";
          } else {
            tabNiveau[posHerosY][posHerosX] = " ";
          }
          posHerosX = posHerosX+bgX;
          posHerosY = posHerosY+bgY;
          plateau.heros_mc._x = TAILLE*posHerosX;
          plateau.heros_mc._y = TAILLE*posHerosY;
          nbCoups++;
          nbCoupsTot++;
        }
      }
    }

Enfin, on modifie la position de notre héros dans le tableau et dans l'affichage.

    if (nbBoulesMises == nbBoules) {
      niveau++;
      creerPlateau();
      constructionJeu();
    }
    affichage();
  }

Pour finir la fonction, on vérifie si le nombre de boules mises est égal au nombre de boules du niveau, si c'est le cas, le joueur a fini le niveau, donc on en charge un autre. Et on réactualises l'affichage en appellant la fonction.

  private function enregAnnule(bouleAvtX:Number, bouleAvtY:Number, bouleAprX:Number, bouleAprY:Number):Void {
    objAnnule = new Object();
    objAnnule.posHerosX = posHerosX;
    objAnnule.posHerosY = posHerosY;
    objAnnule.bouleAvtX = posHerosX+bouleAvtX;
    objAnnule.bouleAvtY = posHerosY+bouleAvtY;
    objAnnule.bouleAprX = posHerosX+bouleAprX;
    objAnnule.bouleAprY = posHerosY+bouleAprY;
  }

Cette fonction sert à enregistrer l'objet d'annulation de déplacement de boules. Elle est appelée avant chaque déplacement de boule. Ainsi, on enregistre les positions qui vont être déplacées dans l'objet pour pouvoir les remettre quand le joueur le souhaitera.

  private function annule(Void):Void {
    if (objAnnule != undefined) {
      // Remise en place du héros
      if (tabNiveau[posHerosY][posHerosX] == "+") {
        // Le héros est sur un trou
        tabNiveau[posHerosY][posHerosX] = ".";
      } else {
        tabNiveau[posHerosY][posHerosX] = " ";
      }
      posHerosX = objAnnule.posHerosX;
      posHerosY = objAnnule.posHerosY;
      if (tabNiveau[posHerosY][posHerosX] == ".") {
        // Le héros est sur un trou
        tabNiveau[posHerosY][posHerosX] = "+";
      } else {
        tabNiveau[posHerosY][posHerosX] = "@";
      }
      plateau.heros_mc._x = TAILLE*posHerosX;
      plateau.heros_mc._y = TAILLE*posHerosY;
      // Remise de la boule
      var boule:MovieClip = tabBoules[objAnnule.bouleAprY][objAnnule.bouleAprX];
      boule._x = TAILLE*objAnnule.bouleAvtX;
      boule._y = TAILLE*objAnnule.bouleAvtY;
      if (tabNiveau[objAnnule.bouleAprY][objAnnule.bouleAprX] == "*") {
        // La boule est sur un trou
        tabNiveau[objAnnule.bouleAprY][objAnnule.bouleAprX] = ".";
        nbBoules--;
      } else {
        tabNiveau[objAnnule.bouleAprY][objAnnule.bouleAprX] = " ";
      }
      if (tabNiveau[objAnnule.bouleAvtY][objAnnule.bouleAvtX] == ".") {
        // La boule était sur un trou
        tabNiveau[objAnnule.bouleAvtY][objAnnule.bouleAvtX] = "*";
        nbBoules++;
        boule.gotoAndStop(2);
      } else {
        tabNiveau[objAnnule.bouleAvtY][objAnnule.bouleAvtX] = "$";
        boule.gotoAndStop(1);
      }
      tabBoules[objAnnule.bouleAprY][objAnnule.bouleAprX] = "";
      tabBoules[objAnnule.bouleAvtY][objAnnule.bouleAvtX] = boule;
      nbCoups++;
      nbCoupsTot++;
    }
  }

Cette fonction sert à annuler le dernier déplacement de boule, lorsqu'on clique sur la touche espace. On commence par vérifier si l'objet d'annulation existe bien et, si c'est le cas on remet le héros à la place où il était avant, ainsi que la boule déplacée. On fait aussi tout un tas de vérification pour remettre les bons nombre de 'boulesMises', en changeant les positions du héros et de la boule en question, ainsi que l'affichage de la boule ('gotoAndStop'). Enfin, on incrémente un coup aux variables de la classe.

  private function sauvegarde(Void):Void {
    if (enregistrement>0) {
      var monShare:SharedObject = new SharedObject();
      monShare = SharedObject.getLocal(NOMSHARE);
      monShare.data.jeu = this;
      monShare.flush();
      enregistrement--;
    }
  }

Cette fonction commence par vérifié si le crédit d'enregistrement par partie n'est pas épuisé, et si il en reste, alors on enregistre dans le ShareObject toutes les variables de la classe, et on écrit le ShareObject, pour enfin retiré un enregistrement disponible.

  private function recov(Void):Void {
    var monShare:SharedObject = new SharedObject();
    monShare = SharedObject.getLocal(NOMSHARE);
    var sauv:Object = monShare.data.jeu;
    if (sauv != null) {
      pseudo = sauv.pseudo;
      mesNiveaux = sauv.mesNiveaux;
      niveau = sauv.niveau;
      // Remise des variables
      nbBoules = 0;
      nbBoulesMises = 0;
      nbCoups = sauv.nbCoups;
      nbCoupsTot = sauv.nbCoupsTot;
      nomNiveau = sauv.nomNiveau;
      tabNiveau = sauv.tabNiveau;
      tabBoules = sauv.tabBoules;
      posHerosX = sauv.posHerosX;
      posHerosY = sauv.posHerosY;
      objAnnule = sauv.objAnnule;
      enregistrement = sauv.enregistrement;
      var monNiveau:XMLNode = mesNiveaux.firstChild.childNodes[niveau-1];
      nomNiveau = monNiveau.attributes.Id;
      var hauteur:Number = tabNiveau.length;
      var largeur:Number = tabNiveau[0].length;
      var ident:Number;
      var initObj:Object = new Object();
      var debut:Boolean;
      var cellule:String;
      var aff:String;
      creerPlateau();
      for (var i = 0; i<hauteur; i++) {
        debut = false;
        for (var j = 0; j<largeur; j++) {
          cellule = tabNiveau[i][j];
          aff = "";
          initObj._x = TAILLE*j;
          initObj._y = TAILLE*i;
          if (cellule == "#") {
            // mur
            aff = "mur";
            debut = true;
          } else if (cellule == " " && debut) {
            // sol
            aff = "sol";
          } else if (cellule == "$") {
            // Boule
            aff = "sol";
            nbBoules++;
            plateau.attachMovie("boule", "boule"+nbBoules, PROFHEROS+nbBoules, initObj);
            tabBoules[i][j] = eval(plateau+".boule"+nbBoules);
            eval(plateau+".boule"+nbBoules).gotoAndStop(1);
          } else if (cellule == "@") {
            // Heros
            aff = "sol";
            plateau.attachMovie("heros", "heros_mc", PROFHEROS, initObj);
            posHerosX = j;
            posHerosY = i;
          } else if (cellule == ".") {
            // trou
            aff = "trou";
          } else if (cellule == "*") {
            // Boule placée
            aff = "trou";
            nbBoules++;
            plateau.attachMovie("boule", "boule"+nbBoules, PROFHEROS+nbBoules, initObj);
            tabBoules[i][j] = eval(plateau+".boule"+nbBoules);
            eval(plateau+".boule"+nbBoules).gotoAndStop(2);
            nbBoulesMises++;
          } else if (cellule == "+") {
            // Héros sur trou
            aff = "trou";
            plateau.attachMovie("heros", "heros_mc", PROFHEROS, initObj);
            posHerosX = j;
            posHerosY = i;
          }
          if (aff != "") {
            ident = (100*i+j);
            plateau.attachMovie(aff, "obj"+ident, ident, initObj);
          }
        }
      }
    }
  }

Cette fonction sert à remettre la sauvegarde du joueur. En fait, on commence par réattribué les variables qui se trouvent dans le ShareObject, puis on fait quasiment la même chose que dans la fonction 'constructionJeu', c'est pourquoi elle ne sera pas recommentée ici.

  private function enregistre(Void):Void {
    if (niveau>1) {
      monEnvoi = new LoadVars();
      monEnvoi.pseudo = pseudo;
      monEnvoi.niveau = niveau;
      monEnvoi.nbCoupsTot = nbCoupsTot;
      monEnvoi.sendAndLoad("enregsoko.php", monEnvoi, "POST");
      // Ici on dit que notre onload sera la fonction enregistreAction
      //    de note objet Jeu, via Delegate.create
      monEnvoi.onLoad = Delegate.create(this, enregistreAction);
    }
  }

Cette fonction sert à envoyer au fichier PHP 'enregsoko.php' les différentes variable dont il a besoin pour faire l'enregistrement. Ensuite, on définit l'onLoad de de l'objet 'monEnvoi' vers la fonction 'enregistreAction'.

  private function enregistreAction(succes:Boolean):Void {
    if (succes && monEnvoi.res == 1) {
      var monShare:SharedObject = new SharedObject();
      monShare = SharedObject.getLocal(NOMSHARE);
      monShare.data.jeu = null;
      creerPlateau();
      nbCoupsTot = 0;
      niveau = 1;
      constructionJeu();
    } else {
      trace("Erreur pendant l'enregistrement du score");
    }
  }

Cette fonction sert à détruire la sauvegarde de jeu du joueur, puis à recommencer le jeu au début, en rappellant les fonctions 'creerPlateau' et 'constructionJeu'.

^^ Retour ^^

sokoscores.sql

Ce fichier sert à créer la table de donnée MySQL pour enregistrer les scores du sokoban

CREATE TABLE sokoscores (
  id_score int not null auto_increment primary key,
  pseudo varchar(200) not null default'',
  niveau int(4) not null default'0',
  nbCoups int not null default'0',
  date_score datetime not null default'0'
);

on a donc besoin d'un identifiant (toujours utile), du pseudo du joueur, du niveau auquel il a terminé, du nombre de coups qu'il a fait pour arriver ici et de la date de l'enregistrement.

^^ Retour ^^

enregsoko.php

Ce fichier, auquel on envoi le pseudo du joueur, le niveau et le nombre de coups sert à enregistrer le score dans la base de donnée.

include('conf.php');

$pseudo = $_POST['pseudo'];
$niveau = $_POST['niveau'];
$nbCoupsTot = $_POST['nbCoupsTot'];

On commence par aller cherche le fichier 'conf.php', qui contient les paramètres de connection à la base de donnée et la classe requêteur MySQL de ma création. Puis on récupères les variables envoyés en POST par l'animation Flash.

$res = 0;
if ($pseudo != "" && $niveau > 1 && $nbCoupsTot > 0) {
  $date = date("Y-m-d H:i:s");
  $req = $sql->insert("sokoscores","\"$pseudo\",\"$niveau\",\"$nbCoupsTot\",\"$date\"","`pseudo`,`niveau`,`nbCoups`,`date_score`");
  if ($req) {
    $res = 1;
  }
}

On initialise la variable '$res' de retour à 0, ensuite, on vérifie que tous les champs sont renseignés. Si c'est le cas, on fait notre requête d'ajout, et si tout se passe bien, on met la varaible de retour à 1.

echo "&res=$res&";

Enfin, on écrit notre variable de retour pour Flash.

^^ Retour ^^

conf.php

Ce fichier est utilisé pour tous les scripts PHP qui auront besoin de se connecter à la base de donnée MySQL. Il paramètre l'accès à la base de donnée et en définissant l'hôte, le nom de la base de donnée, le nom d'utilisateur et le mot de passe. Ensuite on définit toute une classe 'sql' qui permet d'effectuer des requête sur la base de données, suivant les paramètres définies auparavant.
Ce fichier ne sera pas détaillé ici car ce n'est pas le sujet.

^^ Retour ^^

sokobaide.fla

Cette animation Flash est l'aide du jeu Sokoban. Il est constitué en fait d'un accordion. Commes les autres animations de l'application, très très peu de code :

var monSokobaide:Sokobaide = new Sokobaide(this);

On appelle simplement la classe 'Sokoban' en créant un nouvel objet 'monSokobaide' sur la scène en envoyant en paramètres la référence à l'endroit où est appellé la classe.
Dans la bibliothèque, il y a les composants Flash utilisé dans l'animation : 'Accordion' et 'DataGrid'. Il y a aussi un clip 'texte' avec comme identifiant de liaison 'texte' qui contient un champ texte avec pour nom 'texte_txt'.

^^ Retour ^^

accordion.xml

Ce fichier xml définit ce que l'on va afficher dans l'animation 'sokobaide.fla'. Il se présente sous cette forme :

<accordion>
  <text nom="Historique du Sokoban"><![CDATA[Contenu avec <b>HTML</b>]]></text>
  <text nom="Historique du Sokoban"><![CDATA[Contenu avec <b>HTML</b>]]></text>
</accordion>

Chaque noeud 'text' sera affiché dans un élément de l'accordion avec comme titre 'nom'.

^^ Retour ^^

scores.php

Ce fichier PHP va chercher les scores dans la base de donnée et les retournes tous, sous forme d'un fichier xml.

include('conf.php');
$req = $sql->query("SELECT * FROM sokoscores ORDER BY niveau DESC,nbCoups ASC, date_score DESC");
echo "<scores>\n";

On inclut le fichier de configuration MySQL 'conf.php' et on fait la requêtes. On commence par écrire la racine 'scores' du fichier xml.

while($row = mysql_fetch_array($req)) {
  $dat = explode(" ",$row['date_score']);
  $dat1 = explode("-",$dat[0]);
  $date = "$dat1[2]/$dat1[1]/$dat1[0] à $dat[1]";
  echo "<score pseudo=\"".$row['pseudo']."\" niveau=\"".$row['niveau']."\" date=\"$date\" nbCoups=\"".$row['nbCoups']."\"/>\n";
}

On parcourt tous les résultats de la requête. Pour chacun d'entre eux, on transforme la date dans un format francisé et on affiche un nouveau noeud 'score' avec tous les attributs qui définissent le score.

echo "</scores>";

Enfin, on ferme la racine du xml.

^^ Retour ^^

conf.php

CF conf.php, qui est le même fichier PHP.

^^ Retour ^^

Sokobaide.as

C'est la classe qui crée l'accordion et le rempli, dans sokobaide.fla.

import mx.controls.gridclasses.DataGridColumn;
import mx.utils.Delegate;
class Sokobaide {
  private var scene:MovieClip;
  private var monAccordion:MovieClip;
  private var monLoadPhp:XML;
  private var monXml:XML;
  private var monEcouteur:Object;

On commence par importer les classes Flash que l'on utilisera dans la classe. 'gridclasses.DataGridColumn' est le type de donnée d'une colonne d'un DataGrid; on l'utilise pour créer dynamiquement des colonnes de DataGrid avec des largeurs spécifiques. 'Delegate' a déjà été vu et revu.
Ensuite, on crée la classe, et on définit les variable de la classe : 'scene' qui est la référence à l'endroit d'où est appellée la classe, 'monAccordion' est la référence à l'accordion crée, 'monLoadPhp' est l'objet XML dans lequel on chargera les scores, 'monXml' est l'objet XML dans lequel on chargera le contenu de l'accordion et 'monEcouteur' est l'objet qui fera l'écouteur sur l'accordion.

  public function Sokobaide(scen:MovieClip) {
    scene = scen;
    var initObj:Object = new Object();
    initObj._x = 0;
    initObj._y = 0;
    scene.attachMovie("Accordion", "monAccordion", 1, initObj);
    monAccordion = eval(scene+".monAccordion");
    monAccordion.setSize(550, 400);
    monLoadPhp = new XML();
    monLoadPhp.ignoreWhite = true;
    // Ici on dit que notre onLoad sera la fonction loadScore
    //    de notre objet Sokobaide, via Delegate.create
    monLoadPhp.onLoad = Delegate.create(this, loadScore);
    monXml = new XML();
    monXml.ignoreWhite = true;
    // Ici on dit que notre onLoad sera la fonction loadXml
    //    de notre objet Sokobaide, via Delegate.create
    monXml.onLoad = Delegate.create(this, loadXml);
    monXml.load("accordion.xml");
    monEcouteur = new Object();
    // Ici on dit que notre change sera la fonction ecoutChange
    //    de notre objet Sokobaide, via Delegate.create
    monEcouteur.change = Delegate.create(this, ecoutChange);
    monAccordion.addEventListener("change", monEcouteur);
    scene.detruire = Delegate.create(this, detruire);
  }

C'est le constructeur de la classe, qui crée l'accordion en lui donnant les dimensions de la scène, définit la fonction 'onLoad' de 'monLoadPHP' qui sera la fonction 'loadScore' de la classe, fait la même chose pour 'monXml' qui sera la fonction 'loadXml', charge le fichier xml de contenu de l'accordion, et crée l'écouteur sur le changement de l'accordion.

  private function ecoutChange(Evt:Object):Void {
    if (monAccordion.selectedIndex == monAccordion.numChildren-1) {
      // On vient sur les scores, on charge le php!
      monLoadPhp.load("scores.php");
    }
  }

Cette fonction est appellée lorsque l'utilisateur change de parties dans l'accordion. On vérifie juste s'il va sur le dernier, et si c'est le cas, on recharge les scores. Ceci pour être sûr d'afficher les derniers scores disponibles sur le serveur.

  private function loadScore(succes:Boolean):Void {
    if (succes) {
      afficheScores();
    } else {
      trace("Erreur de chargement du Php !");
    }
  }

Cette fonction est l'onLoad' de l'objet Xml 'monLoadPhp'. Elle vérifie juste si le fichier est bien chargé et, si c'est le cas, appelle la fonction 'afficheScores' pour insérer tous les scores dans le DataGrid.

  private function loadXml(succes:Boolean):Void {
    if (succes) {
      ecrireAccordion();
    } else {
      trace("erreur de chargement du xml");
    }
  }

Idem que la précendente, sauf que c'est pour 'monXml' et qu'on appelle 'ecrireAccordion' en cas de succès.

  private function ecrireAccordion(Void):Void {
    var monNoeud:Array = monXml.firstChild.childNodes;
    var textAct:TextField;
    for (var i = 0; i<monNoeud.length; i++) {
      monAccordion.createChild("texte", "texte"+i, {label:monNoeud[i].attributes.nom});
      textAct = eval(monAccordion+".texte"+i).texte_txt;
      textAct._width = 547;
      textAct.autoSize = true;
      textAct.htmlText = monNoeud[i].firstChild.nodeValue;
    }
    monAccordion.createChild("DataGrid", "scores", {label:"Meilleurs Scores"});
    monAccordion.scores.setSize(548, 335);
    ajtCol(monAccordion.scores, "Rang", 40);
    ajtCol(monAccordion.scores, "Pseudo", 230);
    ajtCol(monAccordion.scores, "Niveau", 50);
    ajtCol(monAccordion.scores, "NbCoups", 60);
    ajtCol(monAccordion.scores, "Date", 130);
  }

On commence par mettre dans la variable 'monNoeud' le contenu que l'on doit parcourir du fichier Xml. On le parcourt dans la boucle for, et à chaque nouvelle ligne, on ajoute une nouvelle partie ('createChild') avec comme clip attaché 'texte' (celui qui contient un champ texte) en lui donnant le nom renseigné dans le fichier xml. Puis, on paramètre la largeur du champ texte et sa propriété 'autoSize' pour que rien ne soit caché et on applique le texte html issu du fichier xml.
Ensuite, une fois le fichier xml parcourut en entier, on ajoute encore une partie, qui contient le dataGrid pour les scores et on fait les ajouts de colonne en passant par la fonction 'ajtCol' vue juste après.

  private function ajtCol(grille:MovieClip, nom:String, larg:Number):Void {     var colonne:DataGridColumn = new DataGridColumn(nom);     colonne.headerText = nom;     colonne.width = larg;     grille.addColumn(colonne);   }

Cette fonction sert à ajouter une colonne dans le DataGrid. Elle prend en paramètre : 'grille' qui est la référence au DataGrid dans lequel on doit ajouter la colonne, 'nom' qui est le nom que l'on affiche dans le titre de la colonne et 'larg' qui est la largeur de la colonne.
On crée un objet colonne de type 'DataGridColumn', on lui attribue les paramètres passés à la fonction, et on l'ajoute au DataGrid avec 'addColumn'.

  private function afficheScores(Void):Void {
    var monScore:MovieClip = monAccordion.scores;
    monScore.removeAll();
    var monNoeud:Array = monLoadPhp.firstChild.childNodes;
    for (var i = 0; i<monNoeud.length; i++) {
      var obj:Object = new Object();
      obj.Rang = i+1;
      obj.Pseudo = monNoeud[i].attributes.pseudo;
      obj.Niveau = monNoeud[i].attributes.niveau;
      obj.NbCoups = monNoeud[i].attributes.nbCoups;
      obj.Date = monNoeud[i].attributes.date;
      monScore.addItem(obj);
    }
  }

Cette fonction, appellée lorsque le fichier PHP des scores est fini de charger, commence par supprimer toutes les lignes du DataGrid, puis parcourt toutes les lignes du xml et les ajoutes dans le DataGrid, en passant par un objet dont toutes les propriétés sont en fait le nom des colonnes données au DataGrid. On ajoute véritablement au DataGrid avec 'addItem'.

  private function detruire(Void):Void {
    for (var i = 0; i       eval("monAccordion.texte"+i).texte_txt.removeMovieClip();
      eval("monAccordion.texte"+i).removeMovieClip();
    }
    monAccordion.removeEventListener("change", monEcouteur);
    removeMovieClip(eval("monAccordion"));
    monXml.onLoad = null;
    monLoadPhp.onLoad = null;
    monLoadPhp = null;
    monXml = null;
  }

Il s'agit ici de la fonction destructeur de la classe, qui sera appellée lors de la fermeture de la fenêtre. Elle détruit tous les éléments de l'accordion, puis l'accordion et son écouteur. Enfin on détruit les 'onLoad' des fichiers PHP et XML, ainsi que les objets qui ont permis de les charger.

^^ Retour ^^

musique.fla

Cette animation est un lecteur MP3 utilisant le composant MediaPlayback, qui va cherché sa playlist dans un fichier PHP formaté en XML. Dans sa bibliothèque, il y a seulement le composant Flash 'MediaPlayback' qui permettra de le mettre sur la scène dans la classe. Au niveau du code, toujours une seule ligne de code :

var maMusique:Musique = new Musique(this);

Cela permet, d'appeller la classe 'Musique', en donnant en paramètre la référence à l'endroit où l'on appelle la classe.

^^ Retour ^^

listMP3.php

Ce script PHP récupère la liste des fichier MP3 disponibles dans le dossier mp3, les tris aléatoirement et retourne un fichier xml avec chacun d'entre eux.

$mp3 = glob("mp3/*.mp3");

On récupère dans le tableau '$mp3' la liste de tous les MP3 disponibles (fichiers dans le dossier mp3 dont le nom se termine par '.mp3').

srand((float)microtime()*1000000);
shuffle($mp3);

On trie aléatoirement le tableau '$mp3', après avoir initialisé le générateur de nombre aléatoire avec 'srand' pour un aléatoire plus poussé.

echo "<Musiques>\n";
  foreach($mp3 as $value) {
    echo "<track>$value</track>\n";
  }
echo "</Musiques>";

Enfin, on écrit notre xml en parcourant ligne par ligne le tableau '$mp3', en inscrivant à chaque fois le noeud 'track' contenant simplement l'url du fichier MP3.

^^ Retour ^^

Musique.as

C'est la classe qui crée le player MP3, à l'aide du composant MediaPlayback.

import mx.utils.Delegate;
class Musique {
  private var scene:MovieClip;
  private var mesMusiques:Array;
  private var zikAct:Number;
  private var monLoadPHP:XML;

On importe la classe 'Delegate' puisqu'elle nous servira dans la classe, puis on crée la classe, en commençant par définir toutes les variables de la classe : 'scene' qui est la référence de l'endroit où est appellée la classe, 'mesMusiques' qui est le tableau contenant toutes les url des mp3 de la playlist, 'zikAct' qui est l'index indiquant quelle chanson l'utilisateur écoute actuellement et 'monLoadPHP' qui est l'objet XML qui permettra de charger la playlist.

  public function Musique(scen:MovieClip) {
    scene = scen;
    var initObj:Object = new Object();
    // Paramètre pour le MediaDisplay
    initObj._x = 0;
    initObj._y = 0;
    initObj._width = 300;
    initObj._height = 10;
    initObj.mediaType = "MP3";
    scene.attachMovie("MediaPlayBack", "monPlayer", 1, initObj);
    zikAct = 0;
    monLoadPHP = new XML();
    monLoadPHP.ignoreWhite = true;
    monLoadPHP.load("listMP3.php");
    // Ici on dit que notre onLoad sera la fonction loadList
    //    de notre objet Musique, via Delegate.create
    monLoadPHP.onLoad = Delegate.create(this, loadList);
    scene.monPlayer.autoPlay = false;
    var monEcouteur:Object = new Object();
    // Ici on dit que notre onComplete sera la fonction recharge
    //    de notre objet Musique, via Delegate.create
    monEcouteur.complete = Delegate.create(this, recharge);
    scene.monPlayer.addEventListener("complete", monEcouteur);
  }

C'est le constructeur de la classe, qui crée le MediaPlayback avec un 'attachMovie' sur la 'scene', initialise la chanson lue actuellement 'zikAct' à 0, paramètre et lance le chargement du fichier mp3 dans 'monLoadPHP' et crée l'écouteur de fin de chanson sur le MediaPlayback, dans la fonction 'recharge' de la classe.

  private function loadList(succes:Boolean):Void {
    if (succes) {
      mesMusiques = monLoadPHP.firstChild.childNodes;
      recharge();
    } else {
      trace("Erreur de chargement.");
    }
  }

C'est la fonction appellée lorsque le fichier 'listMP3.php' est fini de chargé. Si tout s'est bien passé, on place la liste des MP3 dans le tableau 'mesMusiques' et on lance la lecture en appellant la fonction 'recharge'.

  private function recharge(Evt:Object):Void {
    if (mesMusiques[zikAct].firstChild.nodeValue == undefined) {
      zikAct = 0;
    }
    scene.monPlayer.setMedia(mesMusiques[zikAct].firstChild.nodeValue, "MP3");
    scene.monPlayer.play();
    zikAct++;
  }

On commence par vérifier si la musique qu'on est censé commencer à jouer est défini. Si ce n'est pas le cas, on réinitialiste 'zikAct' à 0. Ensuite, on attribue le fichier mp3 suivant au MediaPlaybacj avec 'setMedia', puis on lance la lecture avec 'play'. Enfin, on incrément 'zikAct' pour avancé d'une piste pour la prochaine fois.

^^ Retour ^^

video.fla

Cette animation permet de lire une vidéo, à l'aide des composants Flash 'MediaDiplay' et 'MediaController'. Donc on trouve ces 2 composants dans sa bibliothèque. Au niveau du code, on a juste :

var maVid:Vid = new Vid(this);

On appelle simplement la classe 'Vid' en créant un nouvel objet 'maVid' sur la scène en envoyant en paramètre la référence à l'endroit où est appellé la classe.

^^ Retour ^^

video.flv

Ce fichier est juste la vidéo que l'on veut afficher, enregistré au format 'flv' grâce à Flash.

^^ Retour ^^

Vid.as

Cette classe permet d'afficher tous les éléments indispensable pour la lecture vidéo et la vidéo elle-même.

import mx.utils.Delegate;
class Vid {
  private var scene:MovieClip;
  private var maVideo:MovieClip;
  private var monCTL:MovieClip;

On commence par importé la classe 'Delegate' car on en a besoin dans la classe, puis on crée la classe en commençant par définir les variables de la classe : 'scene' qui est la référence à l'endroit où est appellé la classe, 'maVideo' qui sera la référence à l'objet 'MediaDisplay' et 'monCTL' qui sera la référence à l'objet 'monCTL'.

  public function Vid(scen:MovieClip) {
    scene = scen;
    var initObj:Object = new Object();
    // Paramètre pour le MediaDisplay
    initObj._x = 0;
    initObj._y = 0;
    initObj._width = 230;
    initObj._height = 184;
    scene.attachMovie("MediaDisplay", "maVideo", 1, initObj);
    maVideo = eval(scene+".maVideo");
    // Paramètre pour le MediaContrôleur
    initObj._x = 0;
    initObj._y = 184;
    initObj._width = 230;
    initObj._height = 60;
    scene.attachMovie("MediaController", "monCTL", 2, initObj);
    monCTL = eval(scene+".monCTL");
    // Définition de l'écouteur pour pouvoir appliquer la contrôleur à la vidéo
    var monEcouteur:Object = new Object();
    // Ici on dit que notre load sera la fonction charge
    //    de notre objet Vid, via Delegate.create
    monEcouteur.load = Delegate.create(this, charge);
    monCTL.addEventListener("load", monEcouteur);
    // On demande de chargé la vidéo
    maVideo.setMedia("video.flv", "FLV");
    // et on lit
    maVideo.play();
    // fonction de destruction a applé lors de la fermeture de la fenêtre
    scene.detruire = function(Void) {
      removeMovieClip(maVideo);
    };
  }

C'est le constructeur de la classe. Elle pose les 2 composants 'MediaDiplay' et 'MediaController' avec les noms 'maVideo' et 'monCTL', en paramétrant leur position dans l'animation et leur taille. On ajoute aussi un écouteur sur le 'MediaDisplay' pour appellé la fonction 'charge' lorsque la vidéo sera en cours de chargement.
On définit aussi la classe 'detruire' sur la scene qui sera le destructeur de la classe pour ne pas avoir de problème lors de la réouverture d'une fenêtre vidéo.

  private function charge(Void):Void {
    // Association avec le MediaDisplay
    monCTL.associateDisplay(maVideo);
  }

Cette fonction, appellée lorsque la vidéo est en cours de chargement, permet d'associer le contrôleur à la vidéo.

^^ Retour ^^

vidStream.fla

Cette animation permet de lire une vidéo en streaming. Dans la bibliothèque, on a les boutons pour diriger la vidéo avec des noms de liaison, un clip contenant une vidéo vide et un progressBar. Au niveau du code, on a juste :

var maVidStream:VidStream = new VidStream(this,"video.flv",1,1);

On appelle simplement la classe 'VidStream' en créant un nouvel objet 'maVidStream' sur la scène en envoyant en paramètre la référence à l'endroit où est appellé la classe, la vidéo que l'on doit afficher, son nombre de minutes et son nombre de secondes (ici 1min 1sec).

^^ Retour ^^

video.flv

Ce fichier est juste la vidéo que l'on veut afficher, enregistré au format 'flv' grâce à Flash.

^^ Retour ^^

vidStream.as

Cette classe permet d'afficher tous les éléments indispensable pour la lecture vidéo et la vidéo elle-même.

import mx.utils.Delegate;
class VidStream {
  private var scene:MovieClip;
  private var video:String;
  private var monNc:NetConnection;
  private var fluxVideoNs:NetStream;
  private var min:Number;
  private var sec:Number;
  private var inter:Number = 0;

On commence par importer la classe 'Delegate' de Flash, car on en aura besoin. Ensuite, on définit les variable de la classe : 'scene' qui est la référence à l'endroit où est appelé la classe, 'video' est l'url de la vidéo, 'monNc' est l'objet 'NetConnection' pour le streaming, 'fluxVideoNs' est l'objet 'NetStream' pour le streaming, 'min' est le nombre de minutes de la vidéo, 'sec' est le nombre de seconde de la vidéo et 'inter' correspond au nombre attribué par le 'setInterval'.

  public function VidStream(scen:MovieClip, vide:String, mi:Number, se:Number) {
    scene = scen;
    video = vide;
    min = mi;
    sec = se;
    var prof:Number = 1;
    // Création de fond gris
    scene.createEmptyMovieClip("fondGris", prof);
    prof++;
    var fondGris:MovieClip = eval(scene+".fondGris");
    var cl:String = "D0D0D0";
    with (fondGris) {
      moveTo(0, 0);
      linestyle(0, "0x"+cl, 100);
      beginFill("0x"+cl, 100);
      lineTo(230, 0);
      lineTo(230, 220);
      lineTo(0, 220);
      endFill();
    }
    var initObj:Object = new Object();
    initObj._x = 0;
    initObj._y = 0;
    // Création de l'objet vidéo
    scene.attachMovie("maVid", "maVid", prof, initObj);
    prof++;
    // Création du bouton play
    scene.attachMovie("play", "play_btn", prof);
    scene.play_btn._x = 20;
    scene.play_btn._y = 202;
    scene.play_btn.onRelease = Delegate.create(this, lecture);
    prof++;
    // Création du bouton pause
    scene.attachMovie("pause", "pause_btn", prof, initObj);
    scene.pause_btn._x = 20;
    scene.pause_btn._y = 202;
    scene.pause_btn.onRelease = Delegate.create(this, arret);
    scene.pause_btn._visible = false;
    prof++;
    // Création du bouton stop
    scene.attachMovie("stop", "stop_btn", prof, initObj);
    scene.stop_btn._x = 56;
    scene.stop_btn._y = 202;
    scene.stop_btn.onRelease = Delegate.create(this, arretC);
    prof++;
    // Paramètre pour le ProgressBar
    initObj._x = 77;
    initObj._y = 188;
    initObj._width = 150;
    initObj._height = 30;
    initObj.label = "Lecture : %3%% ";
    initObj.mode = "manual";
    scene.attachMovie("ProgressBar", "avance", prof, initObj);
    prof++;
    // Paramétrage du streaming
    monNc = new NetConnection();
    monNc.connect(null);
    fluxVideoNs = new NetStream(monNc);
    scene.maVid.maVideo.attachVideo(fluxVideoNs);
    // Definition du destructeur
    scene.detruire = function(Void) {
      this.maVidStream.fluxVideoNs.close();
    };
  }

Cette fonction est le constructeur de la classe. Elle commence par crée tous les élements à afficher (boutons, vidéo et progressBar). Noter ici que les initObjet sur les boutons ne fonctionnent pas, c'est pour cela que je le fais après leur création. Le reste a déjà été vue plusieurs fois donc n'est pas redétaillé.
Ensuite, on crée notre streaming avec les 4 ernière lignes de la fonction, en reliant 'monNc', 'fluxVideoNs' et l'affichage de la vidéo. Ensuite, on est prêt à afficher une vidéo en streaming sur notre animation.
Enfin, on définit le destructeur de la classe, qui sera appellée lors de la fermeture de la fenêtre pour arrêter le streaming.

  private function maj(Void):Void {
    scene.avance.setProgress(fluxVideoNs.time, min*60+sec);
  }

Cette fonction est appelé par un setInterval pour mettre à jour l'affichage de la progression de la vidéo. On utilise simplement la méthode 'setProgress' du ProgressBar, en lui donnant le temps actuel et le temps total de la vidéo.

  private function lecture(Void):Void {
    if (inter>0) {
      fluxVideoNs.pause(false);
    } else {
      fluxVideoNs.play(video);
    }
    inter = setInterval(this, "maj", 500);
    scene.play_btn._visible = false;
    scene.pause_btn._visible = true;
  }

Cette fonction est appellée lors du clic sur le bouton play. Si un interval a déjà été définit, cela signifie que l'on est en pause, donc on reprend la lecture. Sinon, on lance la lecture en spécifiant l'url du fichier vidéo à streamé. Enfin, on lance le setInterval pour mettre à jour l'affichage, on rend invisible le bouton play et on affiche le bouton pause.

  private function arret(Void):Void {
    fluxVideoNs.pause(true);
    clearInterval(inter);
    scene.play_btn._visible = true;
    scene.pause_btn._visible = false;
  }

Cette fonction est appellée lors du clic sur le bouton pause. Elle met simplementla vidéo en pause, détruit le setInterval, rend invisible le bouton pause et réaffiche le bouton play.

  private function arretC(Void):Void {
    fluxVideoNs.close();
    clearInterval(inter);
    inter = 0;
    scene.play_btn._visible = true;
    scene.pause_btn._visible = false;
  }

Cette fonction est appellée lors du clic sur le bouton stop. Elle appelle la méthode 'close' du streaming pour arrêter la vidéo, détruit le setInterval, et remets 'inter' à 0, rend invisible le bouton pause et réaffiche le bouton play.

^^ Retour ^^

webcam.fla

Cette animation flash permet d'afficher la webcam de l'utilisateur et de jouer le son issu de son micro. Dans a bibliothèque, il y a les composants Flash utilisé : 'Label', NumericStepper' et 'ProgressBar', 1 symbole de vidéo vide et un clip 'camera' avec comme nom de liaison 'camera' qui contient le clip vidéo vide ayant pour nom 'maVideo'.

var maWebcam:Webcam = new Webcam(this);

On appelle simplement la classe 'Webcam' en créant un nouvel objet 'maWebcam' sur la scène en envoyant en paramètre la référence à l'endroit où est appellé la classe.

^^ Retour ^^

Webcam.as

Cette classe affiche tous les éléments nécessaire à l'affichage de la webcam et des paramètres du micro (numericStepper pour régler le volume du micro, label pour afficher la description et progressbar pour afficher le volume de sortie actuel.
En fait, cette classe n'a qu'une seule fonction, qui est son constructeur.

class Webcam {
  public function Webcam(scene:MovieClip) {
    // Création de fond gris
    scene.createEmptyMovieClip("fondGris", 1);
    var fondGris:MovieClip = eval(scene+".fondGris");
    var cl:String = "D0D0D0";
    with (fondGris) {
      moveTo(0, 0);
      linestyle(0, "0x"+cl, 100);
      beginFill("0x"+cl, 100);
      lineTo(200, 0);
      lineTo(200, 208);
      lineTo(0, 208);
      endFill();
    }
    var initObj:Object = new Object();
    // Paramètre pour le ProgressBar
    initObj._x = 0;
    initObj._y = 0;
    scene.attachMovie("camera", "camera", 2, initObj);
    var maVideo:MovieClip = scene.camera.maVideo;
    maVideo._width = 200;
    maVideo._height = 150;

Pour commencer, on crée un fond gris à l'animation, puis on attache le clip camera qui est dans la bibliothèque et on crée la variable 'maVideo' qui est une référence à la vidéo contenu dans le clip. On lui affecte les dimensions que l'on souhaite pour mettre au format de l'animation.

    // Démarrage du micro et paramérage
    var monMic:Object = Microphone.get();
    monMic.gain = 0;
    monMic.setGain(30);
    monMic.setUseEchoSuppression(true);
    scene.attachAudio(monMic);

Ici, on définit un nouvel objet 'monMic' auquel on donne les attributs du micro pour le volume, puis on atache le son du micro sur la scene, avec 'attachAudio'.

    // Paramètre pour le ProgressBar
    initObj._x = 0;
    initObj._y = 151;
    initObj._width = 174;
    initObj._height = 30;
    initObj.label = "Niveau Micro %3%% ";
    initObj.mode = "manual";
    scene.attachMovie("ProgressBar", "niveauMic", 3, initObj);
    var niveauMic:MovieClip = eval(scene+".niveauMic");
    niveauMic.setStyle("color", "0x000000");
    // Paramètre pour le Label
    initObj._x = 0;
    initObj._y = 181;
    initObj._width = 100;
    initObj._height = 22;
    initObj.text = "Niveau Micro :";
    scene.attachMovie("Label", "monLabel", 4, initObj);
    // Paramètre pour le NumericStepper
    initObj._x = 100;
    initObj._y = 181;
    initObj._width = 88;
    initObj._height = 22;
    initObj.maximum = 100;
    initObj.value = monMic.gain;
    scene.attachMovie("NumericStepper", "niveauRegl", 5, initObj);
    var niveauRegl:MovieClip = eval(scene+".niveauRegl");
    // Démarrage de la caméra
    var maCam:Object = Camera.get();
    maVideo.attachVideo(maCam);

Ici on affiche le progressBar avec es valeurs que l'on veut, même chose pour le Label et le numericStepper. On affiche aussi la webcam sur le clip vidéo.

    scene.onEnterFrame = function() {
      niveauMic.setProgress(monMic.activityLevel, 100);
    };
    // Ecouteur pour le changement du niveau Micro
    var monEcouteur:Object = new Object();
    monEcouteur.change = function(Evt:Object) {
      monMic.setGain(niveauRegl.value);
    };
    niveauRegl.addEventListener("change", monEcouteur);
  }

On crée l'enterFrame sur la scene pour changer la valeur du progressbar tout le temps suivant le niveau enregistré actuellement par le micro. On réaffecte aussi lors du changement du numericStepper le nouveau gain pour le Micro, avec un écouteur

^^ Retour ^^

livre.fla

Cette animation sert à gérer un livre d'or, l'affichage et l'ajout. Sa bibliothèqe contient tous les composants Flash dont il a besoin : 'Alert', 'Button', 'TextArea' et 'TextInput'. Au niveau du code, on a juste une ligne :

var monLivre:Livre = new Livre(this);

On appelle simplement la classe 'Livre' en créant un nouvel objet 'monLivre' sur la scène en envoyant en paramètre la référence à l'endroit où est appellé la classe.

^^ Retour ^^

livre.php

Ce fichier PHP va rechercher 10 messages disponibles dans la base de donnée, suivant les paramètres '$page' et '$nbr_ppage' qui lui sont donnés et retourne les résultats sous forme d'un XML.

include('conf.php');

$nbr_ppage = $_POST['nbpage'];
$page = $_POST['page'];

if (!isset($page)) $page = 0;

$debut = $page * $nbr_ppage;

On commence par inclure le fichier 'conf.php' qui est la configuration à la base de donnée et la classe de requêtes MySQL. Ensuite, on récupère les paramètres donnés par le Flash en POST, puis on vérifie si la variable '$page' est bien renseigné, et si ce n'est pas le cas, on l'initialise à 0. Enfin, on calcule la valeur de '$debut' pour la requête.

$sqlv = "SELECT * FROM livreflash ";
$sqlv.= " ORDER BY id_livre DESC ";
$count = mysql_num_rows($sql->query($sqlv));
$sqlv.= " LIMIT $debut,$nbr_ppage";
$req = $sql->query($sqlv);

On écrit notre requête '$sqlv', en calculant, avant d'ajouter le 'LIMIT' à la requête, le nombre de messages total. Ensuite, on exécute la requête MtSQL et on l'attribueà '$req'.

$retour = "<livre tot=\"$count\">";
while($row = mysql_fetch_array($req)) {
  $dat = explode(" ",$row['date_livre']);
  $dat1 = explode("-",$dat[0]);
  $date = "$dat1[2]/$dat1[1]/$dat1[0] ".utf8_encode("à")." $dat[1]";
  $retour.= "<msg titre=\"".$row['titre']."\" date=\"".$date."\" de=\"".$row['pseudo']."\" mail=\"".$row['mail']."\">",$row['msg'])."]]></msg>";
}
$retour.= "</livre>&";

On parcourt tous les résultats pour construire la variable '$retour' que l'on enverra à Flash.

&retour=<?=utf8_encode($retour)?>

Enfin, on écrit la variable retour pour Flash, en prenant soin de l'encoder en utf8 pour bien avoir les accents.

^^ Retour ^^

conf.php

CF conf.php, qui est le même fichier PHP.

^^ Retour ^^

livrestyle.css

C'est une feuille de style tout à fait normal, que l'on applique au 'TextArea' pour afficher les messages. Voici son contenu :

a:link {
  color: #FF0000;
}
a:hover{
  text-decoration: underline;
}
.tout {
  font-size: 12px;
  color: #00FF00;
}
.titre {
  font-size: 13px;
  font-weight: bold;
  color: #00BFFF;
}
.barre {
  color: #0000FF;
}
^^ Retour ^^

Livre.as

C'est la classe qui s'occupe d'afficher tous les éléments nécessaire à la visualisation du livre d'or et à l'ajout.

import mx.utils.Delegate;
import mx.controls.Button;
import mx.controls.TextArea;
import mx.controls.TextInput;
import mx.controls.Alert;
class Livre {
  private var scene:MovieClip;
  private var action_btn:MovieClip;
  private var pageAct:Number;
  private var pageTot:Number;
  private var loadLivre:LoadVars;
  private var styleCSS:Object;
  private var styleCharge:Boolean;
  private var envoiMess:LoadVars;
  private static var NBPAGE:Number = 10;

On commence par importé toutes les classes Flash dont on a besoin dans la classe : 'Delegate', 'Button', 'TextArea', 'textInput' et 'Alert'. Ensuite, on crée la classe et on définit les variables de la classe : 'scene' qui est la référence à l'endroit où est appellée la classe, 'action_btn' qui est la référence au bouton pour changer entre voir et ajouter, 'pageAct' est la page actuelle de visualisation, 'pageTot' est le nombre de pages total, 'loadLivre' est l'objet LoadVars de chargement des messages, 'styleCss' est l'objet dans lequel on charge la CSS, 'styleCharge' sert à savoir si le style a déjà été chargé pour ne pas le refaire, 'envoiMess' est l'objet LoadVars d'ajout de message et 'NBPAGE' paramètre le nombre de messages que l'on affiche par page.

  public function Livre(scen:MovieClip) {
    scene = scen;
    pageAct = 0;
    styleCharge = false;
    creerElementFixe();
    traceVoir();
  }

C'est le constructeur de la classe. Il initialise les variable de la classe, appelle 'creerElementFixe' pour créer le fons gris et le bouton d'action et appelle 'traceVoir' pour afficher tous les éléments pour voir les messages et affiche la 1ère page.

  private function creerElementFixe(Void):Void {
    // Création du fond de couleur gris
    scene.createEmptyMovieClip("fondGris", 1);
    var fondGris:MovieClip = eval(scene+".fondGris");
    var cl:String = "A0A0A0";
    with (fondGris) {
      moveTo(0, 0);
      lineStyle(0, "0x"+cl, 100);
      beginFill("0x"+cl, 100);
      lineTo(500, 0);
      lineTo(500, 400);
      lineTo(0, 400);
      endFill();
    }
    // Création du bouton de choix d'action
    var initObj:Object = new Object();
    initObj._x = 425;
    initObj._y = 10;
    initObj._width = 70;
    initObj.label = "Ajouter";
    // Ici on dit que notre onRelease sera la fonction annuler
    //    de notre objet Param, via Delegate.create
    initObj.onRelease = Delegate.create(this, actionBout);
    scene.createClassObject(Button, "action_btn", 2, initObj);
    action_btn = eval(scene+".action_btn");
  }

Cette fonction créer tous les élements communs aux 2 interface, le fond gris et le bouton d'action.

  private function actionBout(Void):Void {
    if (action_btn.label == "Retour") {
      traceVoir();
      action_btn.label = "Ajouter";
    } else {
      traceAjouter();
      action_btn.label = "Retour";
    }
  }

Cette fonction est appellée lorsque l'utilisateur clique sur le bouton d'action. On regarde le 'label' du bouton, et suivant le cas, on appelle les fonctions qui permettent d'afficher ce que veut l'utilisateur et on change le 'label' du bouton vers l'autre valeur.

  private function traceVoir(Void):Void {
    // Suppression de l'objet ajouter
    removeMovieClip(scene.ajouter);
    // Création du clip contenant tous nos objets à affichés
    scene.createEmptyMovieClip("livre", 3);
    // Création du TextArea pour afficher les messages
    var initObj:Object = new Object();
    initObj._x = 10;
    initObj._y = 10;
    initObj._width = 410;
    initObj._height = 380;
    initObj.editable = false;
    initObj.html = true;
    scene.livre.createClassObject(TextArea, "affichage_txt", 3, initObj);
    // Création du bouton Suivant
    var initObj:Object = new Object();
    initObj._x = 425;
    initObj._y = 330;
    initObj._width = 70;
    initObj.label = "Suivant";
    initObj._visible = false;
    // Ici on dit que notre onRelease sera la fonction annuler
    //    de notre objet Param, via Delegate.create
    initObj.onRelease = Delegate.create(this, suivant);
    scene.livre.createClassObject(Button, "suivant_btn", 4, initObj);
    initObj._y = 360;
    initObj._width = 70;
    initObj.label = "Précédent";
    // Ici on dit que notre onRelease sera la fonction annuler
    //    de notre objet Livre, via Delegate.create
    initObj.onRelease = Delegate.create(this, precedent);
    scene.livre.createClassObject(Button, "precedent_btn", 5, initObj);
    // création de l'objet de load du CSS
    if (styleCharge) {
      // On a deja chargé la CSS, on va cherché maintenant le texte
      chargeLivre();
    } else {
      scene.livre.affichage_txt.text = "Chargement de la CSS...";
      styleCSS = new TextField.StyleSheet();
      // Ici on dit que notre onLoad sera la fonction loadCSS
      //    de notre objet Livre, via Delegate.create
      styleCSS.onLoad = Delegate.create(this, loadCSS);
      styleCSS.load("livrestyle.css");
    }
  }

Cette fonction crée tous les élements pour l'affichage des messages du livre d'or : les 'TextArea' et les boutons suivant et précedent. Ensuite, si la CSS a déjà été chargée, on lance directement le chargement des messages en appellant la fonction 'chargeLivre', sinon, on charge le feuile de style, en affichant un message dans le 'TextArea' pour informer l'utilisateur de ce qui se passe.

  private function loadCSS(success:Boolean) {
    if (success) {
      scene.livre.affichage_txt.text = "Chargement du Livre d'or...";
      scene.livre.affichage_txt.styleSheet = styleCSS;
      chargeLivre();
    } else {
      scene.livre.affichage_txt.text = "Erreur de Chargement de la CSS...";
    }
  }

Cette fonction est appellée lorsque la feuille de style CSS a fini de chargée ou a échoué. Selon le cas, on affiche où en est le chargement des messages dans le 'TextArea' et si c'est bien chargé, on appelle la fonction 'chargeLivre' pour lancer le chargement des messages. On met aussi la variable 'styleCharge' à true pour ne pas recharger la CSS ensuite.

  private function chargeLivre(Void):Void {
    // On applique la CSS
    scene.livre.affichage_txt.styleSheet = styleCSS;
    loadLivre = new LoadVars();
    // Ici on dit que notre onLoad sera la fonction affLivre
    //    de notre objet Livre, via Delegate.create
    loadLivre.onLoad = Delegate.create(this, affLivre);
    loadLivre.page = pageAct;
    loadLivre.nbpage = NBPAGE;
    loadLivre.sendAndLoad("livre.php", loadLivre, "POST");
  }

Cette fonction sert à lancer le chargement des messages du livre d'or. On commence par réappliquer la feuille de style, puis on lance le chargement du PHP dans l'objet 'loadLivre', en envoyant la page que l'on veut 'pageAct' et le nombre par page 'NBPAGE'.

  private function affLivre(succes:Boolean):Void {
    if (succes) {
      var monLivre:XML = new XML(loadLivre.retour);
      monLivre.ignoreWhite = true;
      var mesMessages:Array = monLivre.firstChild.childNodes;
      var affiche:String = "<span class=\"tout\">";
      for (var i = 0; i<mesMessages.length; i++) {
        affiche += "<span class=\"titre\">"+mesMessages[i].attributes.titre+"</span>";
        affiche += " par <a href=\"mailto:"+mesMessages[i].attributes.mail+"\">";
        affiche += mesMessages[i].attributes.de+"</a>";
        affiche += " le "+mesMessages[i].attributes.date+"<br>";
        affiche += mesMessages[i].firstChild.nodeValue;
        affiche += "<br><span class=\"barre\">------------------------------------------------------------------</span><br>";
      }
      affiche += "</span>";
      scene.livre.affichage_txt.text = affiche;
      pageTot = monLivre.firstChild.attributes.tot;
      if (monLivre.firstChild.attributes.tot>(pageAct+1)*NBPAGE) {
        scene.livre.suivant_btn._visible = true;
      } else {
        scene.livre.suivant_btn._visible = false;
      }
      if (pageAct>0) {
        scene.livre.precedent_btn._visible = true;
      } else {
        scene.livre.precedent_btn._visible = false;
      }
    } else {
      scene.livre.affichage_txt.text = "Erreur de chargement des messages du livre d'or";
    }
  }

Cette fonction est appelée lorsque le PHP qui donne les messages de la page souhaitée à fini de chargée. Si le chargement s'est bien passé, on commence par créer un nouvel objet XML 'monLivre', auquel on donne la valeur de la variable 'loadLivre.retour' donnée par le PHP. En faisant ça, 'monLivre' va parser toute la variable 'loadLivre.retour' pour en fair un XML à part entière. Ensuite on parcourt comme un autre XML 'monLivre', en ajoutant à chaque fois la ligne à la variable 'affiche', en insérant les balises HTML que l'on veut. Une fois le parcourt du xml terminé, on applique la variable 'affiche' au champ de texte HTML.
Enfin, on fait quelques tests pour savoir s'il doit afficher les boutons suivants et précédents pour la navigation entre les pages.

  private function suivant(Void):Void {
    if (pageAct<pageTot) {
      pageAct++;
      chargeLivre();
    }
  }

Cette fonction est appellée lors du clic sur le bouton suivant. On commence par vérifier s'il y a bien une page après, et si c'est le cas, on incrémente la valeur de 'pageAct' et on appelle la fonction 'chargeLivre' qui va aller chercher les messages de la page suivante.

  private function precedent(Void):Void {
    if (pageAct>0) {
      pageAct--;
      chargeLivre();
    }
  }

Idem que la fonction précédente, sauf qu'on va à la page précédente.

  private function traceAjouter(Void):Void {
    // Suppression de l'objet Livre
    removeMovieClip(scene.livre);
    // Création du clip contenant tous nos objets à affichés
    scene.createEmptyMovieClip("ajouter", 3);
    // Création des case à remplir
    var initObj:Object = new Object();
    // constant pour tous
    initObj._x = 10;
    initObj._width = 250;
    initObj._height = 22;
    // le titre
    initObj._y = 10;
    initObj.tabIndex = 1;
    scene.ajouter.createClassObject(TextInput, "titre", 4, initObj);
    scene.ajouter.createTextField("titre_txt", 5, initObj._x+initObj._width, initObj._y, 100, initObj._height);
    scene.ajouter.titre_txt.selectable = false;
    scene.ajouter.titre_txt.text = ": Titre";
    // le pseudo
    initObj._y += 30;
    initObj.tabIndex++;
    scene.ajouter.createClassObject(TextInput, "pseudo", 6, initObj);
    scene.ajouter.createTextField("pseudo_txt", 7, initObj._x+initObj._width, initObj._y, 100, initObj._height);
    scene.ajouter.pseudo_txt.selectable = false;
    scene.ajouter.pseudo_txt.text = ": Pseudo";
    // le mail
    initObj._y += 30;
    initObj.tabIndex++;
    scene.ajouter.createClassObject(TextInput, "mail", 8, initObj);
    scene.ajouter.createTextField("mail_txt", 9, initObj._x+initObj._width, initObj._y, 100, initObj._height);
    scene.ajouter.mail_txt.selectable = false;
    scene.ajouter.mail_txt.text = ": Mail";
    // Création du TextArea pour le message
    initObj._y += 30;
    initObj.tabIndex++;
    initObj._width = 480;
    initObj._height = 290;
    scene.ajouter.createClassObject(TextArea, "msg", 10, initObj);
    // Bouton ajouter
    initObj._x = 425;
    initObj._y -= 30;
    initObj.tabIndex++;
    initObj._width = 70;
    initObj._height = 22;
    initObj.label = "Ajouter";
    // Ici on dit que notre onRelease sera la fonction annuler
    //    de notre objet Param, via Delegate.create
    initObj.onRelease = Delegate.create(this, ajouter);
    scene.ajouter.createClassObject(Button, "ajouter_btn", 11, initObj);
  }

Cette fonction sert à créer tous les élements qui permettent l'ajout d'une signature dans le livre d'or. Comme toutes les fonctions utilisées ici ont déjà été commentées, on passe directement à la suite.

  private function ajouter(Void):Void {
    var erreur:Array = new Array();
    if (scene.ajouter.titre.text.length<5) {
      erreur.push("Le titre est trop petit.");
    }
    if (scene.ajouter.pseudo.text.length<3) {
      erreur.push("Le pseudo est trop petit.");
    }
    if (scene.ajouter.mail.text.length<10) {
      erreur.push("Le mail est trop petit.");
    } else if (scene.ajouter.mail.text.indexOf("@") == -1 || scene.ajouter.mail.text.indexOf(".") == -1) {
      erreur.push("Le mail est invalide.");
    }
    if (scene.ajouter.msg.text.length<20) {
      erreur.push("Le message est trop petit.");
    }
    if (erreur.length>0) {
      // Il y a au moins 1 erreur
      Alert.show(erreur.join("\n"), "Erreurs dans les champs", Alert.OK);
    } else {
      scene.ajouter.ajouter_btn.enabled = false;
      envoiMess = new LoadVars();
      envoiMess.titre = scene.ajouter.titre.text;
      envoiMess.pseudo = scene.ajouter.pseudo.text;
      envoiMess.mail = scene.ajouter.mail.text;
      envoiMess.msg = scene.ajouter.msg.text;
      envoiMess.sendAndLoad("ajoutLivre.php", envoiMess, "POST");
      // Ici on dit que notre onLoad sera la fonction envoiLoad
      //    de notre objet Param, via Delegate.create
      envoiMess.onLoad = Delegate.create(this, envoiLoad);
    }
  }

Cette fonction, appellée lors du clic sur le bouton envoyer du formulaire d'ajout de message, commence par vérifier que tous les champs ont une longueur pas trop petite. On vérifie aussi que l'email a bien un '@' et un '.' pour que l'adresse mail soit valide. Si une erreur survient, on ajoute dans le tableau 'erreur' la description de l'erreur.
Ensuite, si le tableau 'erreur' n'est pas vide, on affiche toutes les erreurs dans une 'Alert'. Sinon, on prépare l'envoi des variables dans un LoadVars 'envoiMess' en définissant toutes les variables que l'on enverra, puis on fait l'envoi vers 'ajoutLivre.php' en POST. Enfin, on appelera la fonction 'envoiLoad' lorsque le PHP sera fini de charger.

  private function envoiLoad(succes:Boolean):Void {
    if (succes) {
      if (envoiMess.res=0) {
        scene.ajouter.ajouter_btn.enabled = true;
        Alert.show(envoiMess.erreur, "Erreurs PHP", Alert.OK);
      } else {
        // Le message a bien été envoyé, on réaffiche le Livre en commençant à 0
        pageAct = 0;
        actionBout();
      }
    } else {
      trace("Erreur d'envoi du message");
    }
  }

Cette fonction, appellée lorsque le fichier PHP d'envoi est fini de charger, vérifie la valeur 'res' retournée par le PHP. Si elle vaut 0, c'est qu'une erreur s'est produite; alors, on réactive le bouton d'envoi et on affiche une 'Alert'. Sinon, on remet la variable 'pageAct' à 0, puis on fait comme si l'utilisateur cliquait sur le bouton d'action pour recharger les messages. Ainsi, l'utilisateur verra directement son message, en 1er, puisque le PHP retourne les messages classés du plus récent au plus ancien.

Cette fonction, appellée lorsque le fichier PHP d'envoi est fini de charger, vérifie la valeur 'res' retournée par le PHP. Si elle vaut 0, c'est qu'une erreur s'est produite; alors, on réactive le bouton d'envoi et on affiche une 'Alert'. Sinon, on remet la variable 'pageAct' à 0, puis on fait comme si l'utilisateur cliquait sur le bouton d'action pour recharger les messages. Ainsi, l'utilisateur verra directement son message, en 1er, puisque le PHP retourne les messages classés du plus récent au plus ancien.

^^ Retour ^^

livreflash.sql

Ce fichier sert à créer la table de donnée MySQL pour enregistrer les messages du Livre d'or.

CREATE TABLE livreflash (
  id_livre int not null auto_increment primary key,
  titre varchar(200) not null default'',
  pseudo varchar(200) not null default'',
  mail varchar(200) not null default'',
  msg text not null default'',
  date_livre datetime not null default'0'
);

On a donc besoin d'un identifiant (tojours utile), du titre du message, du pseudo de la personne, du mail de la personne, de son message et de la date du message.

^^ Retour ^^

ajoutLivre.php

Ce fichier, auquel on envoi le pseudo du joueur, le niveau et le nombre de coups sert à enregistrer le score dans la base de donnée.

include('conf.php');

$titre = utf8_decode($_POST['titre']);
$pseudo = utf8_decode($_POST['pseudo']);
$mail = utf8_decode($_POST['mail']);
$msg = utf8_decode($_POST['msg']);

$erreur = "";
$res = 0;

On commence par inclure le fichier de configuration pour la base de donnée et de création de la classe sqlqui permet de faire des requêtes sur la base de donnée. Puis, on récupère les variables envoyées par le Flash en POST, en les décodants. Enfin, on initalise les variables '$erreur' et '$res'.

if ($titre != "" && $pseudo != "" && $mail != "" && $msg != "") {
  $date = date("Y-m-d H:i:s");
  $champs = "`titre`,`pseudo`,`mail`,`msg`,`date_livre`";
  $valeurs = "\"$titre\",\"$pseudo\",\"$mail\",\"$msg\",\"$date\"";
  $req = $sql->insert("livreflash",$valeurs,$champs);
  if ($req) {
    // Ajout réussi
    $res = 1;
  } else {
    $erreur = "Erreur MySQL.";
  }
} else {
  $erreur = "Champs manquants.";
}

Ensuite, on vérifie que tous les champs sont fournis. Si c'est le cas, on fait l'ajout dans la base de donnée, et si tout se passe bien, on définit la variable '$res' à 1. Si une erreur survient, on définit la variable '$erreur' avec la description de l'erreur qui est survenu pour pouvoir l'afficher dans Flash.

echo "&res=$res&erreur=$erreur&";

Enfin, on écrit les variables '$res' et $retour' pour que Flash puisses les exploiter.

^^ Retour ^^

traduction.fla

Cette animation flash permet de traduire des textes dans une langue dans une autre langue, en utilisant un WSDL de BabelFish, trouvé sur www.xmethods.com : http://www.xmethods.net/sd/BabelFishService.wsdl.
A noter que cette traduction ne marche que sur Internet Explorer, sans doute à cause du plugin qui n'est pas le même.
Dans la bibliothèque, il y a juste les composants dont on se sert dans l'animation : 'Button', 'ComboBox', 'Label', 'TextArea' et 'WebServiceConnector'.

var maTraduction:Traduction = new Traduction(this);

On appelle simplement la classe 'Traduction' en créant un nouvel objet 'maTraduction' sur la scène en envoyant en paramètre la référence à l'endroit où est appellé la classe.

^^ Retour ^^

Traduction.as

Cette classe affiche tous les éléments nécessaire à la traduction : 'TextArea' pour entrer le texte à traduire et un autre pour afficher le texte traduit, une 'ComboBox' pour choisir le mode de traduction, un 'Button' pour lancer la traduction et des 'Label' pour décrire les 'TextArea' et afficher l'état de la traducion.

import mx.utils.Delegate;
class Traduction {
  private var scene:MovieClip;
  private var traduc:String = "fr_en";
  function Traduction(scen:MovieClip) {
    scene = scen;
    System.security.loadPolicyFile("http://www.xmethods.net/crossdomain.xml");
    System.security.loadPolicyFile("http://services.xmethods.net/crossdomain.xml");
    creerElementFixe();
  }

On commence par importer la classe Delegate car on en aura besoin, puis on déclare les variables de la classe : 'scene' qui est la référence à l'endroit où est appelée la classe et 'traduc' qui décrit le mode de traduction, de la façon dont le demande BabelFish.
Ensuite, la constructeur de la classe charge les 'crossdomain.xml' de façon à ce que le Player Flash (à partir de la version 7) aie les autorisations pour charger les différentes choses dont le WSDL se servira pour traduire.
Puis, on appelle la fonction 'creerElementFixe' qui affichera tous les éléments de l'animation.

  function creerElementFixe(Void):Void {
    var prof:Number = 1;
    scene.createEmptyMovieClip("fondGris", prof);
    prof++
    var cl:String = "A0A0A0";
    with (scene.fondGris) {
      moveTo(0, 0);
      lineStyle(0, "0x"+cl, 100);
      beginFill("0x"+cl, 100);
      lineTo(420, 0);
      lineTo(420, 390);
      lineTo(0, 390);
      endFill();
    }
    var initObj:Object = new Object();
    initObj._x = 10;
    initObj._y = 5;
    initObj._height = 22;
    initObj.text = "Texte à traduire :";
    scene.attachMovie("Label", "aTraduire_txt", prof, initObj);
    prof++;
    initObj._y += 20;
    initObj.text = "";
    initObj._width = 400;
    initObj._height = 150;
    initObj.html = false;
    scene.attachMovie("TextArea", "aTraduire", prof, initObj);
    prof++;
    initObj._y += initObj._height+5;
    initObj._width = 150;
    initObj._height = 22;
    initObj.labels = Array("Français > Anglais", "Anglais > Français", "Anglais > Allemand", "Allemand > Anglais", "Anglais > Espagnol", "Espagnol > Anglais", "Anglais > Italien", "Italien > Anglais", "Anglais > Portugais", "Portugais > Anglais", "Russe > Anglais");
    initObj.data = Array("fr_en", "en_fr", "en_de", "de_en", "en_es", "es_en", "en_it", "it_en", "en_pt", "pt_en", "ru_en");
    scene.attachMovie("ComboBox", "choixTraduc", prof, initObj);
    var monEcout:Object = new Object();
    monEcout.change = Delegate.create(this, chxLangue);
    scene.choixTraduc.addEventListener("change", monEcout);
    prof++;
    initObj.labels = undefined;
    initObj.data = undefined;
    initObj._x += initObj._width+10;
    initObj._width = 100;
    initObj.label = "Traduire";
    scene.attachMovie("Button", "traduire_btn", prof, initObj);
    monEcout.click = Delegate.create(this, traduire);
    scene.traduire_btn.addEventListener("click", monEcout);
    prof++;
    initObj._x += initObj._width+10;
    initObj.text = "";
    scene.attachMovie("Label", "etat_txt", prof, initObj);
    prof++;
    initObj.label = undefined;
    initObj._x = 10;
    initObj._y += initObj._height+5;
    initObj.text = "Texte traduit :";
    scene.attachMovie("Label", "traduit_txt", prof, initObj);
    prof++;
    var initObj2:Object = new Object();
    initObj2._x = 10;
    initObj2._y = initObj._y+20;
    initObj2._width = 400;
    initObj2._height = 150;
    initObj2.wordWrap = true;
    scene.attachMovie("TextArea", "traduit", prof, initObj2);
    prof++;
    initObj.WSDLURL = "http://www.xmethods.net/sd/BabelFishService.wsdl";
    scene.attachMovie("WebServiceConnector", "connecteur", prof, initObj);
    monEcout.send = Delegate.create(this, envoi);
    scene.connecteur.addEventListener("send", monEcout);
    monEcout.result = Delegate.create(this, resultat);
    scene.connecteur.addEventListener("result", monEcout);
    prof++;
  }

Quasiment rien de nouveau dans cette classe, elle fait juste des attachMovie sur le composants et les pose sur la scene.
On le fait aussi pour le composant 'WebServiceConnector' qui est invisible, mais sert à appeler le WSDL. on lui renseigne en paramètre 'WSDLURL' qui est l'adresse du WSDL sur internet, et on lui applique des écouteurs lors de l'envoi 'send' et lorsque le résultat est arrivé 'result'.

  private function chxLangue(evt:Object):Void {
    traduc = evt.target.value;
  }

Cette fonction est appellé lorsque l'utilisateur change le choix de la langue. On se contente simplement d'appliquer la valeur du type de traduction choisi à la variable d'instance 'traduc'.

  private function envoi(evt:Object):Void {
    scene.etat_txt.text = "En cours";
  }

Cette fonction, appelée lorsque le 'WebServiceConnector' commence à envoyer les valeurs au WSDL, affiche simplement dans le 'Label' 'etat_txt' les mots 'En cours' pour informer l'utilisateur que l'envoi de la traduction a bien commencé.

  private function resultat(evt:Object):Void {
    scene.traduit.text = scene.connecteur.results;
    scene.etat_txt.text = "";
  }

Cette fonction, appelée lorsque le 'WebServiceConnector' reçoit la réponse du WSDL, affiche le résultat dans le 'TextArea' 'traduit', puis efface le texte que l'on a mis sur le 'Label' affichant l'état de la traduction.

  private function traduire(evt:Object):Void {
    var obj:Object = new Object();
    obj.sourcedata = scene.aTraduire.text;
    obj.translationmode = traduc;
    scene.connecteur.operation = "BabelFish";
    scene.connecteur.params = obj;
    scene.connecteur.trigger();
  }

Cette fonction, appelée lors du clic sur le bouton traduire, réaffecte les valeurs 'sourcedata' avec le texte à traduire, 'translationmode' avec le mode de traduction sélectionné au paramètres envoyés au WSDL. On réaffecte aussi l'opération que l'on souhaite faire : 'BabelFish' (on est obliger de le refaire à chaque fois sinon le WebService ne fonctionne pas).
Enfin on lance le chargement du WebService avec la méthode 'trigger' de celui-ci.

^^ Retour ^^

whois.fla

Cette animation flash permet d'afficher les whois des sites internet, en utilisant un WSDL de Flash-DB, trouvé sur www.flash-db.com : http://www.flash-db.com/services/ws/flashWhois.wsdl.
Dans la bibliothèque, il y a juste les composants dont on se sert dans l'animation : 'Button', 'TextInput', 'Label', 'TextArea' et 'WebServiceConnector'.

var monWhois:Whois = new Whois(this);

On appelle simplement la classe 'Whois' en créant un nouvel objet 'monWhois' sur la scène en envoyant en paramètre la référence à l'endroit où est appellé la classe.

^^ Retour ^^

Whois.as

Cette classe affiche tous les éléments nécessaire eu Whois : 'TextInput' pour entrer le domaine dont on veut le whois et un 'TextArea' pour afficher le Whois retourné, un 'Button' pour lancer le Whois et un 'Label' pour écrire 'domaine :'.

import mx.utils.Delegate;
class Whois {
  private var scene:MovieClip;
  function Whois(scen:MovieClip) {
    scene = scen;
    System.security.loadPolicyFile("http://www.flash-db.com/crossdomain.xml");
    creerElementFixe();
  }

On commence par importer la classe Delegate car on en aura besoin, puis on déclare les variables de la classe : 'scene' qui est la référence à l'endroit où est appelée la classe.
Ensuite, la constructeur de la classe charge le 'crossdomain.xml' de façon à ce que le Player Flash (à partir de la version 7) aie les autorisations pour charger les différentes choses dont le WSDL se servira pour sortir le Whois.
Puis, on appelle la fonction 'creerElementFixe' qui affichera tous les éléments de l'animation.

  function creerElementFixe(Void):Void {
    var prof:Number = 1;
    scene.createEmptyMovieClip("fondGris", prof);
    prof++;
    var cl:String = "A0A0A0";
    with (scene.fondGris) {
      moveTo(0, 0);
      lineStyle(0, "0x"+cl, 100);
      beginFill("0x"+cl, 100);
      lineTo(420, 0);
      lineTo(420, 390);
      lineTo(0, 390);
      endFill();
    }
    var initObj:Object = new Object();
    initObj._x = 10;
    initObj._y = 5;
    initObj._height = 22;
    initObj._width = 100;
    initObj.text = "Domaine :";
    scene.attachMovie("Label", "domaine_txt", prof, initObj);
    prof++;
    initObj._x += initObj._width+195;
    initObj._width = 100;
    initObj.label = "Who is ?";
    scene.attachMovie("Button", "whois_btn", prof, initObj);
    var monEcout:Object = new Object();
    monEcout.click = Delegate.create(this, action);
    scene.whois_btn.addEventListener("click", monEcout);
    prof++;
    var initObj2:Object = new Object();
    initObj2._x = 10;
    initObj2._y = initObj._y+25;
    initObj2._width = 395;
    scene.attachMovie("TextInput", "domaine", prof, initObj2);
    prof++;
    initObj2._y += 25;
    initObj2._height = 330;
    initObj2.wordWrap = true;
    initObj2.html = false;
    initObj2.editable = false;
    scene.attachMovie("TextArea", "whois_txt", prof, initObj2);
    prof++;
    initObj.WSDLURL = "http://www.flash-db.com/services/ws/flashWhois.wsdl";
    scene.attachMovie("WebServiceConnector", "connecteur", prof, initObj);
    monEcout.send = Delegate.create(this, envoi);
    scene.connecteur.addEventListener("send", monEcout);
    monEcout.result = Delegate.create(this, resultat);
    scene.connecteur.addEventListener("result", monEcout);
    prof++;
  }

Quasiment rien de nouveau dans cette classe, elle fait juste des attachMovie sur le composants et les pose sur la scene, la même chose que pour la classe 'Traduction'.
On le fait aussi pour le composant 'WebServiceConnector' qui est invisible, mais sert à appeler le WSDL. on lui renseigne en paramètre 'WSDLURL' qui est l'adresse du WSDL sur internet, et on lui applique des écouteurs lors de l'envoi 'send' et lorsque le résultat est arrivé 'result'.

  private function envoi(evt:Object):Void {
    scene.whois_txt.text = "En cours..";
    scene.whois_txt.hPosition = 0;
    scene.whois_txt.vPosition = 0;
  }

Cette fonction, appelée lorsque le 'WebServiceConnector' commence à envoyer les valeurs au WSDL, affiche simplement 'en cours...' dans le 'TextArea' et remet ses scrollers au début.

  private function resultat(evt:Object):Void {
    scene.whois_txt.text = scene.connecteur.results;
  }

Cette fonction, appelée lorsque le 'WebServiceConnector' reçoit la réponse du WSDL, affiche le résultat dans le 'TextArea' 'whois_txt'

  private function action(evt:Object):Void {
    var obj:Object = new Object();
    obj.username = "nyro";
    obj.password = "cedric";
    obj.domain = scene.domaine.text;
    scene.connecteur.operation = "doWhois";
    scene.connecteur.params = obj;
    scene.connecteur.trigger();
  }

Cette fonction, appelée lors du clic sur le bouton 'Whois ?', réaffecte les valeurs 'username' et 'password' avec les identifiants sur lesite flash-db.com et 'domain' qui est en fait ce qu'a rentré l'utilisateur dans le 'TextInput'. On réaffecte aussi l'opération que l'on souhaite faire : 'doWhois' (on est obliger de le refaire à chaque fois sinon le WebService ne fonctionne pas).
Enfin on lance le chargement du WebService avec la méthode 'trigger' de celui-ci.