Licence CC BY-NC-SA

Dessiner avec l'Actionscript

Publié :

Dans ce chapitre, nous allons apprendre à dessiner directement depuis l'Actionscript ! Je suis certain que beaucoup parmi vous attendaient ce moment depuis fort longtemps, c'est pourquoi nous essaierons de présenter un maximum de choses tout en prenant le temps de bien expliquer. Nous présenterons en premier la classe Graphics, qui est à l'origine de tout tracé en Actionscript. Puis nous présenterons alors les différentes méthodes de dessin que possède cette classe.

Enfin pour finir, je vous propose un petit exercice où nous réaliserons ensemble une illustration d'un mouton. Ce sera alors l'occasion de revenir sur ces méthodes de dessin et mieux comprendre toute la philosophie qui se cache derrière le dessin en Flash !

L'objet Graphics

Introduction

Une propriété de type Graphics

Nous avons vu précédemment qu'il existait différents types d'objets d'affichage. Parmi eux, certains possèdent déjà tout le nécessaire pour dessiner. Il s'agit des classes Shape, Sprite et MovieClip (voir figure suivante) !

La classe Shape dans l'arbre des classes d'affichage

Ce qui les différencie des autres classes c'est que celles-ci ont un attribut très particulier : une instance de la classe Graphics. En réalité, c'est cette dernière classe qui dispose de l'ensemble des méthodes permettant de tracer différentes formes. Ainsi en déclarant des objets de type Shape, Sprite ou MovieClip vous pourrez dessiner à l'intérieur de ceux-ci. En particulier, la classe Shape est spécialement faite pour cela puisque celle-ci ne contient que cet attribut et l'accesseur correspondant comme vous pouvez le voir à la figure suivante?

La classe Shape exclusivement utilisé pour sa propriété graphics

Cette classe est allégée, ce qui implique de meilleures performances, mais une instance de la classe Shape ne peut pas contenir d'enfants et elle n'est pas interactive (il est impossible de savoir si l'utilisateur clique dessus).

Pourquoi ne pas utiliser directement l'objet Graphics ?

Il se peut que vous vous soyez déjà demandé pourquoi ne pas créer directement une occurrence de la classe Graphics et utiliser ainsi ses différentes méthodes de dessin. En réalité, l'Actionscript favorise l'utilisation des sous-classes de DisplayObject, dont n'hérite pas la classe Graphics. C'est pourquoi celle-ci est non instanciable : vous ne pourrez donc pas utiliser la syntaxe « new Graphics() » et vous serez donc obligés de passer par l'utilisation de l'une des trois classes décrites plus haut. De plus, elle est de type final, ce qui signifie que l'on ne peut pas créer de sous-classes héritant de la classe Graphics.

Je vous invite donc à créer une instance de la classe Shape que nous utiliserons dans la suite :

1
var monDessin:Shape = new Shape();

Dessin vectoriel

La classe Graphics est utilisée pour créer des dessins vectoriels ! Étant donné que ce terme est peut-être nouveau pour vous, nous allons ici présenter ce qui différencie ce type d'images par rapport aux images « bitmap ». Vous ne vous êtes jamais demandés comment les images étaient stockées à l'intérieur de votre ordinateur ? En fait le stockage des images peut être réalisé de deux manières différentes. Lorsqu'on parle d'images « bitmap », celles-ci sont enregistrées à partir des couleurs de chacun des pixels. En revanche dans les images vectorielles, ce sont les formes en elles-mêmes qui sont sauvegardées. Cela fait donc une énorme différence lors de la lecture du fichier et de l'affichage à l'écran.

Pour mieux comprendre, nous allons prendre l'exemple de la figure suivante.

Image de base

Cette image est quelconque et pourrait très bien être enregistrée en tant qu'image « bitmap » ou vectorielle. L’inconvénient du format « bitmap » est justement qu'il retient des pixels. Ainsi si vous respectez plus les dimensions des pixels, l'image va être déformée. Je m'explique ; imaginez que vous ayez besoin, pour une raison quelconque, d'agrandir votre image. Les pixels vont donc être redimensionnés et l'image n'aura plus l'aspect lisse qu'elle avait au départ. Au contraire, une image vectorielle est définie à partir de formes. Ainsi lorsque celle-ci va être redimensionnée, l'affichage sera adapté pour correspondre à nouveau à la forme désirée.

Voyez plutôt la différence à la figure suivante entre l'image « bitmap » à gauche et l'image vectorielle à droite, toutes les deux redimensionnées à 300%.

Redimensionnement de l'image e base

Dans ce chapitre, nous allons donc parler exclusivement de dessin vectoriel, qui l'un des atouts de cette technologie Flash !

Des contours et des remplissages

Le principe

Si vous avez déjà utilisé des classes de dessin dans d'autres langages de programmation, vous allez voir qu'ici le principe est assez différent. Avant d'entrer plus dans les détails, je vous invite à découvrir les différents termes que nous serons amenés à utiliser dans la suite de ce chapitre. Pour cela je vous propose tout d'abord un petit dessin d'exemple à la figure suivante.

Composition d'un élément graphique

Dans cet exemple nous pouvons voir une bulle de pensée comme on en trouve dans les bandes dessinées. Cette bulle est en fait composée de 4 objets : un « nuage » et trois ovales. Ces objets correspondent en réalité à des tracés différents, qu'il faudrait réaliser l'un après l'autre. Chaque tracé est donc défini de la façon suivante :

  • le remplissage : surface représentée en bleu sur le dessin précédent
  • le contour : ligne qui délimite le remplissage
  • la ligne : trait visible qui suit le contour et pouvant avoir différentes apparences.

Ainsi pour réaliser un tracé en Flash, il nous faut définir le contour de celui-ci en indiquant quel style adopter pour le remplissage et la ligne.

Définir le style des lignes

Tout d'abord, veuillez noter que nous parlons ici de lignes et non de contours, il s'agit donc uniquement du côté esthétique des traits qui effectivement suivent le contour. Pour définir le style des lignes, nous utiliserons la méthode lineStyle() de la classe Graphics. Cette méthode possède beaucoup de paramètres, mais tous sont facultatifs. C'est pourquoi je ne vous parlerai ici que des trois premiers qui sont les plus intéressants. Le premier sert à définir la largeur de la ligne, le deuxième sa couleur, et enfin de dernier permet de préciser l'opacité de celle-ci. Voici un exemple dans lequel nous définissons des lignes de largeur 2, de couleur noire et transparentes à 50% :

1
monDessin.graphics.lineStyle(2, 0x000000, 0.5);

Comme nous l'avons dit, la méthode lineStyle() ne possède que des paramètres facultatifs. Ainsi si vous ne souhaitez pas de lignes sur votre tracé mais uniquement un remplissage, il vous suffit d'utiliser cette méthode sans paramètres, comme ceci :

1
monDessin.graphics.lineStyle();

Définir un remplissage

Contrairement aux lignes, les remplissages nécessitent l'utilisation de deux méthodes : beginFill() et endFill(). La première permet de définir les différentes caractéristiques du remplissage, à savoir sa couleur et son opacité. En revanche ici la couleur est un paramètre obligatoire et ne peut donc pas être omis. Par exemple, voici comment définir une couleur blanche au remplissage :

1
monDessin.graphics.beginFill(0xFFFFFF);

L'utilisation de la seconde méthode est justifiée par la définition même d'un remplissage. En effet, pour remplir l'intérieur d'un contour, celui-ci doit être fermé ! La méthode endFill() sert donc à refermer un contour, pour pouvoir lui appliquer un remplissage. Ainsi si lors du tracé de votre contour, le point final n'est identique au premier, une ligne droite alors tracée entre ces deux points pour fermer le contour. Cette méthode endFill() ne prend aucun paramètre et s'utilise donc simplement de la manière suivante :

1
monDessin.graphics.endFill();

Notez que la méthode beginFill() doit être appelée avant endFill(), et que la définition du contour s'effectue entre ces instructions. Également, tout tracé effectué à l'extérieur de ces deux instructions sera composé uniquement de lignes, et donc sans remplissage.

Dessinez, c'est gagné !

Ça y est, vous allez enfin pouvoir dessiner en Actionscript ! :lol: Nous allons maintenant voir quels sont les différentes méthodes de la classe Graphics, et apprendre à nous en servir. Ensuite nous réaliserons un petit exercice à la fin de ce chapitre, qui devrait vous aider à mieux utiliser cette classe.

Les lignes et les courbes

Une histoire de curseur

À l'origine, l'ensemble des tracés en Flash étaient conçus à l'intérieur du logiciel Flash Professionnal. Ceux-ci étaient donc réalisés graphiquement à l'aide de votre souris, aussi connue sous le nom de curseur. Depuis l'arrivée de l'Actionscript 3, il n'est plus nécessaire de posséder le logiciel pour pouvoir effectuer des tracés. En effet, il est maintenant possible de faire exactement la même chose grâce au code. Cependant les instructions s'exécutent en conservant la logique utilisée pour dessiner dans le logiciel.

Ainsi lorsque vous dessinerez en Actionscript, vous devrez imaginez les différents gestes que vous réaliseriez avec la souris de votre ordinateur. Par exemple si vous souhaitiez tracer une ligne, vous déplaceriez d'abord votre curseur à l'endroit où vous voudriez démarrer celle-ci, puis vous la traceriez. Une fois celle-ci dessinée, votre curseur se trouverait alors à l'autre extrémité de la ligne. Vous pourriez alors continuer votre tracé à partir du même point, ou bien déplacer avant votre curseur vers une autre partie de l'écran.

Revenons maintenant à ce qui nous intéresse : l'Actionscript ! Lorsque vous dessinez grâce à la classe Graphics, vous disposez un curseur fictif que vous pouvez déplacer n'importe où sur l'écran. Pour modifier la position de celui-ci, vous devez utiliser la méthode moveTo() en spécifiant le nouvel emplacement, comme ci-dessous :

1
2
3
var _x:Number = 25;
var _y:Number = 50;
monDessin.graphics.moveTo(_x, _y);

Lorsque vous tracerez des lignes et des courbes, rappelez-vous que vous devez au préalable modifier la position de votre curseur. N'oubliez pas également qu'après un tracé, sa position est automatiquement mise à jour, ce qui facilite la succession de diverses lignes et courbes.

Les lignes droites

Pour réaliser des contours, il est possible de créer des lignes droites. Pour cela, la classe Graphics dispose de la méthode lineTo(), qui trace une ligne entre la position actuelle du curseur et le nouveau point spécifié en paramètre. Voici un exemple qui permet de tracer une droite horizontale d'épaisseur 10 entre les points de coordonnées (10,10) et (10,100) :

1
2
3
monDessin.graphics.lineStyle(10, 0x000000);
monDessin.graphics.moveTo(10, 10);
monDessin.graphics.lineTo(200, 10);

Si vous lancez votre projet, vous verrez apparaître la belle ligne visible à la figure suivante.

Une ligne

Remarquez qu'ici nous n'avons pas utilisé les méthodes beginFill() et endFill(), nous avons donc tracé un contour sans remplissage, en utilisant uniquement un style de ligne. Notez également que nous avons d'abord déplacé notre curseur avant de dessiner la ligne.

À la suite de votre ligne horizontale, vous pourriez très bien continuer le tracé de votre contour avec une ligne verticale par exemple :

1
monDessin.graphics.lineTo(200, 200); // Le curseur est à la position (200,10) avant l'instruction

Les courbes

Les lignes droites c'est bien, mais celles-ci sont vite limitées lorsqu'il s'agit de réaliser un dessin complexe avec des formes arrondis. Par conséquent il a été mis en place ce qu'on appelle les courbes de Bézier ! Le principe de ces courbes est d'utiliser un point de contrôle en plus des deux points d’ancrage. Ce point de contrôle sert à définir la courbure de la ligne en direction de ce point. Pour mieux comprendre l'effet de celui-ci sur la courbe, je vous propose un exemple :

1
2
3
4
5
monDessin.graphics.lineStyle(4, 0x000000);
monDessin.graphics.beginFill(0x55BBFF);
monDessin.graphics.moveTo(10, 100);
monDessin.graphics.curveTo(100, 10, 200, 100);
monDessin.graphics.endFill();

Ici, la méthode curveTo() prend deux paires de coordonnées en paramètres. La première correspond au point de contrôle et la seconde au point d'ancrage final. Voyez plutôt à la figure suivante le résultat du tracé où j'ai ajouté les trois points.

Une courbe décrite par trois points

Dans l'exemple précédent, nous avons définit un remplissage à l'aide de la méthode beginFill(). Vous constaterez alors que le contour a été automatiquement refermé par une ligne droite lors de l'appel de la méthode endFill().

Les formes prédéfinis

Pour les formes géométriques particulières, il existe des méthodes permettant de simplifier leur tracé. Nous pouvons notamment citer les cercles, les ellipses et les rectangles.

Les formes elliptiques

Lorsqu'on parle de formes elliptiques, on inclut généralement les cercles et les ellipses. Nous allons donc voir à présent comment tracer chacune de ces formes. Tout d'abord, occupons-nous des cercles. Nous disposons pour cela d'une méthode nommée drawCircle(), dans laquelle nous devons spécifier les coordonnées x et y du centre du cercle ainsi que le rayon r de celui-ci :

1
monDessin.graphics.drawCircle(x, y, r);

Pour les ellipses, la méthode a utiliser est drawEllipse(). Celle-ci prend cette fois quatre paramètres différents : la largeur et la hauteur de l'ellipse ainsi que les coordonnées x et y du coin supérieur gauche de la zone à tracer :

1
monDessin.graphics.drawEllipse(x, y, largeur, hauteur);

Nous verrons dans l'exercice à venir comment utiliser ces fonctions, mais je vous invite grandement à faire des tests de votre côté pour bien voir l'influence des différents paramètres. N'oubliez pas également de définir un style de ligne ou un remplissage avant l'utilisation de telles méthodes.

Les rectangles

En plus des formes elliptiques, la classe Graphics vous permet de tracer des formes rectangulaires. La méthode drawRect() est ainsi définie exactement de la même façon que drawEllipse(), à la seule différence qu'elle trace cette fois un rectangle. L'instruction ci-dessous ne présente donc aucune surprise :

1
monDessin.graphics.drawRect(x, y, largeur, hauteur);

Il se peut que vous ayez besoin, à un moment ou à un autre, de tracer un rectangle dont les angles sont arrondis. Pour cela, vous disposez également de la méthode drawRoundRect(), où vous devrez renseigner en plus la valeur de l'arrondi :

1
monDessin.graphics.drawRoundRect(x, y, largeur, hauteur, arrondi);

La méthode drawRoundRect() prend en réalité un sixième paramètre si vous désirez des arrondis non uniformes en hauteur et en largeur. Ainsi le cinquième paramètre définit la largeur des arrondis et le sixième leur hauteur.

Techniques avancées

Dessiner une trajectoire

Lorsque vous créerez des contour à l'aide des méthodes lineTo() et curveTo(), il est probable que vous ayez une série d'instructions similaires afin de parcourir l'ensemble de votre contour. C'est pourquoi il existe une méthode drawPath() qui permet d'effectuer l'ensemble de ces tracés en une seule instruction. Cette méthode prend donc en paramètres deux tableaux de type Vector.<int> et Vector.<Number>. Le premier permet de définir le type de commande, quant au second il contient l'ensemble des coordonnées des différents points d'ancrage et points de contrôle. En ce qui concerne les commandes, celles-ci sont définit comme suit :

  • 1moveTo()
  • 2lineTo()
  • 3curveTo().

Pour mieux comprendre, je vous propose de prendre l'exemple suivant :

1
2
3
4
monDessin.graphics.lineStyle(2, 0x000000);
monDessin.graphics.moveTo(0, 0);
monDessin.graphics.lineTo(100, 0);
monDessin.graphics.curveTo(200, 0, 200, 100);

L'ensemble de ces tracés pourrait également être réalisés grâce à la méthode drawPath() de la façon suivante :

1
2
3
4
5
6
7
8
var commandes:Vector.<int> = new Vector.<int>();
commandes.push(1, 2, 3);
var coordonnees:Vector.<Number> = new Vector.<Number>();
coordonnees.push(0, 0);
coordonnees.push(100, 0);
coordonnees.push(200, 0, 200, 100);
monDessin.graphics.lineStyle(2, 0x000000);
mouton.graphics.drawPath(commandes, coordonnees);

Vous remarquerez que nous aurions pu ajouter l'ensemble des coordonnées en une seule instruction avec la méthode push(). Néanmoins pour garder un maximum de lisibilité, je vous conseille de les ajouter par commande, comme si vous utilisiez directement les méthodes correspondantes.

N'est-ce pas plus compliqué et plus long au final ?

Je vous avoue que cette écriture fait peur et est au final plus longue à écrire que la version originale. Cependant, mettez-vous en tête que cette nouvelle version s'exécute plus rapidement que la précédente. Ceci vient du fait que le tracé à l'écran se fait en une seule fois contrairement au premier code où il faut retracer à chaque instruction. Utilisez donc cette méthode au maximum, dès que vous pouvez l'utiliser.

Superposition de remplissages

Lorsque vous tracez différents contours, ceux peuvent être amenés à se superposer. Dans ce cas, il est alors possible de supprimer le remplissage dans la zone de superposition. Pour cela définissez les deux contours à l'intérieur d'un même remplissage, c'est-à-dire sans fermeture du contour entre les deux par la fonction endFill(). Pour vous montrer l'effet créé, voici le code que je vous invite à tester :

1
2
3
4
5
monDessin.graphics.lineStyle(4, 0x000000);
monDessin.graphics.beginFill(0x0000FF);
monDessin.graphics.drawCircle(100, 100, 50);
monDessin.graphics.drawCircle(175, 100, 50);
monDessin.graphics.endFill();

Vous verrez alors deux disques de couleur bleu dont l'intersection est non remplie comme sur la figure suivante.

Exclusion des deux cercles

Un petit pas vers la 3D

Avant de terminer la théorie sur cette classe Graphics, je vais rapidement vous parler d'une méthode un peu spéciale : drawTriangles(). Comme son nom l'indique, cette méthode permet de dessiner des triangles. Cette manière de découper une géométrie en faces triangulaires, est une pratique courante lorsqu'il s'agit de 3D. Cela permet notamment de pouvoir déformer une image en la divisant sur plusieurs faces orientées différemment. Ainsi il est possible d'appliquer ce que l'on appelle une texture, sur un objet 3D modélisé à partir d'une multitude de faces.

Dans ce chapitre, nous n'aborderons pas toutes ces techniques qui sont un peu trop avancées pour nous. Toutefois, je vais vous présenter tout de même comment utiliser cette méthode drawTriangles() avec des lignes et des remplissages. Le principe ici est de créer une liste de points que nous nommerons des sommets, à l'intérieur d'un tableau de type Vector.<Number> contenant leurs coordonnées. Ces sommets sont alors numérotés dans l'ordre de leur déclaration avec pour premier indice 0. Un second tableau de type Vector.<int> permet ensuite créer des triangles en reliant les sommets par trois. Ainsi le premier triangle est généralement constitué des sommets 0, 1 et 2.

Conscient que tout cela est certainement encore flou dans votre tête, je vous propose un petit exemple où nous aurons 5 sommets. Ensuite, nous allons créer quatre triangles composés respectivement des sommets (0,1,2), (1,2,3), (2,3,4) et enfin (2,4,5). Voici le code correspondant à ces manipulations :

1
2
3
4
5
6
var sommets:Vector.<Number> = Vector.<Number>([10, 10, 10, 100, 100, 10, 100, 100, 190, 100, 190, 10]);
var indices:Vector.<int> = Vector.<int>([0, 1, 2, 1, 2, 3, 2, 3, 4, 2, 4, 5]);
monDessin.graphics.lineStyle(4, 0x000000);
monDessin.graphics.beginFill(0xAA00FF); 
monDessin.graphics.drawTriangles(sommets, indices);
monDessin.graphics.endFill();

Vous verrez donc apparaître nos quatre triangles formant ensemble un rectangle, qui pourrait être la base de la modélisation d'un intérieur quelconque par exemple. Voyez à la figure suivante l'affichage correspondant, où j'ai volontairement rajouté les indices des sommets pour que vous puissiez mieux comprendre :

Conception à base de triangles

Cette méthode drawTriangles() est principalement utilisé pour la 3D, c'est pourquoi il n'est pas nécessaire que vous compreniez son fonctionnement dans le détail. Néanmoins pour les intéressés, un troisième paramètre permet de créer de l'UV Mapping. Ainsi vous pouvez renseigner un vecteur Vector.<Number> de coordonnées UV pour appliquer une texture à chaque face. Pour chaque sommet de triangles, vous devez alors faire correspondre un point d'une image en coordonnées UV. Ces coordonnées UV sont alors standardisées à (0,0) pour le coin supérieur gauche de l’image bitmap et (1,1) pour le coin inférieur droit. Ne pouvant pas m'attarder plus sur le sujet, j'invite les intéressés à se rendre dans la rubrique correspondante de la documentation officielle.

Exercice : Dessine-moi un mouton

Conception du dessin

Introduction

Dans cet exercice, nous allons réaliser une petite illustration pas à pas, représentant un mouton !

Avant de nous lancer tête baissée, je vous invite à préparer votre projet afin d'obtenir un résultat similaire au mien à la fin de l'exercice. C'est pourquoi vous allez changer les dimensions de votre scène principale, pour que celle-ci fasse 280 x 220 pixels. Vous pouvez laisser la couleur d'arrière-plan en blanc étant donné que nous allons redéfinir un fond dans peu de temps. Également nous allons allons déclarer deux variables _x et _y pour contrôler la position du mouton, puis une variable Mouton de type Shape qui nous servira à effectuer l'ensemble de nos tracés. Enfin n'oubliez pas d'ajouter notre Mouton à notre conteneur principal grâce à la fonction addChild().

Voici donc notre classe Main au début de l'exercice :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package 
{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.display.Shape;

    public class Main extends Sprite 
    {
        private var _x:int;
        private var _y:int;

        public function Main():void 
        {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }

        private function init(e:Event = null):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
            _x = 160;
            _y = 40;
            var mouton:Shape = new Shape();

            this.addChild(mouton);
        }

    }

}

Le fond

Afin de faire ressortir notre mouton blanc, nous allons tracer une zone rectangulaire de couleur bleu comme arrière-plan de notre dessin. Pour cela nous nous servirons de la fonction drawRoundRect() qui permet d'obtenir un rectangle aux coins arrondis, ce qui rendra l'ensemble plus esthétique. Ici aucune difficulté, nous aurons donc le code suivant :

1
2
3
4
// Fond
mouton.graphics.beginFill(0x10879D);
mouton.graphics.drawRoundRect(0, 0, 280, 220, 40, 40);
mouton.graphics.endFill();

Le corps

La réalisation du corps et certainement la partie la plus longue et fastidieuse de cet exercice. Néanmoins j'ai déjà effectué l'opération, vous n'aurez donc qu'à suivre mes instructions. Pour dessiner le corps du mouton, nous allons dans un premier temps définir les différents paramètres de remplissage et de contour. Puis nous commencerons par tracer une courbe de Bézier qui sera le point de départ pour la suite. Voici donc un premier bout de code :

1
2
3
4
5
6
// Corps
mouton.graphics.lineStyle(4, 0x3A281E, 1.0);
mouton.graphics.beginFill(0xFFFFFF);
mouton.graphics.moveTo(_x, _y);
mouton.graphics.curveTo(_x + 20, _y - 20, _x + 40, _y);
mouton.graphics.endFill();

Comme vous l'imaginez, nous ne pouvons pas dessiner l'intégralité du corps du mouton d'une seule traite. Ainsi comme vous pouvez le voir à la figure suivante, il faut procéder étape par étape :

Mise en place du premier arrondi

Création pas à pas du corps

Tracer chaque « bosse » du corps est une étape qui peut être extrêmement longue suivant vos talents en dessin. D'autre part, il est peut probable que vous obteniez rapidement un rendu proche du mien, c'est pourquoi je vous épargnerez cette tâche périlleuse. Je vous propose plutôt le code final permettant de dessiner l'intégralité du corps :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// Corps
mouton.graphics.lineStyle(4, 0x3A281E, 1.0);
mouton.graphics.beginFill(0xFFFFFF);
mouton.graphics.moveTo(_x, _y);
mouton.graphics.curveTo(_x + 20, _y - 20, _x + 40, _y);
mouton.graphics.curveTo(_x + 60, _y - 10, _x + 70, _y + 10);
mouton.graphics.curveTo(_x + 90, _y + 10, _x + 90, _y + 30);
mouton.graphics.curveTo(_x + 110, _y + 40, _x + 100, _y + 60);
mouton.graphics.curveTo(_x + 120, _y + 80, _x + 100, _y + 90);
mouton.graphics.curveTo(_x + 110, _y + 110, _x + 90, _y + 110);
mouton.graphics.curveTo(_x + 80, _y + 140, _x + 60, _y + 120);
mouton.graphics.curveTo(_x + 40, _y + 150, _x + 20, _y + 130);
mouton.graphics.curveTo(_x, _y + 150, _x -20, _y + 130);
mouton.graphics.curveTo(_x -40, _y + 140, _x -50, _y + 120);
mouton.graphics.curveTo(_x -70, _y + 120, _x -70, _y + 100);
mouton.graphics.curveTo(_x -100, _y + 90, _x -90, _y + 70);
mouton.graphics.curveTo(_x -110, _y + 50, _x -90, _y + 40);
mouton.graphics.curveTo(_x -90, _y + 20, _x -60, _y + 20);
mouton.graphics.curveTo(_x -50, _y, _x -30, _y + 10);
mouton.graphics.curveTo(_x -20, _y -10, _x, _y);
mouton.graphics.endFill();

Voilà, vous devriez maintenant avoir un beau corps de mouton (voir figure suivante), qui je l'avoue ressemble pour l'instant plus à un nuage qu'à autre chose.

Le corps du mouton

La tête

À présent, nous allons nous occuper de la tête de notre mouton. Nous lui ferons donc deux oreilles, puis nous ajouterons la forme globale du visage. Les yeux quant à eux seront réalisés plus tard puisqu'ils demanderont un peu plus de travail.

Commençons par les oreilles, que nous créerons à partir de courbes de Bézier. La partie haute des oreilles sera dissimulée plus tard, il est donc inutile de s'attarder dessus. C'est pourquoi nous tracerons uniquement une courbe pour chaque oreille, et nous laisserons le soin à la fonction endFill() de refermer chacun de nos remplissages. Ainsi le code présenté ci-dessous permet de réaliser les deux oreilles de la bête :

1
2
3
4
5
6
7
8
9
// Tête
mouton.graphics.beginFill(0xF9D092);
mouton.graphics.moveTo(_x - 30, _y + 50);
mouton.graphics.curveTo(_x - 90, _y + 165, _x - 10, _y + 60);
mouton.graphics.endFill();
mouton.graphics.beginFill(0xF9D092);
mouton.graphics.moveTo(_x + 50, _y + 50);
mouton.graphics.curveTo(_x + 100, _y + 165, _x + 30, _y + 60);
mouton.graphics.endFill();

Bien évidemment, nous avons utilisé une nouvelle couleur de remplissage pour donner un effet de peau à nos oreilles que vous pouvez voir à la figure suivante.

Mise en place des oreilles

Je suggère maintenant que nous passions au visage. Pour dessiner celui-ci, nous allons commencer par tracer une ellipse. Puis nous utiliserons une ligne droite ainsi qu'une courbe de Bézier sans remplissage pour représenter le museau de l'animal. Là encore il n'y a aucune difficulté, il suffit de faire les choses dans l'ordre :

1
2
3
4
5
6
7
mouton.graphics.beginFill(0xF9D092);
mouton.graphics.drawEllipse(_x - 30, _y +20, 80, 150);
mouton.graphics.endFill();
mouton.graphics.moveTo(_x - 5, _y + 155);
mouton.graphics.curveTo(_x + 10, _y + 165, _x + 25, _y + 155);
mouton.graphics.moveTo(_x + 10, _y + 160);
mouton.graphics.lineTo(_x + 10, _y + 170);

Voici donc notre mouton qui commence doucement à prendre forme, comme le montre la figure suivante.

Notre mouton et sa tête

Les cheveux

Nous allons à présent dessiner ce que j'ai appelé les cheveux, mais qui correspond en réalité à la petite touffe de laine présente sur le dessus de la tête. La technique utilisée ici est similaire à celle employée pour le corps, cependant nous allons utiliser cette fois la méthode drawPath(). Afin de rendre le code plus lisible, je vous conseille encore une fois d'ajouter les différentes coordonnées par commande. Voici donc le code que j'ai réalisé pour tracer les cheveux de notre beau mouton :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// Cheveux
var commandes:Vector.<int> = new Vector.<int>();
commandes.push(1, 3, 3, 3, 3, 3, 3, 3, 3);
var coordonnees:Vector.<Number> = new Vector.<Number>();
coordonnees.push(_x - 20, _y + 20);
coordonnees.push(_x, _y - 10, _x + 20, _y + 10);
coordonnees.push(_x + 40, _y, _x + 50, _y + 30);
coordonnees.push(_x + 80, _y + 30, _x + 60, _y + 50);
coordonnees.push(_x + 70, _y + 70, _x + 40, _y + 70);
coordonnees.push(_x + 20, _y + 90, _x, _y + 70);
coordonnees.push(_x - 20, _y + 90, _x - 30, _y + 60);
coordonnees.push(_x - 60, _y + 50, _x - 40, _y + 30);
coordonnees.push(_x - 40, _y, _x - 20, _y + 20);
mouton.graphics.beginFill(0xFFFFFF);
mouton.graphics.drawPath(commandes, coordonnees);
mouton.graphics.endFill();

Nous avons cette fois quelque chose qui commence réellement à ressembler à un mouton (voir figure suivante).

Mise en place des cheveux

Les pattes

Continuons l'exercice en dessinant les pattes, dont je vous avouerai, je n'ai pas réussi à trouver l'effet que je souhaitais. Ce n'est pas grave, contentons-nous de dessiner deux rectangles arrondis en guise de pieds. Rien de plus simple, ceci est réalisable en quatre instructions :

1
2
3
4
5
// Pattes
mouton.graphics.beginFill(0xF9D092);
mouton.graphics.drawRoundRect(_x - 60, _y + 155, 40, 20, 20, 20);
mouton.graphics.drawRoundRect(_x + 40, _y + 155, 40, 20, 20, 20);
mouton.graphics.endFill();

Voici donc à la figure suivante notre mouton qui peut à présent se tenir debout sur ces pattes.

Notre mouton se tient debout

Les yeux

Notre mouton est très beau, mais il est pour l'instant aveugle ! Nous allons maintenant nous occuper des yeux. Pour ceux-ci, nous n'utiliserons que des cercles, ou plutôt des disques. Il nous en faudra trois pour chaque œil : un pour l’œil en lui-même, un pour la pupille et enfin un dernier pour simuler le reflet blanc à l'intérieur de celle-ci. Un petit détail supplémentaire, nous n'aurons pas besoin de contours pour les deux derniers disques. C'est pourquoi nous définirons un style de lignes transparent pour ceux-ci. Je vous propose donc de découvrir le code correspondant à l'ensemble de ces manipulations :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Yeux
mouton.graphics.beginFill(0xFFFFFF);
mouton.graphics.drawCircle(_x - 10, _y + 100, 15);
mouton.graphics.drawCircle(_x + 25, _y + 100, 20);
mouton.graphics.endFill();
mouton.graphics.lineStyle(1, 0x3A281E, 0.0);
mouton.graphics.beginFill(0x000000);
mouton.graphics.drawCircle(_x - 10, _y + 100, 8);
mouton.graphics.drawCircle(_x + 25, _y + 100, 8);
mouton.graphics.endFill();
mouton.graphics.beginFill(0xFFFFFF);
mouton.graphics.drawCircle(_x - 8, _y + 98, 2);
mouton.graphics.drawCircle(_x + 27, _y + 98, 2);
mouton.graphics.endFill();

Cette petite touche finale vient donner vie à notre mouton qui nous scrute maintenant du regard (voir figure suivante) !

Le mouton intégral

La bulle

J'aurai pu laisser ce « chef-d’œuvre » tel quel, mais je trouvais qu'il manquait un petit quelque chose. J'ai donc ajouté une bulle au mouton comme si on se trouvait dans une bande dessinée. Rien de nouveau ici, il s'agit presque exclusivement de cercles remplis ainsi que d'un rectangle arrondi. Encore une fois, la seule difficulté est de placer les éléments au bon endroit. Voici comment faire :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Bulle
mouton.graphics.beginFill(0x3A281E);
mouton.graphics.drawCircle(_x - 60, _y + 70, 5);
mouton.graphics.drawCircle(_x - 80, _y + 50, 10);
mouton.graphics.drawRoundRect(_x - 150, _y - 25, 90, 60, 60, 60);
mouton.graphics.endFill();
mouton.graphics.beginFill(0xFFFFFF);
mouton.graphics.drawCircle(_x - 120, _y + 15, 5);
mouton.graphics.drawCircle(_x - 105, _y + 15, 5);
mouton.graphics.drawCircle(_x - 90, _y + 15, 5);
mouton.graphics.endFill();

Cette fois, nous avons à la figure suivante le résultat final de notre dessin.

L'illustration complète

Malgré ce que vous avez peut-être ressenti au cours de cet exercice, la réalisation de ce mouton a demandé beaucoup de travail, et je m'y suis repris à plusieurs fois pour arriver à ce résultat. N'hésitez pas à essayer de réaliser d'autres dessins plus simples pour vous faire la main avec les méthodes de cette classe Graphics !

Code final

Pour terminer ce chapitre, je vous ai réécrit l'intégralité du code afin que vous puissiez effectuer vos propres tests.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package 
{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.display.Shape;

    public class Main extends Sprite 
    {
        private var _x:int;
        private var _y:int;

        public function Main():void 
        {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }

        private function init(e:Event = null):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
            _x = 160;
            _y = 40;
            var mouton:Shape = new Shape();

            // Fond
            mouton.graphics.beginFill(0x10879D);
            mouton.graphics.drawRoundRect(0, 0, 280, 220, 40, 40);
            mouton.graphics.endFill();

            // Corps
            mouton.graphics.lineStyle(4, 0x3A281E, 1.0);
            mouton.graphics.beginFill(0xFFFFFF);
            mouton.graphics.moveTo(_x, _y);
            mouton.graphics.curveTo(_x + 20, _y - 20, _x + 40, _y);
            mouton.graphics.curveTo(_x + 60, _y - 10, _x + 70, _y + 10);
            mouton.graphics.curveTo(_x + 90, _y + 10, _x + 90, _y + 30);
            mouton.graphics.curveTo(_x + 110, _y + 40, _x + 100, _y + 60);
            mouton.graphics.curveTo(_x + 120, _y + 80, _x + 100, _y + 90);
            mouton.graphics.curveTo(_x + 110, _y + 110, _x + 90, _y + 110);
            mouton.graphics.curveTo(_x + 80, _y + 140, _x + 60, _y + 120);
            mouton.graphics.curveTo(_x + 40, _y + 150, _x + 20, _y + 130);
            mouton.graphics.curveTo(_x, _y + 150, _x -20, _y + 130);
            mouton.graphics.curveTo(_x -40, _y + 140, _x -50, _y + 120);
            mouton.graphics.curveTo(_x -70, _y + 120, _x -70, _y + 100);
            mouton.graphics.curveTo(_x -100, _y + 90, _x -90, _y + 70);
            mouton.graphics.curveTo(_x -110, _y + 50, _x -90, _y + 40);
            mouton.graphics.curveTo(_x -90, _y + 20, _x -60, _y + 20);
            mouton.graphics.curveTo(_x -50, _y, _x -30, _y + 10);
            mouton.graphics.curveTo(_x -20, _y -10, _x, _y);
            mouton.graphics.endFill();

            // Tête
            mouton.graphics.beginFill(0xF9D092);
            mouton.graphics.moveTo(_x - 30, _y + 50);
            mouton.graphics.curveTo(_x - 90, _y + 165, _x - 10, _y + 60);
            mouton.graphics.endFill();
            mouton.graphics.beginFill(0xF9D092);
            mouton.graphics.moveTo(_x + 50, _y + 50);
            mouton.graphics.curveTo(_x + 100, _y + 165, _x + 30, _y + 60);
            mouton.graphics.endFill();
            mouton.graphics.beginFill(0xF9D092);
            mouton.graphics.drawEllipse(_x - 30, _y +20, 80, 150);
            mouton.graphics.endFill();
            mouton.graphics.moveTo(_x - 5, _y + 155);
            mouton.graphics.curveTo(_x + 10, _y + 165, _x + 25, _y + 155);
            mouton.graphics.moveTo(_x + 10, _y + 160);
            mouton.graphics.lineTo(_x + 10, _y + 170);

            // Cheveux
            var commandes:Vector.<int> = new Vector.<int>();
            commandes.push(1, 3, 3, 3, 3, 3, 3, 3, 3);
            var coordonnees:Vector.<Number> = new Vector.<Number>();
            coordonnees.push(_x - 20, _y + 20);
            coordonnees.push(_x, _y - 10, _x + 20, _y + 10);
            coordonnees.push(_x + 40, _y, _x + 50, _y + 30);
            coordonnees.push(_x + 80, _y + 30, _x + 60, _y + 50);
            coordonnees.push(_x + 70, _y + 70, _x + 40, _y + 70);
            coordonnees.push(_x + 20, _y + 90, _x, _y + 70);
            coordonnees.push(_x - 20, _y + 90, _x - 30, _y + 60);
            coordonnees.push(_x - 60, _y + 50, _x - 40, _y + 30);
            coordonnees.push(_x - 40, _y, _x - 20, _y + 20);
            mouton.graphics.beginFill(0xFFFFFF);
            mouton.graphics.drawPath(commandes, coordonnees);
            mouton.graphics.endFill();

            // Pattes
            mouton.graphics.beginFill(0xF9D092);
            mouton.graphics.drawRoundRect(_x - 60, _y + 155, 40, 20, 20, 20);
            mouton.graphics.drawRoundRect(_x + 40, _y + 155, 40, 20, 20, 20);
            mouton.graphics.endFill();

            // Yeux
            mouton.graphics.beginFill(0xFFFFFF);
            mouton.graphics.drawCircle(_x - 10, _y + 100, 15);
            mouton.graphics.drawCircle(_x + 25, _y + 100, 20);
            mouton.graphics.endFill();
            mouton.graphics.lineStyle(1, 0x3A281E, 0.0);
            mouton.graphics.beginFill(0x000000);
            mouton.graphics.drawCircle(_x - 10, _y + 100, 8);
            mouton.graphics.drawCircle(_x + 25, _y + 100, 8);
            mouton.graphics.endFill();
            mouton.graphics.beginFill(0xFFFFFF);
            mouton.graphics.drawCircle(_x - 8, _y + 98, 2);
            mouton.graphics.drawCircle(_x + 27, _y + 98, 2);
            mouton.graphics.endFill();

            // Bulle
            mouton.graphics.beginFill(0x3A281E);
            mouton.graphics.drawCircle(_x - 60, _y + 70, 5);
            mouton.graphics.drawCircle(_x - 80, _y + 50, 10);
            mouton.graphics.drawRoundRect(_x - 150, _y - 25, 90, 60, 60, 60);
            mouton.graphics.endFill();
            mouton.graphics.beginFill(0xFFFFFF);
            mouton.graphics.drawCircle(_x - 120, _y + 15, 5);
            mouton.graphics.drawCircle(_x - 105, _y + 15, 5);
            mouton.graphics.drawCircle(_x - 90, _y + 15, 5);
            mouton.graphics.endFill();

            this.addChild(mouton);
        }

    }

}

En résumé

  • La classe Graphics renferme toutes les méthodes permettant de dessiner directement depuis le code.
  • Pour accéder à cette classe Graphics, il est nécessaire de passer par l'instanciation de l'une de ces trois classes : Shape, Sprite ou MovieClip.
  • En Flash, les dessins réalisés sont de type vectoriels, qui s'adaptent à la taille de l'affichage.
  • Pour dessiner en Actionscript, nous devons définir un contour puis spécifier les styles de remplissages et de lignes qui doivent lui être appliqués.
  • Les contours peuvent être réalisés grâce à des lignes droites ou des courbes de Bézier.
  • Des formes prédéfinies facilitent le tracé de formes géométriques simples, telles que les cercles, les ellipses et les rectangles.