Utiliser les forces pour mouvoir des objets
Bonjour à tous. Cet article à pour but de vous montrer comment utiliser les forces physiques pour faire bouger des objets dans une animation Flash en AS3. Des connaissances physiques seraient un plus, mais si vous n’y connaissez rien vous pouvez bêtement appliquer les formules disponibles sans vous soucier du pourquoi du comment.
Les forces physiques permettent d’appliquer des mouvements réalistes aux objets, et nous allons via une application pratique, après la théorie, le démontrer en créant une animation Flash faisant voler un petit vaisseau spatial en prenant compte de toutes les forces (poids, friction, poussé réacteurs) qui s’appliquent à lui.
Note : vous devez avoir les bases en AS3, c'est-à-dire la connaissance des principes de la POO, l’organisation globale des packages, la Document Class.
Notions abordées :
- Document class
- Physique
- Trigonométrie
Sommaire du cours :
I. Présentation des lois et de l’animation
1) Les propriétés physiques nécessaires 2) L’animation à réaliser
II. Création des bases de l’animation
1) Le contexte graphique 2) Paramétrage animation & code de base
III. Implémentation des fonctions
1) Trouver l’accélération en fonction des forces et du temps passé 2) Définir les forces présentes
IV. Conclusions
1) Sur l’animation réalisée 2) Aller plus loin
A la fin de cet article, vous obtiendrez une animation ressemblant à celle-ci-dessous (sans les vecteurs et le bouton en haut à droite). Pour ceux qui n’auraient pas suivit les explications, un fichier ZIP contenant les sources et l’animation est disponible à la fin de cet article.
animation finale :
I. Présentation des lois physiques et de l’animation
1) Les propriétés physiques nécessaires
Nous allons utiliser des forces physiques qui s’appliquent au vaisseau. Il nous faudra aussi calculer l’accélération du vaisseau en fonction de ces forces, et du temps qui s’est écoulé entre le précédent calcul d’accélération.
Sachant que pour une force : F=1N, (N=Newton) et un corps d’une masse : m=1kg, on à une accélération : λ = 1m/s/s. On peut donc définir notre loi physique qui détermine une accélération λ (lambda) par seconde par seconde en fonction d’une force :
λ=F/m
Le tout est à multiplier par le temps écoulé depuis le dernier calcul de l’accélération en secondes, ce qui nous donnera la valeur de l’accélération en m/s par rapport au dernier calculs effectués, en effet λ est en m/s/s et non juste m/s.
Explications : Une accélération est en m/s. C'est-à-dire que chaque seconde du temps qui passe, la vitesse d’un corps en m/s sera modifiée par cette accélération. Ici on récupère un résultat en m/s/s (mètres par seconde par seconde) ce qui veut dire que l’accélération d’un corps va augmenter de cette valeur chaque seconde. En faite ce n’est pas l’accélération d’un corps à proprement parler que l’on calcule mais l’accélération qu’il subit par seconde donc en (m/s)/s. Si on multiplie ce résultat par le temps écoulé depuis notre dernier calcul en secondes on obtiendra l’accélération en m/s.
Nous allons appliquer 3 forces à notre petit vaisseau :
-Son poids :
- P=m.g (on prendra g=9.8N/kg et m=300kg)
- Direction : droite passant par centre de gravité du vaisseau et verticale
- Sens : vers le bas
-Friction de l’air
- Complexe à déterminer, nous utiliserons une formule extrêmement simplifié qui consiste à définir la force comme une fraction du vecteur vitesse donc F=x.v avec x=facteur vitesse (x<v).
- Direction : la même que la direction du vecteur vitesse
- Sens : opposé à celui du vecteur vitesse.
-Poussé des réacteurs :
- R=m.g/125*l (avec m.g=poids et l = distance sourie/vaisseau en pixels)
- Direction : droite passant par le curseur de la souris et le centre gravité du vaisseau
- Sens : vers le curseur de la souris
2) L’animation à réaliser
Cette partie fera office de cahier des charges. Nous allons créer un contexte visuel simple pour cette animation de démonstration. Un petit vaisseau de 100 pixels de large qui sera orienté (le nez) plus ou moins haut en fonction de la vitesse de l’appareil. Sur ce vaisseau, un réacteur orientable qui exercera une poussé vers le curseur de la souris, nous le contrôlerons via le code. Justement, au niveau code, de quoi aurons-nous donc besoin ? Une fonction pour calculer une force globale (la résultante) équivalente à toutes les forces appliquées au vaisseau, une autre fonction pour calculer l’accélération du vaisseau en m/s en fonction de cette force et du temps passé, et une dernière fonction pour mettre à jour tout cela via un Timer appelé rapidement (toutes les 10ms). J’ajouterais personnellement un bouton pour montrer ou cacher une représentation graphiques des vecteurs forces et vitesse qui s’appliquent au vaisseau, mais vous n’aurez pas cette partie du code, se sera à vous d’essayer de la faire vous-même car c’est un bon exercice, et ça na pas grand-chose à voir avec le sujet principal de l’article : la physique;).
Encore une dernière petite précision. Sur la scène nous prendrons comme échelle 1px = 1m. Cela facilitera le positionnement du vaisseau après calcul de l’accélération, mais comme notre scène représentera du coup 640m par 480m il faudra mettre des forces assez puissantes, nous allons voir cela par la suite lorsque nous coderons le calcul des forces.
II. Création des bases de l’animation
1) Le contexte graphique
Nous travaillerons via l’IDE Flash CS3 pour éditer les graphismes et compiler notre code AS3. Ouvrez un nouveau document Flash AS3, et sauvegardez ce fichier sous le nom ‘Main.fla’.
Dessinez sur la scène principale un vaisseau d’environ 100 pixels de large vu de coté. Convertissez ce vaisseau en clip (modification/convertir en symbole) et donnez lui comme nom d’occurrence (dans le panneau de propriétés [fenêtres/propriétés]) le nom ‘vaisseau_mc’. Pour finir, centrez le vaisseau de façon à ce que l’origine du clip (0 ; 0) corresponde au centre de gravité du vaisseau (environs le centre en largeur & hauteur)
Dans ce clip vaisseau_mc, créez un réacteur au milieu du vaisseau. Convertissez ce réacteur en clip et donnez-lui comme nom d’occurrence ‘reacteur_mc’. placez son centre de rotation en son milieu (placez le réacteur centré sur son origine [coordonnées 0,0]), en effet le réacteur tournera sur lui-même. Vous devriez avoir maintenant sur votre scène principale un clip de vaisseau, et dans celui-ci un clip de réacteur.
Si vos graphismes ne sont pas très jolis (comme les miens par exemple) cela ne fait rien, ici il est important de souligner que c’est l’aspect ‘physique’ des forces qui s’appliqueront sur le vaisseau qui nous intéresse, au niveau du code, qui va arriver juste après.
Pour terminer cette partie graphismes, je souligne que pour plus de réalisme vous devriez ajouter quelques flammes à votre réacteur. Pour faire rapide et simple, dans un nouveau clip dans ce réacteur, ‘gribouillez’ des flammes jaunes, oranges et rouges puis faites une interpolation de forme vers d’autres flammes et appliquez sur le tout (sur le clip des flammes) un filtre de flou à 10px, cela marche relativement bien pour un rendu pas trop médiocre…
2) Paramétrage animation & code de base
Nous allons commencez par paramétrer l’animation via la fenêtre modification du document (document/modifier ou directement via le panneau de propriétés) : passez le frame rate (images par seconde) à 48fps. Personnellement mon animation fait 640*480px mais libre a vous de choisir une autre taille .
Pour coder cette animation, nous allons utiliser une Document Class qui se nommera Main, mettez donc grâce au panneau propriété de Flash le nom ‘Main’ dans la case ‘document class’.
Maintenant nous allons créer le code de base de l’animation : la structure de la class Main et les prototypes des fonctions dont on se servira par la suite, c'est-à-dire une fonction pour trouver une force résultante de plusieurs forces, une fonction de mise à jour de l’écran (update) appelé par un Timer et pour finir une fonction pour calculer l’accélération du vaisseau. Donc, tout d’abord créez un fichier ‘Main.as’ pour notre Document Class.
//////////////////// package { import flash.display.Sprite; //notre scene heritera de Sprite et non pas de MovieClip bien trop lourd import flash.events.TimerEvent; //pour detecter l'evenement TIMER du timer d'update public class Main extends Sprite { public function Main() { } private function getForce(...$forces:Object):Object { //cette fonction retournera une force globale de toutes les forces passées en parametres return(new Object()); } private function getAceleration($timeElapsed:Number, $force:Object, $weight:Number):Object { //cette fonction retournera une acceleration (vecteur) en fonction temps écoulé et force globale return(new Object()); } private function update(e:TimerEvent):void { //fonction principale de mise à jour de l'écran } } } //////////////////
III. Implémentation des fonctions
Dans cette partie nous allons coder nos trois fonctions, en faite finir notre animation en somme. Tout d’abord nous allons coder la structure de l’animation : les fonctions getForce et getAcceleration. Puis nous nous attarderons pour finir à la fonction update en ajoutant des forces au vaisseau, effectuant les déplacements et rotations nécessaires, cette partie sera un peu plus longue que les autres.
1) Trouver l’accélération en fonction des forces et du temps passé
Nous allons commencer par la fonction getForce (la plus simple). Elle prend en paramètre des forces (un nombre illimité de forces grâce à ‘…’ juste avant le nom de paramètre $force qui transformera ce paramètre en une sorte de ‘tableau’ de paramètres) de type Object qui devront comporter deux composantes : x et y. En retour on donne une nouvelle force sous forme d’Object encore une fois avec aussi les composantes x et y. Pour trouver la force résultante des autres, il suffit d’additionner toutes des composantes x et y ensembles. Nous allons donc parcourir toutes les forces passées en paramètre, et ajouter leurs composantes à celle de la force finale que l’on retournera :
//////////////////// private function getForce(...$forces:Array):Object { //cette fonction retournera une force globale de toutes les forces passées en parametres var toReturn:Object={x:0, y:0}; //on ajoute toutes les composantes à la force resultante : for(var i:int=0; i<$forces.length; i++) { toReturn.x+=$forces[i].x; toReturn.y+=$forces[i].y; } //on retourne la nouvelle force return(toReturn); } ////////////////
Maintenant nous allons attaquer une fonction un peu plus complexe : getAceleration. Nous allons tout d’abord trouver l’accélération en mètres par seconde par seconde (non ce n’est pas une faute de frappe) du corps en fonction de la force passée en paramètre, puis nous donnerons cette accélération en mètres par seconde via le temps qui s’est écoulé depuis le dernier calcul de l’accélération (paramètre $lastTime [en s]) La fonction prend un troisième paramètre, c’est le poids de l’objet à déplacer, en effet plus un objet est lourd, plus il faudra une force importante pour le déplacer (rappel pour une masse de 1kg il faut 1N pour avoir une accélération de 1m/s/s):
//////////////////// private function getAceleration($timeElapsed:Number, $force:Object, $weight:Number):Object { //cette fonction retournera une acceleration (vecteur) en fonction temps écoulé et force globale //on calcul l'aceleration en m/s/s : var velocity:Object={x:0, y:0}; velocity.x=$force.x/$weight; velocity.y=$force.y/$weight; //on la passe en m/s : velocity.x*=$timeElapsed; velocity.y*=$timeElapsed; //on retourne l'acceleration en m/s : return(velocity); } ////////////////
2) Définir les forces présentes
Il nous reste à implémenter la fonction principale, la fonction update qui sera appelée toutes les 10ms par un Timer que l’on va créer via le constructeur de notre class Main. Dans la fonction d’update nous allons calculer les forces qui s’appliquent au vaisseau, puis trouver l’accélération pour le bouger en fonction de ces forces. Puis nous finirons avec quelques petites améliorations graphiques comme l’orientation du réacteur vers la souris et une inclinaison du vaisseau en fonction de sa vitesse.
Commençons d’abord par créer les variables minimums et lancer la fonction d’update. Il nous faudra donc un Timer, lui ajouter son écouteur d’événement, et créer un objet (Object) pour le vecteur vitesse du vaisseau (avec les propriétés x et y):
//////////////////// package { //les autres imports... import flash.utils.Timer; //timer pour mettre à jours l'ecran import flash.events.TimerEvent; //pour déclancher la fonction d'update import flash.utils.getTimer; //pour trouver le temps écoulé entre deux calculs public class Main extends Sprite { //nouvelles variables pour gérer la vitesse et la mise à jour de l'écran private var updateTimer:Timer; private var speed:Object; private var lastTime:Number; //dernier temps en ms public function Main() { //initialisation speed={x:0, y:0}; lastTime=getTimer(); updateTimer=new Timer(10); updateTimer.addEventListener(TimerEvent.TIMER, update); updateTimer.start(); } //les autres fonctions de la class... } } ///////////////
Ok, maintenant on s’attaque à la fonction update. On va calculer les 3 forces, trouver la force résultantes ce ces 3 forces, puis l’accélération en conséquence que l’on appliquera à la vitesse du vaisseau. Enfin nous bougerons notre vaisseau en fonction de sa vitesse :
//////////////////// private function update(e:TimerEvent):void { //fonction principale de mise à jour de l'écran //le poids var forceP:Object={x:0,y:9.8*3000}; //(3000 = poid du vaisseau [en kg] ) //la friction var forceF:Object={x:speed.x, y:speed.y}; //var forceF:Object={x:0, y:0}; //la poussée des réacteurs : (orienté vers le curseur de la souris); var angle:Number=Math.atan2(vaisseau_mc.y-mouseY, vaisseau_mc.x-mouseX); var distance=Math.sqrt(Math.pow(mouseX-vaisseau_mc.x,2)+Math.pow(mouseY-vaisseau_mc.y,2)); var forceR:Object={x:-Math.cos(angle)*distance*forceP.y/80, y:-Math.sin(angle)*distance*forceP.y/80}; //on retrouve une force globale : var resultante:Object=getForce(forceP, forceF, forceR); //on retrouve l'acceleration : var acceleration:Object=getAceleration((getTimer()-lastTime)/1000, resultante, 3000); lastTime=getTimer(); //on change la vitesse : speed.x+=acceleration.x; speed.y+=acceleration.y; //on bouge le vaisseau : vaisseau_mc.x+=speed.x; vaisseau_mc.y+=speed.y; } ///////////////
Vous trouvez peut être que le résultat n’est pas satisfaisants mais c’est un effet qui découle du fait que notre vaisseau n’est pas encore animé. Orientons les réacteurs comme il le faut (vers le curseur de la souris), changeons la taille des flammes en fonction de la puissance des réacteurs, et faisons s’incliner le vaisseau en fonction de sa vitesse, sa devrais aller mieux après
//inclinaison du réacteur : vaisseau_mc.reacteur_mc.rotation=angle*180/Math.PI-90; //conversion de radians en degres //taille des flammes en fonction puissance réacteurs : vaisseau_mc.reacteur_mc.flammes_mc.scaleY=Math.sqrt(Math.pow(forceR.y,2)+Math.pow(forceR.y,2))/1000; //legere inclinaison du vaisseau en fonction vitesse : vaisseau_mc.rotation=speed.x*4; /////////////////
Et voila, l’animation est plus réaliste. Ce qu’il faut savoir c’est que le fait de diriger l’orientation des réacteurs via la souris peut déstabiliser certaines personnes au niveau du maniement, mais rien ne vous empêche de remplacer cela par un contrôle via clavier ! Après tout nous avons plutôt étudié l’aspect physique et non le gameplay de l’animation. Vous pouvez aussi faire bouger le décors et non le vaisseau de –speed.x et –speed.y ce qui permet de se diriger sur des grandes distance plutôt que reste sur la petite fenêtre de l’écran.
IV. Conclusions
Sur l’animation réalisée
L’animation est maintenant terminée. Dans l’exemple final, j’ai inclut un bouton pour afficher/cacher les vecteurs représentant les forces qui s’appliquent au vaisseau et sa vitesse. Cette animation avait pour but de vous montrer comment fonctionne un déplacement par forces via les lois physiques que nous connaissons. Cela apporte plus de précision dans les mouvements par rapport à la réalité. On utilise ce procédé dans les moteurs physiques notamment.
Aller plus loin
Comme dit plus haut, vous pouvez intégrer à votre animation d’autres méthodes de contrôle comme le clavier. Mais cette partie aborde plutôt ce que vous pourriez faire au niveau de la physique pour aller plus loin, car c’est le sujet principal de cet article. A l’aide des fonctions proposées, vous pourriez créer des classes Vector2D et ForcesManager pour mieux organiser le code et avoir le moins de formules physiques possible dans les fonctions principales. Si vous le souhaitez, mais cela est beaucoup plus dur, vous pourriez inclure une gestion des collisions en calculant les forces de réaction du support et les frottements… Mais cette fois des notions assez bonnes en physique vous serons nécessaires, vous ne pouvez pas y échapper. Bon courage
Sources
Ce tutorial touche à sa fin, j’espère qu’il vous à plu
Télécharge le fichier ZIP du projet final

Télécharger le fichier ZIP du projet modifié (vecteurs forces & vitesse)
Télécharger toutes les étapes du code
A bientôt,
Stefbuet
