Comprendre et utiliser les interpolateurs

Thomas Pfeiffer

Dans ce tutoriel, nous allons comprendre et voir comment utiliser des interpolateurs sur des objets 3D créés via la librairie Actionscript 2 Sandy ♪

- Titre : Comprendre et utiliser les interpolateurs

- Auteur : Thomas Pfeiffer

- Parution : 26 juillet 2007

- Licence : Copyright ® 2006-2007 Thomas Pfeiffer. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Comprendre les interpolateurs

- III. 1er exemple

- III-A. Code

- III-B. Rendu

- III-C. Sources

- IV. Savoir réagir aux événements

- V. La souscription

- VI. Deuxième exemple

- VI-A. Code

- VI-B. Rendu

- VI-C. Sources

I - Introduction

Vous voilà déjà peut-être plus intéressé par Sandy. Mais certainement vous vous dîtes, mais comment faire tourner un cube sur lui-même en continu ? Réponse: les interpolateurs (interpolators).

II - Comprendre les interpolateurs

Les interpolateurs s'inscrivent comme des feuilles, et plus précisément comme des enfants de TransformGroup. Pourquoi ? Car un interpolateur va venir modifier la valeur de la transformation associée à son nœud parent en continu. Plus en détail, l'interpolateur va venir mettre à jour la matrice (tiens, vous voyez ce mot pour la première fois alors qu'on parle de 3D depuis 4 tutoriels maintenant !) de transformation au fur et à mesure du rendu.

Par conséquent, il est inutile d'associer une instance de Transform3D par setTransform à un TransformGroup qui possède un Interpolateur ! L'interpolateur va venir automatiquement écraser l'ancienne valeur.

Bon maintenant, voyons quels sont les interpolateurs dont Sandy dispose :

- PositionInterpolator : comme son nom l'indique, cet interpolateur permet de jouer sur la position de l'objet. Vous pourrez donc déplacer votre objet entre deux positions (deux instances de la classe Vector) sur un certain nombre de frames (nombre de rendus nécessaires à réaliser l'entière interpolation) ;

- RotationInterpolator : permet de faire une rotation en continu entre deux angles sur un nombre de frames et selon un axe de rotation donné. Vous pouvez changer l'axe de rotation ainsi que le centre de rotation (point autour duquel l'objet va tourner) à tout moment ;

- ScaleInterpolator : permet d'appliquer des opérations de redimensionnement sur l'objet. Étant donné que l'on peut déformer un objet 3D selon trois axes, il faut renseigner les valeurs de scale pour chacune d'entre elles. Cela se fait par le biais de l'objet Vector. Ainsi pour définir le scale de départ et d'arrivée, il vaut passer en paramètre deux Vectors.

III - 1er exemple

III-A - Code

Réalisons maintenant notre première application avec ces fameux interpolateurs. Nous utilisons ici un RotationInterpolator et nous allons faire faire à notre cube un tour sur lui-même selon l'axe vertical (les valeurs par defaut !) :

import sandy.core.data.*;
import sandy.core.group.*;
import sandy.primitive.*;
import sandy.view.*;
import sandy.core.*;
import sandy.skin.*;
import sandy.util.*;
import sandy.core.transform.*;
import sandy.events.*;

function init( Void ):Void
{
// nous créons notre écran. Élément primordial puisque c'est celui dans lequel les données seront affichées.
var ecran:ClipScreen = new ClipScreen( this.createEmptyMovieClip('écran', 1), 300, 300 );
// nous créons notre camera. Le second paramètre est la distance nodale, qui donne donc l'effet
// de perspective aux objets affichés
var cam:Camera3D = new Camera3D( 700, écran);
// nous ajoutons notre camera au monde 3D
World3D.getInstance().addCamera( cam );
// nous créons notre Group qui servira de racine
var bg:Group = new Group();
// et l'ajoutons au monde 3D. Par défaut le dernier branchGroup ajouté est celui qui est actif
// lors de la demande de rendu
World3D.getInstance().setRootGroup( bg );
// nous lançons la création de la scène
createScene( bg );
// maintenant que tout est créé nous pouvons lancer le rendu du monde
World3D.getInstance().render();
}

function createScene( bg:Group ):Void
{
// nous créons notre objet. Pour cela nous utilisons ici la primitive box.
// cela créé une boite qui, dans le cas où les dimensions de hauteur, profondeur, largeur, sont les mêmes
// génère un cube. Ici nous créons donc un cube de 50 pixels.
var o:Object3D = new Box( 50, 50, 50, 'quad' );
// on créé un skin qui va permettre d'habiller un peu mieux notre objet 3D.
// ici le skin SimpleColor qui demande une couleur de replissage, la valeur de l'alpha et l'épaisseur du trait.
var skin:Skin = new MixedSkin( 0x00FF00, 100, 0, 100 );
// on applique le skin sur l'objet.
o.setSkin( skin );
// Nous créons deux transform Group différents. Chacun correspondant à un noeud de la branche de transformations
// que nous voulons créer.
var tg1:TransformGroup = new TransformGroup();
var tg2:TransformGroup = new TransformGroup();
// Nous créons les objets permettant de transformer notre cube
// Profitons-en d'ailleurs pour commencer à nommer nos variables correctement. Vous le verrez ceci
// deviens vite très important
var translation:Transform3D = new Transform3D();
// creation d'un interpolateur à la place du transform3D
var ease:Ease = new Ease();
ease.linear();
var rotint:RotationInterpolator = new RotationInterpolator( ease.create(), 200 );
// translation de 500 pixels dans l'axe des Z.
translation.translate( 0, 0, 500 );
// Nous appliquons la translation au noeud 1
tg1.setTransform( translation );
// Pour le nœud de rotation, nous n'appliquons plus la transformation comme avant avec le setTransform,
// mais nous ajoutons un fils à ce noeud
tg2.setTransform( rotint );
// nous ajoutons notre objet comme enfant du noeud correspondant à la rotation.
// l'opération de rotation lui sera donc appliquée avant celle de translation
tg2.addChild( o );
// Maintenant nous relions nos deux noeuds, en mettant la translation en opération parent à celle de rotation.
// Cet ordre est très important selon le résultat voulu !
tg1.addChild( tg2 );
// nous ajoutons le transformGroup comme filds du BranchGroup afin de créer l'arborescence
//et rendre l'objet affichable.
bg.addChild( tg1 );
}
// Nous lançons la création du monde 3D
init();

III-B - Rendu

Animation en format "flash" non supportée.
Contenu uniquement consultable en ligne à cette adresse :
https://kiroukou.developpez.com/articles/sandy/v1.1/interpolateurs/images/interpolation-1.swf

III-C - Sources

Télécharger la source (Miroir)

IV - Savoir réagir aux événements

Nous venons de voir que nous sommes capables de faire tourner notre cube simplement. Mais bon, peut-être qu'il serait intéressant de lui faire comprendre de continuer cette rotation continuellement ou sinon, qu'à la fin, notre interpolateur fasse marche arrière.

Et bien rassurez-vous, cela est complètement faisable. Il suffit de comprendre comment réagir aux événements que vous envoient ces interpolateurs !

Voici une liste des événements qu'ils vous envoient :

- onProgressEVENT : envoyé durant la progression de l'interpolation ;

- onPauseEVENT : envoyé lorsque l'interpolateur est mis en pause ;

- onResumeEVENT : envoyé lorsque l'interpolateur sort du statut de pause ;

- onStartEVENT: envoyé lorsque l'interpolation commence ;

- onEndEVENT : envoyé lorsque l'interpolateur finit son interpolation.

Dans le prochain exemple, nous allons nous servir du onEndEVENT. Comment cibler cet événement ? Vous pouvez le faire de plusieurs façons :

- soit par la classe InterpolationEvent et du coup InterpolationEvent.onEndEVENT ;

- soit par votre interpolateur, RotationInterpolator.onEndEVENT.

V - La souscription

Vous pouvez souscrire à un événement aussi simplement que cela :

var rotint:RotationInterpolator = new RotationInterpolator( tg2, ease.create(), 100 );
rotint.addEventListener( InterpolationEvent.onEndEVENT, this, onRotationEnd );

Qu'est-ce que c'est que onRotationEnd ? Si vous n'êtes pas familier avec les événements, peut-être vous posez vous cette question. Les autres, vous devez vous demander quoi mettre dans cette fonction.

Cette fonction est celle qui est appelée lorsque l'événement auquel vous vous êtes abonné est envoyé. Dans cette fonction est passé en paramètre un objet, qui dans le cas des interpolateurs est typé InterpolationEvent. Cet objet permet de retrouver l'objet émetteur, donc l'interpolateur, grâce à sa méthode getTarget().

Mais maintenant, comment lui dire de recommencer ou d'aller en chemin inverse ? Et bien une fois que vous avez récupéré votre interpolateur, vous avez le choix entre deux méthodes :

- redo() : relance exactement la même interpolation ;

- yoyo() : lance l'interpolation en sens inverse.

VI - Deuxième exemple

VI-A - Code

Je pense que vous avez maintenant les éléments en main pour comprendre le deuxième exemple :

import sandy.core.data.*;
import sandy.core.group.*;
import sandy.primitive.*;
import sandy.view.*;
import sandy.core.*;
import sandy.skin.*;
import sandy.util.*;
import sandy.core.transform.*;
import sandy.events.*;

function init( Void ):Void
{
// nous créons notre écran. Élément primordial puisque c'est celui dans lequel les données seront affichées.
var ecran:ClipScreen = new ClipScreen( this.createEmptyMovieClip('écran', 1), 300, 300 );
// nous créons notre camera. Le second paramètre est la distance nodale, qui donne donc l'effet
// de perspective aux objets affichés
var cam:Camera3D = new Camera3D( 700, écran);
// nous ajoutons notre camera au monde 3D
World3D.getInstance().addCamera( cam );
// nous créons notre Group qui servira de racine
var bg:Group = new Group();
// et l'ajoutons au monde 3D. Par défaut le dernier branchGroup ajouté est celui qui est actif
// lors de la demande de rendu
World3D.getInstance().setRootGroup( bg );
// nous lançons la création de la scène
createScene( bg );
// maintenant que tout est créé nous pouvons lancer le rendu du monde
World3D.getInstance().render();
}

function createScene( bg:Group ):Void
{
// nous créons notre objet. Pour cela nous utilisons ici la primitive box.
// cela créé une boite qui, dans le cas où les dimensions de hauteur, profondeur, largeur, sont les mêmes
// génère un cube. Ici nous créons donc un cube de 50 pixels.
var o:Object3D = new Box( 50, 50, 50, 'quad' );
// on créé un skin qui va permettre d'habiller un peu mieux notre objet 3D.
// ici le skin SimpleColor qui demande une couleur de remplissage, la valeur de l'alpha et l'épaisseur du trait.
var skin:Skin = new MixedSkin( 0x00FF00, 100, 0, 100 );
// on applique le skin sur l'objet.
o.setSkin( skin );
// Nous créons deux transform Group différents. Chacun correspondant à un noeud de la branche de transformations
// que nous voulons créer.
var tg1:TransformGroup = new TransformGroup();
var tg2:TransformGroup = new TransformGroup();
// Nous créons les objets permettant de transformer notre cube
// Profitons-en d'ailleurs pour commencer à nommer nos variables correctement. Vous le verrez ceci
// deviens vite très important
var translation:Transform3D = new Transform3D();
// creation d'un interpolateur à la place du transform3D
var ease:Ease = new Ease();
ease.linear();
var rotint:RotationInterpolator = new RotationInterpolator( ease.create(), 100, 45, 180 );
// On ajoute un écouteur à l'interpolateur pour se tenir au courant quand il a fini son trajet
rotint.addEventListener( InterpolationEvent.onEndEVENT, this, onRotationEnd );
// translation de 500 pixels dans l'axe des Z.
translation.translate( 0, 0, 500 );
// Nous appliquons la translation au noeud 1
tg1.setTransform( translation );
// Pour le noeud de rotation, nous n'appliquons plus la transformation comme avant avec le setTransform,
// mais nous ajoutons un fils à ce noeud
tg2.setTransform( rotint );
// nous ajoutons notre objet comme enfant du noeud correspondant à la rotation.
// l'opération de rotation lui sera donc appliquée avant celle de translation
tg2.addChild( o );
// Maintenant nous relions nos deux noeuds, en mettant la translation en opération parent à celle de rotation.
// Cet ordre est très important selon le résultat voulu !
tg1.addChild( tg2 );
// nous ajoutons le transformGroup comme filds du BranchGroup afin de créer l'arborescence
//et rendre l'objet affichable.
bg.addChild( tg1 );
}

// notre fonction qui reçoit l'événement comme quoi la rotation est finie
function onRotationEnd( e:InterpolationEvent ):Void
{
e.getTarget().yoyo();
}

// Nous lançons la création du monde 3D
init();

VI-B - Rendu

Animation en format "flash" non supportée.
Contenu uniquement consultable en ligne à cette adresse :
https://kiroukou.developpez.com/articles/sandy/v1.1/interpolateurs/images/interpolation-2.swf

VI-C - Sources

Télécharger la source (Miroir)