Amusez-vous à peindre à la manière de Piet Mondrian
dans l'animation Director 8 ci-dessus. Il suffit de cliquer sur le canevas
noir et déplacer le pointeur de la souris, tout en gardant le bouton
de la souris enfoncé. Cliquez sur la palette pour changer de couleur.
Remarquez que si vous cliquez sur l'une des couleurs de la palette, cette
couleur passe devant les autres. Remarquez que la palette se dessine en
premier plan : vous ne pouvez pas peindre par-dessus. Remarquez que les
rectangles ne laissent pas de "trace" : lorsque vous diminuez
la taille d'un rectangle, la zone qu'il cachait se rafraîchit pour
montrer l'image qu'il y avait avant.
Cliquez sur le dernier rectangle que vous avez dessiné. Vous pouvez
maintenant déplacer ce rectangle tant que vous gardez le bouton
de la souris enfoncé. Si vous changez de couleur avant de déplacer
le rectangle, le rectangle change de couleur.
Rien dans les manches
Ouvrez maintenant l'animation non protégée
mondrian.dir (29Ko). Pour copier cette animation
sur votre disque dur plutôt que de l'ouvrir dans votre browser,
cliquez sur le lien avec le bouton droit de votre souris (si vous êtes
sur PC), ou cliquez sur le lien et attendre (si vous êtes sur Mac).
Un menu contextuel apparaîtra : choisissez "Enregistrer la
cible sous..." (ou une autre instruction semblable).
Une fois que vous avez ouvert cette animation dans Director 8, regardez
la fenêtre du Scénario : que voyez-vous ?
Rien. L'animation n'utilise aucune image-objet. Les pistes sont toutes
vides. Oh pardon : il y a un petit comportement dans la piste des scripts
dans l'image 1. Regardez donc dans la fenêtre de la Distribution.
Voilà la confirmation : cette animation comporte un seul acteur
Script et rien d'autre. Alors d'où viennent ces images dont vous
pouvez changer la taille, la position et le locZ ?
Imaging Lingo
Director 8 vous propose tout un jeu de commandes
pour gérer les images. Ces commandes sont rapides et puissantes.
Vous pouvez créer des images à la volée, les stocker
en mémoire vive, et manipuler leurs pixels à volonté.
Vous pouvez pratiquer des encres et des distorsions quad. Vous pouvez
sauver le résultat dans un acteur bitmap, et même l'envoyer
par Internet par le biais du xtra Multiuser.
Dans cet article, je vous montre comment :
Dessiner directement sur la Scène ;
Remplir une partie d'une image avec aplat de couleur ;
Détecter des clics sur une zone de l'écran ;
Créer une image en mémoire vive à partir de rien
Copier une partie d'une image vers une autre ;
Manipuler l'image de la Scène pour imiter la présence
des images-objets.
Ceci n'est qu'une introduction aux possibilités des commandes
de gestion d'images.
Malgré les apparences, mon intention n'est nullement de vous encourager
à abandonner les images-objets dans vos projets. Dans une animation
complexe, gérer l'image à l'écran sans se servir
des images-objets reviendrait à réinventer la roue. Au contraire,
j'espère que cette démonstration vous aidera à mieux
comprendre comment les images-objets fonctionnent "sous le capot".
Cette connaissance vous permettra d'imaginer comment le nouveau Lingo
peut complémenter les fonctionnalités image-objet existantes.
Dessiner sur la Scène
Voici deux gestionnaires simples qui dessinent une forme sur la Scène.
on carreRouge
carre = rect(60, 20, 260, 220)
rouge = rgb(255, 0, 0)
(the stage).image.fill(carre, rouge)
updateStage
end
on rondBleu
carre = rect(50, 10, 270, 230)
bleu = rgb(0, 0, 255)
options = [#color: bleu, #shapeType: #oval]
(the stage).image.fill(carre, options)
updateStage
end
Le mot anglais "fill" signifie "remplir". Pensez
à utiliser le bouton "L" (pour Lingo) dans la barre d'outils
des fenêtres Messages ou Script pour insérer des mots clefs
avec la bonne syntaxe, lorsque vous créez vos propres gestionnaires.
Référez-vous également au Dictionnaire Lingo pour
avoir des détails sur l'utilisation des commandes. Vous verrez
que la commande "fill()" accepte plusieurs syntaxes
différentes.
Remarquez aussi la nouvelle propriété "image",
qui vous donne accès aux pixels. Ici, vous modifiez les pixels
de l'image de la Scène. Les acteurs bitmap, vectorShape et Flash
ont aussi une propriété "image".
Vous pouvez également créer des images "crues",
stockées dans une variable, avec la fonction "image()".
C'est d'ailleurs ce que vous allez voir bientôt.
Créez une nouvelle animation, ouvrez la fenêtre de Scripts,
et collez ces gestionnaires dans un Script d'Animation. Pour l'instant
il est inutile de démarrer l'animation, puisqu'elle ne contient
pas d'image-objet, et s'arrêtera aussitôt lancée. Vous
pouvez, par contre, tapez les commandes suivantes dans la fenêtre
de Messages avec l'animation à l'arrêt.
carreRouge
rondBleu
Notez que, pour faire une ellipse, il faut quand même définir
un rectangle, puis utiliser une liste d'options pour indiquer qu'il s'agit
de remplir le rectangle avec une forme ovale. Amusez-vous avec les propriétés
possibles de cette liste d'options : #shapeType, #lineSize, #color et
#bgColor.
D'autres formes avec des quads
Vous pouvez créer des formes plus intéressantes encore en
utilisant un quad comme destination, plutôt qu'une zone rectangulaire.
Un quad est une liste de quatre points : chaque point définit l'un
des coins d'un quadrilatère quelconque. Dans l'exemple ci-dessous,
deux de ces points se superposent : le quad en question forme donc un
triangle.
on triangleJaune
gauche = point( 50, 35)
droite = point(270, 35)
bas = point(160, 235)
triangle = [gauche, droite, bas, bas] -- quad triangulaire
jaune = rgb(255, 255, 0)
carre = image(200, 200, 8) -- image carrée vide
carre.fill(carre.rect, jaune) -- image jaune carrÈe
(the stage).image.copyPixels(carre, triangle, carre.rect)
updateStage
end
Ajoutez ce gestionnaire à votre script, puis testez-le depuis la
fenêtre des Messages :
triangleJaune
Notez la fonction "image()", qui vous permet de
créer une image en mémoire vive, de toutes pièces.
Les pixels de l'image créée sont tous blancs au départ.
(Pour vérifier ceci, mettez la ligne qui remplit l'image carrée
de jaune en commentaire, en plaçant des tirets "--" devant
cette ligne).
Notez aussi la commande "copyPixels()". Vous verrez
plus bas une utilisation plus simple. Ici, on copie l'image carrée
sur la Scène, en la déformant pour la faire tenir dans une
zone définie par le quad "triangle". CopyPixels
est une commande très puissante. Vous pouvez, par exemple, utiliser
une liste de paramètres pour modifier les modalités de la
copie. Vous pouvez ainsi manipuler :
#color et #bgColor: la couleur
des zones opaques et transparentes ;
#ink et #blendLevel: l'effet
d'encre et l'opacité ;
#maskImage et #maskOffset: l'image en noir
et blanc utilisée pour délimiter des zones non quadrilataires
;
... et d'autres paramètres encore plus spécialisés.
Consultez le Dictionnaire Lingo pour plus de précisions.
Esquisser un comportement
Essayons maintenant d'automatiser cette tache. Convertir votre script
d'animation en comportement. Pour ce faire, cliquez sur le bouton avec
un "i" blanc dans un rond bleu, pour ouvrir le nouvel Inspecteur
de propriétés, puis choisir "Comportement" dans
le menu local.
Ajoutez ces deux gestionnaires à votre comportement
:
on beginSprite
carreRouge
rondBleu
triangleJaune
end
on exitFrame
go the frame
end exitFrame
Glissez votre comportement sur la piste Script de la première
image de votre animation, puis lancez celle-ci.
Le gestionnaire "on beginSprite" est exécuté
avant que la Scène ne soit dessinée pour la première
image. Mais la Scène reste vide. Rien ne se dessine dessus. Ce
n'est pas la faute de vos gestionnaires. C'est une question de timing.
Les gestionnaires "on carreRouge", "on
rondBleu" et "on triangleJaune" ont
été appelés trop tôt. Vous pouvez rappeler
le gestionnaire "on beginSprite", qui les appelle
à son tour, en tapant la commande suivante dans la fenêtre
de Messages :
sendAllSprites(#beginSprite)
Et voilà, les trois formes apparaissent. Mais pourquoi n'étaient-elles
pas apparues auparavant ?
Quand est-ce qu'on dessine ?
Director reconnaît plusieurs étapes dans l'affichage d'une
image à l'écran. Il ponctue ces étapes avec des messages.
En ce qui nous concerne ici, en voici l'ordre :
#beginSprite...envoyÈ aux comportements qui commencent
dans l'image à afficher
#prepareFrame..envoyé avant que l'image ne se
dessine
#enterFrame....envoyé après que l'image
a été dessinée
#exitFrame.....envoyé avant de sortir de l'image
courante
Si vous dessinez sur la Scène dans un gestionnaire "on
beginSprite" ou "on prepareFrame",
Director risque de plaquer les images-objets par-dessus votre dessin.
Dans notre cas, il a carrément refusé d'exécuter
la commande "updateStage", puisque cette commande
l'oblige à redessiner la Scène, et donc d'envoyer de nouveau
les messages #beginSprite et #prepareFrame,
ce qui le ferait entrer dans un cercle vicieux.
Pour faire apparaître vos formes, il faut donc attendre le message
#enterFrame. Remplacez le gestionnaire "on beginSprite"
avec ce qui suit :
property debut
on beginSprite
debut = TRUE
end
on enterFrame
if debut then
-- Ces lignes ne seront exÈcutÈes qu'une seule fois
carreRouge
rondBleu
triangleJaune
debut = FALSE
end if
end
Relancez votre animation : les formes se dessinent correctement. Le
timing de la gestion de l'image de la Scène est donc important.
Changer de locZ
Le locZ d'une image-objet détermine dans quel ordre son image sera
affichée à l'écran. Vous pouvez très facilement
simuler cet effet : il suffit de redessiner une forme par-dessus une autre.
Puisque nous avons des formes simples, d'une seule couleur, nous allons
pouvoir détecter un clic sur une forme selon la couleur qui se
trouve sous le pointeur de la souris. (Dans l'animation mondrian.dir,
j'utilise une liste de rects pour arriver à la même fin).
Ajoutez les lignes suivantes à votre comportement (les propriétés
doivent être déclarées tout en haut du script, avant
les gestionnaires "on carreRouge", "on
rondBleu" et "on triangleJaune") :
property rouge
property bleu
property jaune
on mouseUp
couleurSousPointeur = (the stage).image.getPixel(the mouseLoc)
case couleurSousPointeur of
rouge: carreRouge
bleu: rondBleu
jaune: triangleJaune
end case
end
Relancez votre animation. Maintenant, lorsque vous cliquez sur une partie
d'une forme la forme en question est redéssiné devant les
autres.
Vous pouvez télécharger une version complète de
cette animation : formes.dir.
getPixel() se métamorphose
Dans Director 7, la fonction "getPixel()" existait
déjà, mais elle n'était pas officiellement supportée
par Macromedia. Elle ne paraissait donc pas dans le Dictionnaire Lingo,
et Macromedia se réservait le droit de modifier son utilisation.
Ce qui a été fait : dans Director 8, elle ne marche pas
de la même façon que dans Director 7. Si vous mettez à
jour une animation où vous utilisiez la version non officielle
de cette commande, vous devez donc changer la syntaxe. De la même
façon, "setPixel()" a aussi pris de l'envergure.
Voir le Dictionnaire Lingo pour les détails.
Tampon d'écran
Lorsque vous faites déplacer une image-objet sur la Scène,
le fond réapparaît inchangé derrière. La partie
du fond cachée par l'image-objet doit être stockée
en mémoire, sinon Director ne saurait pas quoi afficher après
le passage de l'image-objet. En effet, Director réserve un bloc
de mémoire vive appelé "Tampon d'écran",
qui stocke l'ensemble de l'image de la Scène, avant de l'afficher.
Pour imiter cette indépendance de l'image-objet, vous allez créer
votre propre tampon d'écran. C'est une démarche assez gourmande
en mémoire : les dimensions de l'écran doivent être
multipliées par la résolution couleur de l'écran.
Essayez ceci dans la fenêtre de Messages :
put the stage.rect.width * the stage.rect.height * the colorDepth
/ 8 / 1024 && "Ko"
Pour une Scène de 800 x 600 pixels avec une résolution
couleur de 32 bits, cela représente presque 2 Mo, rien que pour
laisser aux images-objets leur indépendance. Puisque Director s'octroie
déjà un espace mémoire de cette taille, il est déconseillé
de la dédoubler. Je le fais ici uniquement dans un but pédagogique...
et avec une petite Scène.
Une image qui en cache une autre
Voici une version dépouillée du comportement "* Mondrian
*". Il vous permet de dessiner une série de rects rouges à
l'écran (dans cette version simplifiée, il faut tirer depuis
le haut à gauche vers le bas à droite). Créez un
nouveau comportement et collez ce script dedans. Glissez le comportement
sur la piste Script de la première image de votre animation, pour
remplacer le comportement qui y est actuellement, puis relancez votre
animation.
property monClicH -- the mouseH on mouseDown
property monClicV -- the mouseV on mouseDown
property monRect -- rect de la zone de peinture active
property monTamponEcran -- copie de l'image de la Scène avant dessin
on mouseDown(me)
-- Commencer ý dessiner un nouveau rect
monClicH = the mouseH
monClicV = the mouseV
monRect = rect(0, 0, 0, 0) -- valeur factice
monTamponEcran = duplicate((the stage).image) -- Ètat actuel
end mouseDown
on exitFrame(me)
if the mouseDown then
-- Restaurer la zone modifiÈe la derniËre fois
(the stage).image.copyPixels(monTamponEcran, monRect, monRect)
-- Garder en mÈmoire la zone qui est sur le point d'Ítre modifiÈe
monRect = rect(monClicH, monClicV, the mouseH, the mouseV)
-- Dessiner ce rect ý sa nouvelle taille
(the stage).image.fill(monRect, rgb(255, 0, 0))
end if
go the frame
end exitFrame
Remarquez que la partie cachée derrière
le rectangle est dessiné en premier, puis le rectangle est dessiné
par-dessus. L'écran n'est pas rafraîchi entre ces deux actions
: il n'y a pas de "updateStage", et Director attend
de passer à l'image suivante avant de remettre la Scène à
jour. Ainsi, l'utilisateur ne voit que le résultat des deux actions,
en une seule fois.
Notez la fonction
"duplicate()" utilisée
sur l'image de la Scène. Pour Lingo, une image est un pointeur.
C'est à dire qu'il contient l'adresse mémoire ou les informations
sur les pixels sont stockées en mémoire. Si deux variables
contiennent une même adresse mémoire, elles peuvent toutes
les deux être utilisées pour manipuler les mêmes pixels.
Une modification faite sur une des variables est immédiatement
ressentie par l'autre. C'est comme deux portes qui donnent sur la même
pièce.
Si on affecte à la variable "monTamponEcran"
le pointeur "(the stage).image", alors les pixels
adressés par monTamponEcran
changeront continuellement pour refléter les changements sur la
Scène. C'est pourquoi il faut créer une copie de l'état
actuel de la Scène, en utilisant la fonction "duplicate()".
C'est cette fonction qui, ici, accapare tant de mémoire vive. Pour
reprendre la métaphore, elle crée une nouvelle pièce
et nous indique où en trouver la porte.
Ici la commande "copyPixels()"
est utilisée d'une manière simple. Dans cet exemple, elle
copie une zone de notre tampon d'écran vers la zone correspondante
sur la Scène. Les images source et destination ont la même
taille, et les zones ont les même coordonnées. Comme vous
avez vu avec le gestionnaire "on triangleJaune"
plus haut, la commande "copyPixels()" peut faire
des manipulations bien plus complexes que celle-ci. Ici, par contre, l'image
qui est copiée n'est pas un simple aplat de couleur. Vous créez
une peinture personnalisée sur la Scène : "copyPixels()"
s'occupe de copier une partie de cette peinture à la manière
d'un emporte-pièce. Les pixels copiés forment un arrangement
unique de rectangles de couleurs différentes.
Une animation qui démontre ce comportement est disponible : depouille.dir.
Pour aller plus loin
L'animation mondrian.dir propose plus de fonctionnalités,
et est construite de façon bien plus rigoureuse, mais le principe
est le même. Le comportement "* Mondrian *" est bien commenté.
Vous pouvez essayer de compléter les deux comportements dépouillés
que vous avez étudiés, en vous inspirant des gestionnaires
de l'animation complète. Vous pouvez même ajouter vos propres
fonctionnalités : pour permettre à l'utilisateur de se servir
d'autres formes (ligne, ovale, quad ...), pour "Annuler", ou
"Rejouer la Création", ...
Si vous avez des questions sur les concepts abordés, vous pouvez
me contacter à newton@planetb.fr.