**Ceci est une ancienne révision du document !** ----
====== Le sol ====== <WRAP center alert> WIP, en cours de réécriture </WRAP> Pour simplifier, on représente le sol par une ligne horizontale infranchissable, située à une certaine hauteur. <code javascript> // le sol var hauteurSol = 400 var sol = new Path.Line(new Point(0, hauteurSol), new Point(800, hauteurSol)) sol.strokeColor = '#ccc' sol.strokeWidth = 2 </code> La ''position'' de la balle est un point situé en son milieu, à l'intérieur du cercle. Pour savoir quand la balle touche le sol, il nous faut considérer le point de sa surface situé en bas. Comme le sol est horizontal, on peut comparer uniquement la position verticale des éléments. Et comme la valeur ''taille'' correspond au rayon de notre balle, la balle touche le sol si sa position verticale plus son rayon est égale à la hauteur du sol. ===== L'arrêt ===== La balle ne peut traverser le sol, elle ne doit donc pas tomber si elle est en son contact. C'est le rôle d'une condition, que l'on place dans la boucle. <code javascript> function onFrame() { // ajout de la force de gravité vitesse += gravite // si la balle touche le sol if( balle.position.y + taille >= hauteurSol) { // on la stoppe vitesse = new Point() } else { // la balle tombe balle.position += vitesse } } </code> <WRAP center info > [[http://sketch.paperjs.org/#S/fVPLbsIwEPwVKz2QiCikUlELEr0g9dRDJQ49lB6MWRoXxxvZTqoK8cH9i3oDhoSiWkpk787s7MPeRZqXEE2jxRacKKI0Erim82jEFGcrrhQszVI33DDHpT+xGRvnwVShlU6i9kYNX+wFpXbxbZ6nzP+SgGqkA2uhB7pP2RnQygQ3d0U2l0YoiEP49KjdElpwZp3BLcxRofHEwc1kMtnw8eAS8CrXrqCUyUGfrwt0Iw1qDSVoF1L4MJzS7OVIZSQdos/RogqMgtcOarNA5Ul3+akntjWcSnmWGuJezDMxSTtqD3nf1yr7YH9KFUIMOllxLUtOXaLzptaiHQjqJ+MnGydsR3bml9zEh05noa/ZNxuGsT7OuuJnFq3OZWAOa1HAsRep1yGXdVhV0KVcm3kSAPuwAeUh/0iVK7hw8k+sHVsDoTZoBND+MLyfa/rDWRjtRaAS6+Z0A4K93x4iH+P0Mt/7Z7IywLcVlWWj6dv7/hc=|Voir l'exemple 5 : stop ! le sol !]] </WRAP> La balle chute, et une fois... **dans** le sol, s'arrête. Le problème est que notre condition vérifie si la balle est **déjà** dans le sol pour l'arrêter, il est donc un peu trop tard pour réagir. Pour palier à ça, on vérifie si la balle va se retrouver dans le sol **à la frame suivante**, et si c'est le cas, on l'arrête avant. <code javascript> function onFrame() { // ajout de la force de gravité vitesse += gravite // calcul de la position suivante var positionSuivante = balle.position + vitesse // si la balle touche le sol à la position suivante if( positionSuivante.y + taille >= hauteurSol) { // on la stoppe vitesse = new Point() } else { // sinon la balle tombe balle.position = positionSuivante } } </code> <WRAP center info > [[http://sketch.paperjs.org/#S/fVPLasMwEPwV4R5iE5O40NAmkF4CPfVQyKGHpgdFWddqZMlIsksJ+dde+xddOZYfSajBRtqdndmXD4GkOQSLYL0Hy7IgDpjauft0SgQlWyoEbPRGVlQTSzneyJLMEm8qlOGWK4lGCV/kRXFpw9skiQl+Io+quAVjYAC6j0kHqGW8m9pssuKaCQg9fdxo1wE1eGKsVntYKaE0Bo5u5vN5Smejc8Ar39nMpewc7nV1ATFKeO2MlhZKvVYCYXdJW5qpDW1Gz1xC2KU/wxK7yCjuVfaQDH110sh2kTJjbNRLC2TFtZIScpDWZ/GhqeveoHVIP5lFvUgqeU5dn9w9LSWrR6Lkk8bZhhE5ODvBx2E/VWnJDtx0U6UZuPNJ5dfD/LzGS6/vPT0iRgUrRcPU7oEpeUVlF+Ad68aOhZzG00aMvdwVDcPbJSRWlSyDZnjk519VnoYXypNvlGpW+HHZH1DXn0YWGZHdWFUU0Hdd2+PIA47+AAIh55S9MvLtgPSsHcuLxAcCR/xDtxrovnDqJli8vR//AA==|Voir l'exemple 6 : on stoppe avant le sol.]] </WRAP> ===== Réaction ===== Lorsqu'une vraie balle touche le sol, de par son élasticité et parce qu'elle ne peut traverser ce dernier, elle s'écrase et se déforme. Une fraction de seconde plus tard, tentant de retrouver sa forme originale, elle est propulsée vers le haut. C'est une force de réaction, perpendiculaire à la surface de contact. Comme notre sol est horizontal, la force de réaction est verticale. <code javascript> var reaction = new Point(0, -vitesse.y * 2) </code> Dans notre condition, quand nous savons que la balle touche le sol, au lieu de la stopper nous allons calculer cette force de réaction et l'ajouter à la vitesse. <code javascript> function onFrame() { // ajout de la force de gravité vitesse += gravite // calcul de la position suivante var positionSuivante = balle.position + vitesse // si la balle touche le sol à la position suivante if( positionSuivante.y + taille >= hauteurSol) { // on calcule la force de reaction var reaction = new Point(0, -vitesse.y*2) // on l'ajoute à la vitesse vitesse += reaction } else { // sinon la balle tombe balle.position = positionSuivante } } </code> Et voila notre balle qui rebondis! [{{articles:mel-python:animation_procedurale:balle_sol_rebond.png|La condition permet de passer ponctuellement d'une force de gravité à une force de réaction, entraînant le rebond.}}] ===== Efficience ===== Vous pouvez remarquer que quel que soit le nombre de rebonds effectué par la balle, jamais elle ne s'arrête. Nous avons en effet créé ici une balle rebondissante parfaite, qui à chaque rebond remonte exactement aussi haut qu'au départ, sans jamais s'essouffler. Pour changer cela il n'y a qu'à modifier l'**intensité de la force de réaction**. L'efficience du rebond d'une matière étant un réel paramètre de description physique (en anglais "bounciness"), si l'on veut être plus précis cela correspond à : <code python> # regles nextPosition = vectorAdd(position, vitesse) if nextPosition[1] - taille <= sol: # rebond bounciness = 0.75 reaction = [0, -vitesse[1] * (1 + bounciness), 0] vitesse = vectorAdd(vitesse, reaction) else: # chute vitesse = vectorAdd(vitesse, gravite) </code> Quelques indications : * **0.0** : rebond nul, l'objet ne rebondis pas. * **0.3** : rebond réaliste peu efficient, chaque rebond est beaucoup plus faible que le précédent (type boule de bowling) * **0.7** : rebond réaliste efficient (type ballon de foot) * **0.9** : rebond réaliste très efficient, chaque rebond est presque aussi important que le précédent (type balle rebondissante) * **1.0** : rebond parfait, chaque rebond est aussi important que le précédent (mouvement perpétuel) * **>1** : rebond irréaliste, chaque rebond est plus important que le précédent (type flubber) [{{http://www.exploratorium.edu/baseball/activities_images/bounce_chart.gif|Efficience du rebond de quelques types de balles.}}] ===Friction=== Si maintenant la taille du rebond décroit avec le temps, le mouvement de la balle initié par la force de départ est lui toujours perpétuel : même sans rebondir, elle continue de glisser sur le sol. Il nous manque un paramètre de **friction**. L'effet de la friction est de freiner un objet lorsqu'il y a contact. Qui dit freiner dit diminuer sa vitesse. Il nous faut donc réduire la longueur du vecteur vitesse quand il y a contact avec le sol. Comme vu dans le [[admin:scriptspython#Addition, soustraction, multiplication|chapitre sur les vecteurs]], il s'agit d'une simple multiplication du vecteur avec un nombre. Je récupère donc la fonction à cet effet : <code python> def vectorMult(a, b): return [a[0]*b, a[1]*b, a[2]*b] </code> Dans notre condition, après avoir calculé la force de réaction et la nouvelle vitesse, nous n'avons plus qu'à multiplier cette dernière : <code python> # regles nextPosition = vectorAdd(position, vitesse) if nextPosition[1] - taille <= sol: # rebond bounciness = 0.75 friction = 0.95 reaction = [0, -vitesse[1] * (1 + bounciness), 0] vitesse = vectorAdd(vitesse, reaction) vitesse = vectorMult(vitesse, friction) else: # chute vitesse = vectorAdd(vitesse, gravite) </code> Quelques indications : * **0.00** : friction maximale, l'objet est immédiatement stoppé au contact de la surface. * **0.25** : friction réaliste forte, l'objet est très vite freiné par la surface (type sable) * **0.99** : friction réaliste faible, l'objet est lentement freinée par la surface (type de sol lisse) * **1.00** : friction nulle, l'objet n'est pas freiné par la surface (type glace) * **>1.00** : friction irréaliste, l'objet est accéléré par la surface (type accélérateur de jeu de course) =====Code final===== <code python> from maya.cmds import polySphere, currentTime, move # fonctions def vectorAdd(a, b): '''Somme de deux vecteurs''' return [a[0] + b[0], a[1] + b[1], a[2] + b[2]] def vectorMult(a, b): '''Multiplication de vecteur par un nombre''' return [a[0] * b, a[1] * b, a[2] * b] # conditions initiales taille = 5 balle = polySphere(radius=taille) position = [0, 50, 0] vitesse = [1, 0, 0] bounciness = 0.75 friction = 0.95 # environnement gravite = [0, -0.1, 0] sol = 0 # animation time = 100 for frame in range(1, time): # regles positionGravite = vectorAdd(position, vitesse) if positionGravite[1] - taille <= sol: # rebond reaction = [0, -vitesse[1] * (1 + bounciness), 0] vitesse = vectorAdd(vitesse, reaction) vitesse = vectorMult(vitesse, friction) else: # chute vitesse = vectorAdd(vitesse, gravite) # mouvement position = vectorAdd(position, vitesse) currentTime(frame) move(position[0], position[1], position[2], balle) </code> =====Conclusion===== Vous remarquerez que l'on utilise seulement trois fonctions de Maya : **polySphere**, **currentTime** et **move**, qui servent uniquement à visualiser le résultat de nos calculs. Cette procédure est donc transposable à tout langage, tout logiciel permettant de programmer (Flash, Processing, Blender...) et n'est en rien rattaché à Maya. Il s'avère par la même occasion que les temps de calculs sont infiniment plus rapides que si l'on avait usé et abusé des fonctions internes au logiciel.