lundi, octobre 24 2005

Pas le temps... mais pourquoi ?

Eh oui, je viens de le dire, je n'ai pas beaucoup de temps pour faire de la veille technologique en ce moment... En fait, il s'agit surtout de finir mes projets actuels, afin de prendre un nouveau départ!!!

En fait, je quite bientôt mon employeur actuel (avec une petite larme à l'oeil, CTC va me manquer quand-même :(), pour rejoindre l'équipe de Centile (avec la banane aux lèvres :D) pour un projet passionnant! Au coeur de ce que l'on peut appeler un "paradis pour développeurs" (y a que ca là-bas) dans ce qui semble être une super ambiance, ca va coder dur, et surtout en ActionScript (ben oui, malgré les nombreuses années depuis lesquelles je code en AS, c'est quand-même la première fois que c'est un travail a plein-temps, non freelance :P). Une nouvelle compétence chez eux, qui utilisaient plutôt des langages plus "institutionnels" (Visual, Java, ...). Bon, il faut avouer que celui qui a fait la première maquette est carrément balaise quand-même! 8)

Au programme, connexion serveur temps réel et applications "standalone" de type mProjector... Je boût d'impatience! :mrgreen:

Pas le temps ! Pas le temps !

Argh... Que de nouveautés... Je vous passerai l'annonce de la sortie de AS3 avec l'alpha1 de Flex 2 avec quatre train de retards... :$ Pensez aussi que MySQL5 vient de sortir, tout en entrant dans la cours des grands avec triggers et procédures stockées, de quoi s'assurer de modèles de données robustes...

Bon, j'espère que je trouverai bientôt un peu de temps aprés avoir fini le TAF en cours pour me lancer dans l'AS3 (j'en suis encore au stade de dessiner un carré, vive drawRect() :mrgreen:)...

lundi, octobre 10 2005

ActionScript 3 : oulala...

Etant donné l'émulsion de la blogosphère Flash, ce post ne vous apprendra rien. Vraisemblablement, le Saint Graal des développeurs AS s'apprête à pointer le bout de son nez, j'ai nommé : ActionScript 3! Mike Chambers a eu beau nous certifier durant le FlashFestival que promis, juré, craché aucune version 3 de l'AS n'était en préparation, il est là quand-même... :P

Cette nouvelle version du langage est associée à la plate-forme Flex 2 et à une version 8.5 du player, mais il reste encore assez compliqué de comprendre sous quelle forme...

Tout d'abord, voyons ce que propose l'AS3 :

  • Des meilleures performances, grâce à une nouvelle machine virtuelle. Le player 8.5 verra donc cohabiter deux machines virtuelles, augmentant ainsi son poids. Bon, on va pas râler non plus, avec nos connexions ADSL pour la plupart, on peut bien attendre quelques secondes de plus pour avoir la compatibilité ascendante avec AS1/2... :)
  • Un environnement de développement basé sur Eclipse, anciennement "code-nommé" Zorn. Un vrai rien que pour les codeurs et tout, et tout...
  • La norme ECMAScript respectée à la lettre. On va pouvoir faire des private/protected, et tout le toutim...
  • Le XML supporté nativement. Ca c'est cool, mais j'ai pas trop compris ce qui changeait (pitet' parce que je n'ai pas regardé ce qu'est l'E4X :$)
  • A priori (mais j'ai pas non plus tout décortiqué), une structure du framework complètement revue. Reste à voir ce que ça va donner. Personnellement, j'espère qu'on aura enfin le droit à un new MovieClip();... J'espère aussi que le typage par interfaces a été plus largement utilisé...
  • Ma "top feature" à moi, la gestion des sockets binaires et des expressions régulières (rationnelles pour les puristes)! 8) Les deux combinés vont pouvoir donner naissance à des serveurs optimisés à mort au niveau du poids des données transférées dans le cadre d'une connexion socket.
  • Et plein d'autres trucs qu'ils sont super trop bien...

Par contre, la disponibilité du langage reste assez floue... Je n'ai toujours pas bien compris comment avec quels outils on va bien pouvoir code. :\ On parle d'une version 8.5 de l'IDE Flash, alors que Mike Chambers n'annonce sa dispo sur FlashCoders que dans une version 9 dont la période de sortie est floue, ce qui fait que je ne comprends pas vraiment comment l'on va bien pouvoir harmoniser les numéros de version du player et de l'IDE... Moi, je me rangerais plus du côté de quelques commentaires qui disent qu'ils auraient mieux fait de laisser l'AS3 dans l'IDE Flash (ben oui, on a vu qu'il y était dans la démo 8ball de Mike au FlashFestival :P), quitte à sortir la version 8 un peu plus tard, plutôt que de l'enlever à l'arrache pour paumer un peu tout le monde...
On parle aussi de compilation AS3 depuis Zorn via du MXML qui génèrera du SWF, mais aucune compatibilité AS2/1, merci pour ceux qui voulaient en faire encore un peu... Bref, c'est un peu la panade à ce niveau là, et si quelqu'un pouvait éclairer tout le monde en réunissant des infos CLAIRES (outils, versions, utilisation, et pourquoi pas dates?) concernant les outils de développement à venir, ça devrait faire plaisir à pas mal de monde je pense... ;)

En attendant le 17 Octobre pour une version alpha, il ne nous reste qu'à ronger notre frein...

lundi, septembre 26 2005

FileLink : Téléchargement de fichier et menu contextuel

En tant qu'utilisateur, je trouve parfois assez exaspérant de devoir ouvrir, par exemple, un fichier PDF dans mon navigateur. Je préfère largement le télécharger et l'ouvrir avec Acrobat Reader...

Lorsqu'il s'agit d'un lien dans une page HTML, il est facile d'utiliser un clic droit (ou CTRL pour les maceux) pour pouvoir choisir entre l'ouvrir réellement ou le télécharger. Par contre, dans un site Flash c'est plus délicat...

C'es pourquoi j'ai créé vite-fait une petite classe qui permet d'assigner à un clip un menu contextuel qui va permettre soit d'ouvrir, soit de télécharger un fichier déterminé. Elle est basique pour l'instant, mais l'on peut prévoir de la faire évoluer avec quelques options (localisation, cible du getURL, comportement par défaut du onRelease, etc...). :) A noter que le téléchargement ne fonctionne que dans un environnement HTTP (cela est du aux restrictions de la classe FileReference), donc on oublie le test dans l'IDE Flash ou dans une page HTML accédée directement depuis le disque dur... :(import flash.net.FileReference;
import mx.utils.Delegate;
/**
 * FileLink
 *
 * @notice Can only be used in HTTP environment due to FileReference restrictions
 * @author  <a href="http://www.lalex.com/">LAlex</a>
 * @version 1.0
 * @since   
 */

class com.lalex.utils.FileLink {
        // File path
        private var _path:String;
        function FileLink(f:String) {
                _path = f;
        }
        /**
         * Assign a link to a movieclip, setting its context menu
         *
         * @usage   fLink.assignTo(myLinkButton, true, true);
         * @param   clip       MovieClip to click to obtain download menu
         * @param   setRelease Set the onRelease handler of the clip ?
         * @param   hide       Hide context menu builtin items ?
         */

        function assignTo(clip:MovieClip, setRelease:Boolean, hide:Boolean) {
                hide = !!hide;
                setRelease = !!setRelease;
                var dl:Function = Delegate.create(this, this.download);
                var op:Function = Delegate.create(this, this.open);
                var m:ContextMenu = new ContextMenu();
                m.customItems.push(new ContextMenuItem("Ouvrir", op));
                m.customItems.push(new ContextMenuItem("Télécharger", dl));
                if (hide)
                        m.hideBuiltInItems();
                if (setRelease)
                        clip.onRelease = op;
                clip.menu = m;
        }
        /**
         * Open a file in the browser
         * Used by a proxy (Delegate) function
         */

        private function open() {
                getURL(_path);
        }
        /**
         * Dowload the file to the local computer
         * Used by a proxy (Delegate) function
         */

        private function download() {
                var f:FileReference = new FileReference();
                f.download(_path);
        }
}
Et voici son utilisation (celle du bouton ci-dessous)import com.lalex.utils.FileLink;
var fl:FileLink = new FileLink("files/FileLink.zip");
fl.assignTo(bt, true, true);




::Télécharger FileLink.zip::

mardi, septembre 20 2005

Flash 8 loves IE... not me!

Il existe la possibilité dans Windows XP de minimiser la présence de IE pour favoriser celle de navigateurs alternatifs (mettons Firefox par exemple :mrgreen:). Par exemple en changeant le navigateur par défaut, puis en désactivant l'accés à IE dans la configuration des programmes par défaut, présente depuis le SP1.

Seulement, tous les softs ne tiennent pas forcément compte de cela, et forcent l'utilisation de IE. Et quelle déception de voir que Flash est de ceux-là. Allez dans l'aide, et cliquez sur "View comments on LiveDocs"... C'est bien le navigateur Microsoftien qui s'ouvre, quel que soit votre configuration... :\

SuperColor devient XColor pour Flash 8

Pour le retour de mon blog, quelque peu perturbé par un changement de serveur (mais qui pulse bien, ca valait le coup :P), je vous propose une version Flash 8 de ma classe SuperColor, qui devient pour l'occasion XColor (pour eXtended Color).

En effet, comme vous l'avez peut-être déjà vu, la classe Color est déclarée deprecated dans Flash 8. Et comme j'aime pas spécialement trop changer mes habitudes de codage (surtout quand elles sont efficaces), j'ai donc refactorisé la classe pour qu'elle puisse être utilisée strictement de la même manière dans Flash 8, sans utiliser une classe qui est à priori vouée à disparaitre... Il s'agissait donc de recréer le comportement de Color en utilisant sa remplacante : ColorTransform!

Etant donné que ma classe héritait de Color, il m'a alors suffit de réimplémenter les méthodes setTransform, getTransform, setRGB et getRGB pour que tout remarche exactement comme avant. C'est reparti pour du Tween facile sur les couleurs! 8)
Par contre, cette classe ne marche que sur Flash 8, pour les "MX2004iens", il suffit de continuer à utiliser SuperColor... ;)

::Télécharger XColor.zip::

mardi, septembre 13 2005

Flash 8 : Premières impressions

Aprés quelques légères heures passées dans les différents packages de Flash 8, voici mes premières impressions sur la partie framework. Je n'ai encore rien compilé pour l'instant, alors je peux sans doute faire pas mal d'erreurs... :$

  • SEPY ne compile plus dans l'IDE Flash sur un CTRL+ENTREE :\ (mais bon, pas la faute a Macromedia ca quand-même :))
  • De bonnes idées, telles que les classes Point, Rectangle et Matrix en intrinsic. Il va quand-même falloir songer a les étendre pour avoir plus de possibilités...
  • La classe Matrix qui me décoit pas mal : uniquement dédiée aux transformations 2D, il est impossible d'accéder aux trois données du bas. A moins qu'il soit possible de hacker tout cas avec des ASSetPropFlags ou autre... :o L'idéal aurait été une classe Matrix "de base" et une classe TransformMatrix.
  • La classe Rectangle présente pas mal de fonctionnalités sympas. L'avoir en intrinsic permet d'obtenir des performances appréciables. A priori, les développeurs de jeux vont s'en donner à coeur joie... :P
  • La classe Color est deprecated... j'en fais quoi de ma classe SuperColor moi alors? :( Bon, je vais devoir effectuer un bon refactoring moi... :P
  • Cool la classe Transform! 8) Pouvoir appliquer tout un ensemble de transformations d'un coup, c'est bien pratique. Reste a créer des propriétés virtuelles pour pouvoir faire des Tween dessus! :P
  • Je ne me suis pas encore penché sur la manipulation de Bitmaps, mais ca promet...
  • Une feature pas vraiment mise en avant est pourtnant bien pratique. Il s'agit de la scale9Grid. En gros, ca permet de redimensionner un clip sans déformer les bords en le divisant en 9 parties (bon, je sais, c'est un gros raccourci :$). Et tout ca, accessible depuis l'IDE ou depuis ActionScript.
  • L'apparition de la méthode clone() dans les nouvelles classes, ca c'est cool! 8)
  • L'évenement onHTTPStatus est bien pratique pour les LoadVars et autres XML. Par contre, a priori je n'ai pas vu de possibilité d'interrompre un transfert... :o

A priori, tout ca se présente trés bien donc. 8)

Flash 8

Bon, c'est bon, il est sorti, voilà! Pour cause de sortie de Flash 8, je pense que je vais devenir associable pendant au moins une semaine.... :mrgreen: J'ai pas fait partie de la beta moi! :$ Alors je me plonge dans les entrailles de la bête... :P

mardi, septembre 6 2005

LAlex.com propose ses services

Aprés une loooooongue reflexion, et également un petit coup de pouce de mon employeur, je propose dorénavant mes services en tant que développeur, consultant ou formateur en développement web, principalement (voire exclusivement) concernant Flash et PHP. Cette nouvelle direction dans ma vie professionnelle s'accompagne d'un mini-site sur LAlex.com.

En effet, je dispose dorénavant au sein de mon entreprise de toute la lattitude nécessaire pour travailler comme freelance. Je gère l'ensemble de mes prestations, de la commande à la facturation, et la société qui m'emploie se contente uniquement d'apposer son nom sur la facture. Et qui plus est, je travaille aux horaires de bureau (entre autres :P), ce qui fait que je peux me consacrer à plein temps aux projets qui me sont confiés, assurant ainsi des conditions de travail optimales (ce qui ne peut qu'être bénéfique à la qualité du travail fourni), et des délais "normaux".

Bref, maintenant je n'ai plus à refuser les projets qu'on me propose! Ce que j'aimerais maintenant, c'est trouver des projets techniquement ambitieux dans lesquels je puisse vraiment me lâcher! :P

J'en profite pour faire un petit clin d'oeil à un collègue graphiste, qui s'est lancé lui aussi il y a quelques mois, et a fait un zouli site. Nous travaillons en binôme, donc s'il y a besoin d'une vraie compétence en conception graphique (évidemment, si je suis tout seul, ca ira pas loin :$), c'est possible aussi!

Je croise les doigts... :mrgreen:

mardi, août 30 2005

RegExp dans Flash 8 : une solution grace à ExternalInterface

Pour tout de suite rassurer ceux qui auraient eu de faux espoirs dûs au titre de ce billet, les RegExp ne sont pas dans Flash 8... Par contre, les nouvelles possibilités du player laissent entrevoir une alternative plutôt élégante. :)

En effet, Flash 8 dispose d'une nouvelle manière de communiquer avec ses "hôtes", notamment les browsers, mais aussi les projecteurs de type mProjector. Ce remplacement de FSCommand, permet de faire des appels synchrones a des API externes. Vous trouverez plus d'infos sur la mailing list de OSFlash (merci Darron Schall), ou sur le blog de Geoff Stearns qui s'attarde sur la partie Javascript.

Pour revenir a nos RegExp, le même Geoff Stearns a tout simplement une l'idée aussi simple que géniale d'utilise les RegExp de Javascript. En effet, Javascript intègre complètement les expressions régulières et est intégré dans la plupart des navigateurs. Il est donc possible d'encapsuler complètement la communication Flash/Javascript, et de les utiliser comme s'il s'agissait de fonctions natives, comme c'est montré (via l'utilisation de prototypes) sur ce même blog. Il est bien évidemment possible de créer une classe indépendante qui gérerait tout ca.
Une trés bonne alternative à la classe RegExp développée en AS1 (puis portée en AS2 par Joey Lott), qui de par sa nature, doit être bien moins performante (enfin, je suppose :$).

Reste malgré tout l'inconvénient de ne pas pouvoir les tester dans l'environnement auteur de Flash, ou via un simple SWF dans le projecteur standalone. On reste donc obligé de le tester via une page HTML.
Mais si une interface de type IRegExp était créée, puis implémentée par les différents projecteurs (mProjector, SWF Studio, ScreenWeaver, etc...), on pourrait ainsi avoir un accés commun aux RegExp dans n'importe quel environnement, avec pourquoi pas la classe RegExp en AS (adaptée bien sûr, sans utilisation de String.prototype) pour le projecteur par défaut! 8)

lundi, août 22 2005

Debug 0.2

Ca ne fait pas longtemps que je re-code en AS2 au quotidien. Et depuis que j'ai repris, j'utilise ma classe Debug intensivement, et la fait donc évoluer en fonction de mes besoins, et aussi des bugs qu'elle peut comporter... :P

Au programme de cette nouvelle version, tout d'abord le fait d'éviter une boucle infinie dans le cas ou un object est composé d'un autre, qui lui-même pointe vers son parent. En gros, c'est comme si on parcourait récursivement un clip, et sa propriété _parent...

Ensuite, une méthode éxécutée au premier appel de dump(), qui va parcourir toutes les classes présentes et leur créer une propriété contenant le nom de la classe, et une propriété conternant le nom du package. Cela permet d'afficher tout ca lors du dump d'un objet... En fait, c'est le seul moyen de retrouver le nom d'une classe sans le préciser dans le code de cette classe... Je crois me souvenir que Liguorien avait déjà évoqué ce sujet, s'inspirant de la partie reflection de la librairie as2lib...

Et troisièmement, la possibilité de spécifier un caractère qui permettra d'ignorer toute propriété dont le nom commence par celui-ci... Par exemple, il m'arrive d'utiliser une propriété contenant un XML dans mes classes. Il me suffit de faire commencer son nom par '$' (caractère par défaut) pour qu'elle ne soit pas incluses dans l'affichage...

/**
* Debug
* @version 0.2
* @package com.lalex.utils
*
* @author LAlex
* @since 2005-08-16
*/

class com.lalex.utils.Debug {
   /**
   * Property used to store class names
   */

   public static var classNameProperty:String = "__className";
   public static var packNameProperty:String = "__packName";
   private static var ignoreChar:String = "$";
   private static var __classInit:Boolean = false;
   /**
   * Trace an object detailled content
   * @param o Object to display
   */

   public static function dump(o) {
                if (!__classInit) {
                        initClassNames();
                        __classInit = true;
                }
                trace(dumpToString(o));
   }
        /**
        * Browse all classes and create a property for class name
        * and a property for package name
        * Browse is recursive
        * @param o Object containing classes
        * @param pa Package name
        */

   public static function initClassNames(o, pa:String) {
                if (!__classInit) {
                        o = o ? o : _global;
                        pa = pa.length ? pa : "";
                        for (var p in o) {
                                if (typeof o[p] == "function") {
                                        o[p][classNameProperty] = p;
                                        o[p][packNameProperty] = pa.length ? pa.substr(0, pa.length-1) : "";
                                        _global["ASSetPropFlags"](o[p], classNameProperty + "," + packNameProperty, 7);
                                } else if (typeof o[p] == "object") {
                                        initClassNames(o[p], pa + p + ".");
                                }
                        }
                }
        }
   /**
   * Return a string containing the object content. Recursive method
   * @param o Target object
   * @param d Depth. Used to generate tabs
   * @param a Array containing already dumped objects
   */

        private static function dumpToString(o, d:Number, id:Number):String {
                var ret:String = "[";
                d = d ? d : 0;
                id = id ? id : _dumped.push(new Array())-1;
                var dp:Number = d;
                var sp:String = "";
                while (d-- > 0) {
                        sp += "  ";
                }
                if (typeof o == "object") {
                        if ( o["__constructor__"][classNameProperty] ) {
                                ret += o["__constructor__"][classNameProperty] + " (" + o["__constructor__"][packNameProperty] + ")";
                        } else if (o instanceof Array) {
                                ret += "Array";
                        } else {
                                ret += "Object";
                        }
                        if (!o.__dumped) {
                                ret += "
"
+ sp;
                                o.__dumped = true;
                                _global["ASSetPropFlags"](o, "__dumped", 1);
                                _dumped[id].push(o);
                                for (var p in o) {
                                        if (typeof p != "function") {
                                                if (p.substr(0,1) != ignoreChar) {
                                                        ret += p + ": " + dumpToString(o[p], dp+1, id) + "
"
+ sp;
                                                }
                                        }
                                }
                        }
                        ret += "]";
                } else {
                        ret = o.toString();
                }
                var nd:Number = _dumped[id].length;
                while (nd-- > 0) {
                        var df = _dumped[id].pop();
                        df.__dumped = false;
                        delete df.__dumped;
                }
                return ret;
        }
        private static var _dumped:Array;
}

En prévision, pourquoi pas de pouvoir retrouver l'héritage d'une classe, comme l'a suggéré ekameleon dans le premier post de cette classe ? ;)

::Télécharger Debug 0.2::

vendredi, août 19 2005

Identifiant unique pour un objet

Pour des raisons de performances, il peut etre trés utile d'attribuer un identifiant unique à un objet. En effet, un identifiant numérique peut devenir index dans un tableau, et ainsi augmenter considérablement les performances pour retrouver celui-ci sans avoir à parcourir la totalité d'un tableau.

C'est pourquoi j'ai développé une classe ObjectCollector, qui permet d'attribuer un identifiant unique à un objet, et éventuellement de retrouver cet objet à partir de ce dernier, mais uniquement s'il a été "collecté" (on a pas toujours besoin de le retrouver par son id)... ;)

/**
 * Collect objects and give them a unique Id.
 * @author  <a href="http://www.lalex.com/">LAlex</a>
 * @version 1.0
 * @since 2005-08-19
 */

class com.lalex.core.ObjectCollector {
        // Stores last given id
        private static var __id:Number = 0;
        // Store collected objects
        private static var __instances:Array = [];
        /**
        * If not already done, return a unique Id for the object
        * @param o
        * @return Object's unique id
        */

        public static function uId(o):Number {
                if (!o["__ocId"]) {
                        o["__ocId"] = ++__id;
                        _global.ASSetPropFlags(o, "__ocId", 7, 1);
                }
                return o["__ocId"];
        }
        /**
        * Collect an object. Must be call before using getById method
        * @param o Object to be collected
        * @return Object's unique id
        */

        public static function collect(o):Number {
                __instances[uId(o)] = o;
                return uId(o);
        }
        /**
        * Release an object. Must be used
        * before any delete of a collected object
        * @param o Object to be released
        */

        public static function release(o) {
                delete __instances[uId(o)];
        }
        /**
        * Release all collected objects
        */

        public static function clear() {
                var n:Number = __instances.length;
                while(n-- > 0) {
                        release(__instances[n]);
                }
        }
        /**
        * Retrieve an object from its id
        * The object must have been collected first
        * @param id Object's id
        * @see #collect
        */

        public static function getById(id:Number) {
                return __instances[id];
        }
}
Utilisation : import com.lalex.core.ObjectCollector;
// Objects
var o1 = {a:150, b:"LAlex"};
var o2 = {d:false, e:"Blablabla"};
// Collect the 'o1' object
var o1id = ObjectCollector.uId(o1);
trace("o1 id : " + o1id); // o1 id : 1
ObjectCollector.collect(o1);
trace("Retrieved ? " + (o1 == ObjectCollector.getById(o1id))); // Retrieved ? true
// Release 'o1' object
ObjectCollector.release(o1);
trace("Retrieved ? " + (o1 == ObjectCollector.getById(o1id))); // Retrieved ? false
// Get the 'o2' unique id
var o2id:Number = ObjectCollector.uId(o2);
trace("o2 id : " + o2id); // o2 id : 2
trace("Trouvé ? " + (o2 == ObjectCollector.getById(o2id)));// Retrieved ? false

::Télécharger ObjectCollector 1.0::

mardi, août 16 2005

Double clic dans Flash

Le fait de pouvoir intercepter un évenement de type double clic a longtemps été demandé pour les boutons Flash. En attendant, je fais comme tout le monde, je me débrouille... et voici comment je fais dans le cadre d'une classe héritant de MovieClip.

class DblClickMovieClip extends MovieClip {
        // Interval called if second click is too late
        private var _itv:Number;
        // Amount of succesive clicks
        private var _click:Number = 0;
        // onPress event
        private function onPress() {
                dblClick();
        }
        // Check if 2 clicks were made in the given timelapse
        private function dblClick() {
                if (++_click == 2) {
                        _click = 0;
                        onDblClick();
                } else {
                        // Double click timelapse can be set here
                        _itv = setInterval(this, "clickTimeout", 250);
                }
        }
        // Init clicks
        private function clickTimeout() {
                clearInterval(_itv);
                _click = 0;
        }
        // Called if double click
        private function onDblClick() {
                trace("Double click");
        }
}

Bien évidemment, cette méthode est adaptable si on ne veut pas que tout le clip soit clicable... ;) Je pensais d'ailleurs implémenter ca par la suite sous forme de décorateur d'un clip...

Il doit y avoir des centaines de méthodes pour arriver au même résultat, alors si quelqu'un en voit une meilleure, qu'il n'hésite pas! :) Et pour finir, ne désespérons pas de l'avoir un jour en natif, il ne nous aura fallu que 4 versions pour avoir l'upload de fichiers ! :P

mercredi, août 10 2005

Moteurs 3D : Flash 8 is god

Ce qui est clair, c'est que Flash 8 va beaucoup aider au développement de moteurs 3D, grâce a ses commandes de manipulation des bitmaps. Andre Michelle l'a bien compris, et nous propose parmise ses expériences 8ball, un rendu 3D avec zBuffer, et une gestion de la lumière particulièrement sympa.

Argh, je tiens plus d'essayer Flash 8, histoire d'implémenter quelques algos dans mon moteur 3D... :)

lundi, août 8 2005

Flash 8 enfin annoncé

C'était kitch à mort, Macromedia l'a fait : l'annonce de Flash 8 le 8/8 : http://www.macromedia.com/software/flash/flashpro/ 8)

Vous trouverez un petit tour assez instructif sur les nouvelles fonctionnalités. Un coup de coeur pour le paramétrage des interpolations, et pour la possibilité de diffuser des évenements à certains points (cue points) d'une vidéo!

Pour le reste, no comment tant que j'ai pas la trial dans les mains... ;)

vendredi, août 5 2005

Utilisation avancée de EventDispatcher

Je continue l'extension de classes natives de Flash pour leur attribuer le modêle évenementiel de EventDispatcher. J'en suis venu à me pencher sur la classe EventDispatcher elle-même, et j'ai découvert quelques tips, que tout le monde ne connaît pas forcément...

1. INITIALISATION
Une méthode toute simple consiste à déclarer les trois méthodes principales, et d'appeler ensuite la méthode statique 'initialize' qui va s'occuper de décorer le prototype de notre classe. A la base, c'est même dans le constructeur que l'on initialise l'instance, mais dans la classe ca fait plus mieux. En gros, on retrouve le même code un peu partout, excepté le nom de la classe qui change. import mx.events.EventDispatcher;
class EventSource {
        public var addEventListener:Function;
        public var removeEventListener:Function;
        private var dispatchEvent:Function;
        private static var __initEvent = EventDispatcher.initialize(EventSource.prototype);
}
Il faut bien avouer que à la pratique, au bout d'un moment, ca devient assez lourd de toujours retaper la même chose. Un #include serait bien pratique, mais étant donné que le nom de la classe change à chaque fois, cela obligerait à laisser le initialize() dans le code de la classe. J'ai donc utilisé une autre méthode :// fichier "initEvent.as"
private static var __dispatcher:Function = mx.events.EventDispatcher;
public function addEventListener(e:String, o) {
  __dispatcher.prototype.addEventListener.apply(this, arguments);
}
public function removeEventListener(e:String, o) {
  __dispatcher.prototype.removeEventListener.apply(this, arguments);
}
private function dispatchEvent(o) {
  __dispatcher.prototype.dispatchEvent.apply(this, arguments);
}
private function dispatchQueue(q, o) {
 __dispatcher.prototype.dispatchQueue.apply(this, arguments);
}
// fin "initEvent.as"

// fichier "EventSource.as"
class EventSource {
        #include "initEvent.as"
}
La méthode dispatchQueue() n'est jamais utilisée en général, excepté par dispatchEvent(), mais elle a besoin d'être là pour faire fonctionner le tout... Bon, je veux bien concevoir que c'est pas trés académique d'utiliser les prototypes. :$ Mais en tout cas, ca fonctionne plutôt bien, et ca me permet de chager de "dispatcher" facilement. Par exemple, si je préfère utiliser GDispatcher, il me suffit de changer la première ligne de mon fichier, et toutes les classes qui l'utilisent seront mises à jour à la compilation! 8) Et si jamais, j'ai un besoin réellement spécifique, il me suffit de copier/coller le code directement dans la classe. Cela peut même éventuellement me permettre de rajouter un typage quelqconque a mes paramètres, ou d'afficher des infos de debug, etc... 2. HANDLER INTERNE La classe EventDispatcher permet nativement de créer un handler interne qui va intercepter les évenements diffusés. Il suffit pour cela de créer une méthode qui s'appelle <nomEvenement>Handler(). Ca permet d'éviter d'inscrire une instance comme son propre écouteur :// Sans handler interne
class EventSource {
        #include "initEvent.as"
        function EventSource() {
           this.addEventListener("onEvent", this);
        }
        private function onEvent(o) {
           trace(o.type + " event dispatched");
        }
}
// Avec handler interne
class EventSource {
        #include "initEvent.as"
        function EventSource() { }
        private function onEventHandler(o) {
           trace(o.type + " event dispatched");
        }
}
Bien pratique parfois, pour du debug par exemple. :) 3. HANDLER GENERIQUE En plus d'une méthode portant le nom de l'évenement à intercepter, un objet qui en écoute un autre peut intercepter n'importe quel évenement pour peu qu'il y soit abonné, s'il implémente une méthode nommée handleEvent(). C'est d'ailleurs ce système qu'utilise la classe Delegate de Macromedia (et celle de Francis BOURRE aussi, mais son site il est plus la pour l'instant :()// Fichier "EventSource.as"
class EventSource {
        #include "initEvent.as"
        function EventSource() { }
        function doIt() {
                dispatchEvent({type:"onEvent", target:this});
        }
}
// Fin fichier
// Fichier "EventListener.as"
class EventListener {
        function EventListener() {}
        function handleEvent(o) {
                trace(o.type + " event handled by handleEvent() method");
        }
        function onEvent(o) {
                trace(o.type + " event handled by onEvent() method");
        }
}
// Fin fichier
// Code principal
var es:EventSource = new EventSource();
var el:EventListener = new EventListener();
es.addEventListener(el);
es.doIt();
// Sortie
onEvent event handled by handleEvent() method
onEvent event handled by onEvent() method
4. FONCTIONS Un évenement peut également être inetrcepté par un simple fonction. Plus besoin à ce moment là de passer par une instance de classe. Bien entendu, dans le cadre d'un développement full-OO, cette pratique n'est pas vraiment conseillée... 8|var es = new EventSource();
var fct:Function = function(o) {
        trace(o.type + " handled by a function");
}
es.addEventListener("onEvent", fct);
es.doIt();
// Sortie
onEvent handled by a function

Wala, wala pour les quelques subtilités de EventDispatcher... ;)
Bon, je sais, il faut absolument que je fasse quelque chose pour les couleurs de mon code. Ca va viendre, ca va viendre... :$

mercredi, août 3 2005

Composants UI : quelle alternative ?

Maintenant qu'on est rassuré (enfin déçu) sur la non-évolution de l'Actionscript dans la prochaine mouture de Flash, il devient urgent si l'on est développeur de RIA (fashion non ?) de trouver une alternative aux composants V2 de Macromedia. En effet, il faut bien avouer qu'ils sont lourds et buggués, mais quand-même bien pratiques !

Histoire de ne pas réinventer la roue en les développant soi-même (surtout que c'est quand-même un gros morceau), j'ai essayé de faire un petit tour d'horizon de ce qui existait et qui semblait sérieux.

Le seul package dont je peux vraiment parler, étant donné que c'est le seul "essayable", est celui des SMX Components. Déployés sur le site de la BBC (a ce qu'il paraît, je les ai jamais trouvés), ils semblent fiables et performants, mais assez "brouillons" à mon avis... Il manque aussi quelques composants, comme le Tree, Accordion, Radio, Date, etc... Et puis quand je dis "essayé", c'est en dilletante, jamais au coeur d'une véritable application...

Les BitComponents de BJC, aujourd'hui rachetés pas FlashLoaded... Il n'est pas possible de les essayer malheureusement, mais sur le papier ils semblent trés complets. Eprouvés, leur réputation n'est plus à faire... En plus, 99$ c'est pas non plus le bout du monde ! :)

Les XPComponents, dont une nouvelle version vient de voir le jour... Un package trés complet, et sourtout basé sur des interfaces, ce qui permet une personnalisation avancée. Comme les précédents, trés bien sur le papier, mais impossible de les essayer... Pour 349$, vous avez quand-même les sources

Et l'arlésienne : GLIC, dorénavant les mCOM :mrgreen: On les attend, on les attend, et toujours rien. Evidemment, quand on sait qui est derrière, on se dit dit que ca vaut surement le coup. Reste à voir ce que ca va donner, et surtout les conditions de licence et de prix... :o

Pour conclure, je dirais que pour acheter un pack de composants comme ceux cités ci-dessus, la moindre des choses serait de pouvoir les essayer. Evidemment, difficile d'implémenter un système de "Trial" pour des composants, mais quand on développe de véritables applications, il est délicat d'investir si l'on est pas sûr que les composants répondront à notre besoin. :\

En restant sur le papier, personnellement je serais séduit par les BitComponents : pas cher, complets, et esthétiquement trés convenables.

Si vous connaissez d'autres sets de composants, ou que vous avez essayé un des sets cités ici, n'hésitez pas à le signaler dans les commentaires... ;)

jeudi, juillet 21 2005

Case hexagonales adjacentes

J'ère en ce moment sur un projet de jeu de plateau à porter en Flash. Or, ce jeu se joue avec des cases hexagonales, ce qui a priori rendait le stockage plutôt complique... :( Eh bien, il n'en est rien! 8) Il suffit en fait de stocker les cases dans un "bête" tableau à deux dimensions. C'est juste l'affichage qui diffère un peu, comme l'explique bien cette page. :)

Bien que je n'en ai pas spécialement besoin dans mon cas, je n'ai pu m'empêcher de me poser la question du pathfinding sur ce type de cartes. Eh bien, en fait c'est plutôt trés simple, étant donné que la question des diagonales ou des coins à contourner ne se pose plus. Par contre, le problême reste de trouver les cases conjointes à une case donnée. En effet, selon sur quelle "ligne" on se trouve, le test est différent.

Voici la fonction que j'ai implémenté a cette fin :function getNearHexPoints(g:Array, x:Number, y:Number):Array {
        var a:Array = new Array();
        for (var i=-1 ; i<=1 ; i++) {
                for (var j=-1 ; j<=1 ; j++) {
                        if ((i || j) && (!(i && j) || (y%2 && i == 1) || (!(y%2) && i == -1)) && g[x+i] && g[y+j]) {
                                a.push({x:x+i, y:y+j});
                        }
                }
        }
        return a;
}

Et voici son resultat (case cliquables) :

La classe PathFinder qui gère aussi les cases hexagonales ne devrait pas tarder... ;)

mardi, juillet 19 2005

Classe de debug

Je suppose que la plupart des codeurs ont leur classe de debug. Voici la mienne, a peine commencée. Il s'agit uniquement d'un dump d'un objet. La méthode un objet recursivement pour afficher l'ensemble de ses propriétés. Il gère également le fait qu'on ait pu mettre le nom de la classe dans une variable statique (par défaut __className).

/**
* Debug
* @version 0.1
* @package com.lalex.utils
*
* @author <a href="http://www.lalex.com/">LAlex</a>
* @since 2005-07-19
*/

class com.lalex.utils.Debug {
        /**
        * Property used to store class names
        */

        public static var classNameProperty:String = "__className";
        /**
        * Trace an object detailled content
        * @param o Object to display
        */

        public static function dump(o) {
                _global.trace(dumpToString(o));
        }
        /**
        * Return a string containing the object content. Recursive method
        * @param o Target object
        * @param d Depth. Used to generate tabs
        */

        private static function dumpToString(o, d:Number):String {
                d = d ? d : 0;
                var dp:Number = d;