====== Le sol ======
Pour simplifier, on imagine le sol comme une ligne horizontale infinie. On peut définir son emplacement par une seule valeur, sa position en hauteur.
var hauteurSol = 400
On le représente ici via une ligne grise, en utilisant en PaperJs ''Path.Line''.
var sol = new Path.Line(9999, 0)
sol.position.y = hauteurSol
sol.strokeColor = '#ccc'
Pour que la balle réagisse au contact du sol, il faut d'abord déterminer quand ce contact a lieu et, s'il a lieu, adopter un comportement différent. C'est le rôle d'une condition.
===== Le contact =====
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 logiquement plutôt considérer ses bords, c'est à dire sa position plus son rayon (ici c'est ''taille''). Et puisque le sol est horizontal, on n'a finalement qu'à comparer la position verticale du bord inférieur de la balle (''position.y + taille'') à la hauteur du sol (''hauteurSol'').
function onFrame() {
// si la balle touche le sol
if( position.y + taille >= hauteurSol) {
// on stoppe sa vitesse
vitesse = new Point()
} else {
// sinon, elle tombe
vitesse += gravite
}
position += vitesse
balle.position = position
}
[[http://sketch.paperjs.org/#S/bZPBbsIwDIZfJeoOFFGVTloPILEL0k47TOKww9ghBJdGpEmVpEwT4oF5izml7kpFD22cfP5tx+450ryCaBltjuBFGSWRMPtgz+dMcbbjSsHWbvWJW+a5RIutWJ7RVm2c9NJo3NTwwz6M1D5+zrKE4WtK1El6cA7uoDxhNyAEAuaMIrrkjYfGboxCh5cs6yDQJ2mN1lCB9sQeLA/id8oYPM1b6c6TF4UUJT8A24NjV3UNCo4k2hJJgPsyXUsrFMRUWtLV3Sq2cOq8NUdYG2UsOk6eFotFwfPJGPiUe1+G66JQri2pD/QuNcTou6CrwPOUwqa/iP5fBR2PQwshJsNStax4cA920WjRNsfoN4tdjqfsHPYZPsg62XeYedOIEgaNCIwsYjZIZ0YD8DpMbKDZ6WJA501doxan1g+RR9MwJeDCQOHZWNNJHToBt1yr3UPB2YrmoVejRT+nyIxSou+tc4OBpmUALvhj7CzwYx3SddHy6/vyBw==|Voir l'exemple 5 : stop ! le sol !]]
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.
// si la balle va toucher le sol à la position suivante
var positionSuivante = position + vitesse + gravite
if( positionSuivante.y + taille >= hauteurSol) {
// on la stoppe
vitesse = new Point()
} else {
// sinon, elle tombe
vitesse += gravite
}
[[http://sketch.paperjs.org/#S/bVOxbsIwEP0VKx0IAoVUKgNIdEHq1KESQ4fSwZgLsTB2ZDupKpR/7cpf9BxyaUhhCLHvvXf3/JxzpPkJomW0OYIXeTSNhNmH9WzGFGc7rhRs7VZX3DLPJa7Yis1T2iqMk14ajZsavtibkdrHj2k6ZfgYE6qSHpyDG9B8yq6A0AiYM4rQOS89lHZjFBKe0rQFga6kNVrDCbQn7MHyIH6jjM2TeSPdMnmWSZHzA7A9OHZRl6DgSKKxSALc58laWqEgJmvT1nej2IAT5605wtooY5E4elgsFhmfj4aAd7n3eTguauUaS12jV6khRu6CjgLrCbVNvhH6dxRUHrYWQoz6VrU88UAP66zUognH6BeLKcdjdg77DH+IdbJLmHlTihzaINhPKHTRulJWXHsgaj/3TVvDWTr8pMt7QvkQVWbxPyoandDVeu5b7k3bToziOJjzpiigX7p3v8YEqBkorA21nNQhW7i6P+3uCk5WQwc1vfzZXRGcSvR/vQu9T4ReA6DGT21ngR+LMK6Llh+f9S8=|Voir l'exemple 6 : on stoppe avant le sol]]
===== Le rebond =====
Lorsqu'une vraie balle touche le sol, elle s'écrase, se déforme et, 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. Pour simplifier, on peut donc considérer que la force de réaction a pour effet de dévier la direction de la balle, tout en conservant son angle d'incidence. Notre vecteur étant décrit en séparant ses composantes ''x'' et ''y'', il nous suffit d'inverser sa valeur verticale sans toucher à sa valeur horizontale.
Pour l'inverser, il suffit de multiplier par ''-1''.
// si la balle va toucher le sol à la position suivante
var positionSuivante = position + vitesse + gravite
if( positionSuivante.y + taille >= hauteurSol) {
// on dévie sa direction
vitesse.y *= -1
} else {
// sinon, elle tombe
vitesse += gravite
}
[[http://sketch.paperjs.org/#S/bVOxbuMwDP0Vwh3iNjnXBS5DCqRLgZtuOCDDDe0NikzXRBSpkGQXRZF/vTV/UcoxXTetB9sS33vkE6m3zKo9ZrfZZodRN9ki065K6+trMAq2yhh89I+2Ux6iIl7BGpalbD27QJGc5U2LL/DHkY35TVkugF+XguooYgj4CbRcwAmQEiEEZwTdqDZi6zfOMOFnWQ4gtB15Zy3u0UbBPnmVxD8pc/Ji2UsPTFXXpBv1hFBhgKM5JoUgEr1FEVCxKe7Ja4O5WFsMvnvFHlyE6N0O751xnomzi9VqVavl7Bzwl6rYpOOSVKG3NCb6TRZz5q7kKDheSNrilaEfRyHh89Ra69nUqqW9SvS0rlur++Y4+8tzl/NLeEv7wA9jA40dhuha3eDQCPifAmNrQ0udshGFOu37ZohxLSN+Lv0WAtX5FwLbm8tA3U2NTmoc6mTJ6tgRl6agIo9a/AlmSMeSV2v4cSOhA6DhoTuXC2RTU/Fke7/Fb6RgvpbRGtXk58Pn+tyofE9DMLkb8psAB75jW49q95ymNWS3D/8O7w==|Voir l'exemple 7 : le rebond]]
La balle rebondis, oui, mais... trop bien. En inversant comme un miroir la direction verticale, on a créé une balle rebondissante parfaite, qui à chaque rebond remonte exactement aussi haut qu'au départ, sans jamais s'essouffler.
Dans la vie réelle, l'intensité de la force de réaction dépend d'un certains nombre de paramètres, dont le plus caractéristique est l'efficience de rebond caractérisant la matière et la structure de la balle. Cette caractéristique est un paramètre de description physique souvent nommé en anglais //bounciness//.
Au lieu de multiplier par ''-1'' la valeur verticale de la vitesse, on utilise donc cette nouvelle caractéristique de la balle, que l'on définit au préalable.
var bounciness = .7
// si la balle va toucher le sol à la position suivante
var positionSuivante = position + vitesse + gravite
if( positionSuivante.y + taille >= hauteurSol) {
// on dévie sa direction, selon son efficience en rebond
vitesse.y *= -bounciness
} else {
// sinon, elle tombe
vitesse += gravite
}
[[http://sketch.paperjs.org/#S/bVPLbtswEPyVhXqwUjuKCtQoHMC5BOiphwI+9ND0QFGraGGaDEhKRRH4X3v1X3Qpa2XFqQHb4nJm9jGr18yqA2b32W6PUbfZKtOuTue7OzAKKmUMPvkn2ysPURGfYAvrUkKV66wmiyFwuPgi4RcXKJKzHLT4G747sjH/VJYr4J8bQfUUmYhvQOsVnAEpP0JwRtCt6iJ2fucMEz6X5QhC25N31uIBbRTss1dJ/I0yJy/Wg/TIVE1DulXPCDUGOJlTUghTZ+rc6yCgYls8ktcGc2ltNY5jUBzARYje7fHRGeeZuPiw2WwatV5cA35QHds0RUkVhpamRN94njlzNzIKvi8kbfGHoZdRyPV1aq31Yt6qpYNK9HRu2LLBHGe/ejY/v4HXFAf+MDbQZDxE1+kWRyPgb7qYrA0d9cpGFOrc9914x7VM+KX4LQRq8ncEbm8pe/Ywb3RW41gnS9annrg0BTV51GdXAppUHH8x+UtoNfKSgMfK2XquMZbDKT9u4fayyYI5Ahrezuu8gWzKg+f5HCr8jyYst7KDk5o8XAayvZ6I/J+3ZfYSyWMCHPkdrTyq/Uta65Dd//x1/Ac=|Voir l'exemple 8 : bounciness]]
Si on multiplie par zéro, la balle ne rebondit pas une seule fois. Si on multiplie par une valeur faible, par exemple ''.2'', chaque rebond est drastiquement plus faible que le précédent et la balle rebondis peu de fois, comme pour une boule de bowling. Si on multiplie par une valeur plus proche de 1, par exemple ''.7'', chaque rebond est un peu plus faible que le précédent mais la balle rebondis beaucoup plus de fois, comme pour un ballon de foot.
[{{ http://www.exploratorium.edu/baseball/activities_images/bounce_chart.gif |Efficience du rebond de quelques types de balles.}}]
===== Un peu de 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, dont l'effet est de freiner un objet lorsqu'il y a contact.
Il suffit de réduire cette fois la vitesse générale de la balle lorsqu'elle est en contact avec le sol, selon une nouvelle caractéristique définie au préalable.
var friction = .9
// on freine sa vitesse
vitesse *= friction
Si on multiplie par zéro, la balle est immédiatement stoppée au contact du sol. Avec une valeur faible, par exemple ''.2'', la balle est très vite ralentie par le sol, comme tombant dans du sable. Si on multiplie par une valeur très proche de 1, par exemple ''.9'', la balle est lentement ralentie par le sol, comme roulant sur un sol lisse.
En multipliant par ''1'', la balle n'est plus freinée du tout, comme sur de la glace, et avec une valeur supérieure à 1 la balle accélère même au contact du sol, comme s'il s'agissait d'un accélérateur de jeu vidéo.
[[http://sketch.paperjs.org/#S/bVRNb9swDP0rhHeIu2RuBiwoXCC7FOiphwE57LDuIMt0TUSRCknOPor8113zL0Y5puOmDZDEIh8fyUfKL5lVO8xus80Wo26zRaZdnc7X12AUVMoYfPSPdq88REV8gjWslmKqXGc1WQyBzcWNmBtPOpKzyViK8dkFGowWf8E3Rzbmn5fLBfDPlaD2FJkNX4FWCzgBUlEIwRlBt6qL2PmNMxzwZbkcQGj35J21uEMbBfvkVSJ/xczJi1VPPUSqpiHdqieEGgMczTExhLFddRKgJ1CxLe7Ia4O5tLYYNOoZe3ARondbvHPGeQ6cfSjLslGr2SXgO9WxTdJKqtC3NCZ6YJFzji1FCvYXkrb4w9CzFOK+TK21nk1btbRTKTydG55jPxxn7z1vRH4FL8kO/GFsoHEbILpOtzgMAv4lxzja0NFe2YgSOp37ZvBxLSN+LvOWAGryNwHc3lyW7+u00UmNQ51MWR/3xKUpqMmjPk0loEnF8RfTfAmtRl4S8Fg5W085hnI45cc1fDqv99s8jUf2pESysq3z9NfZqAy+R/k7UcrNEMAB0HDoZR+BbKobT3rvqvcIYb6WnR7Z5OEs8PpSYfk/bd/kUspjAhz4RVB5VNvndE1Cdvvj5+E/|Voir l'exemple 9 : friction]]
[[http://sketch.paperjs.org/#S/hVTBbqMwEP0Viz0AhbXIbnMIXfZSaU9baaUc9pDmYBNTrFA7MiarquLfdwYwwWmrIkXBM++N38w88Roo9iyCPNgehS3rIA1KfcDzmRmiuCEFWWWP5lHh+aRbaaVWEFTiH/mjpbLRKstSwGSxQ9Wss6IzW90A7jab2U+GnaUVHhmodD0w8cdZ04gWALs9HittIiTKIrsj8geogb8kickrZgk8mEWJD8zW1DB10M9RfHObJaNmB+EAmTn4hJZJuCrMTeqFUV/bQvyi0C+9AsF+5Hvsl+C6U6VUUCbMPaQPq4wscZRhDgMgyVVRuvbRJ8hOohB3L03ZiMjfAa4gJSZ2xN69cIp02lqjj+JeNxonFn7ZbDYVW4fvov7Kg60B9W3ODpuhp66tIz5c0bu1tsOeZ22/ofcIam9SMloC8tQZh74A9OIPl76WVpZl6DxRwTgHz2n1y4BVo8X6PzfI0gFjDzu5v046ddtOnpkaHDpNYzZ8ApHJHPA+OXlZR1bRmzrQLfJGr5Gfy86vJI4LmC4A2k1BvnJ6sdKHWERy6sy0hPVENJD/+BqSFO810i8Pb8ZQXPiez3r4cHAj2PGEfmyDfLfv/wM=|Démo : bounciness et friction aléatoire]]
=====Conclusion=====
// la balle
var taille = 50
var bounciness = .7
var friction = .9
var position = new Point(100, 100)
var vitesse = new Point(5, 0)
// le sol
var hauteurSol = 400
// environnement
var gravite = new Point(0, .5)
// affichage des éléments
var balle = new Path.Circle(position, taille)
balle.strokeColor = '#999fa5'
balle.strokeWidth = 5
var sol = new Path.Line(9999, 0)
sol.position.y = hauteurSol
sol.strokeColor = '#ccc'
// animation
function onFrame() {
// si la balle touche le sol à la position suivante
var positionSuivante = position + vitesse
if( positionSuivante.y + taille >= hauteurSol) {
// on dévie sa direction, selon son efficience en rebond
vitesse.y *= -bounciness
// on freine sa vitesse
vitesse *= friction
} else {
// sinon, elle tombe
vitesse += gravite
}
position += vitesse
balle.position = position
}
Ce petit bout de code ne repose que très peu sur PaperJs, et ne lui délège principalement que la gestion du rendu visuel, sans que la mécanique de base ne repose dessus. On peut donc utiliser n'importe quel autre méthode, librairie ou logiciel pour représenter le résultat, qu'il s'agisse de déplacer des cubes sous Blender ou tracer des croix sur une feuille de papier.