8Fermer10
bearbecueLe 25/04/2009 à 22:23
rrzzz ?

hum.
comme dit Jyaif, avoir la normale de ton terrain peut aider.

en gros ce que tu cherche, c'est la normale a la derivee de la courbe representant ta bitmap en un point donne. pour un terrain sans formations concaves (genre trous ou galleries), une facon rapide d'obtenir ca est de prendre la hauteur du pixel a gauche (P-1) et du pixel a droite (P+1) de celui que tu intersectes (P), de calculer les vecteurs [P-1, P] et [P, P+1], qui auront pour coordonnee {1, hauteur(P) - hauteur(P-1)} et {1, hauteur(P+1) - hauteur(P)}, de faire leur moyenne (tu peux faire une moyenne ponderee par les longueurs des deux vecteurs suivant comment tu veux approximer la courbure de ton terrain), et de calculer la normale au vecteur tangent resultant (normale.x = -tangente.y, normale.y = tangente.x).

mais bon juste avoir une normale ca va pas t'amener tres loin (et visiblement c'est deja plus ou moins bon pour ce qui est de la normale grin)

la ca va surtout dependre de comment tu modelises ton tank.
une suggestion, assez simple, mais qui devrait fonctionner pas trop mal:

tu peux le modeliser avec les proprietes suivantes:
- centre de gravite
- vitesse lineaire
- vitesse angulaire (un seul angle en 2D)
- une liste de points "solides" de contact. si tu modelise une brique, il te faut 4 points pour chaque cote, pour une forme plus complexe tu peux en mettre plus, mais bon c'est pas oblige...

bon. alors une fois que t'as ca, a chaque update de ton tank, tu parcours la liste de ses points de contact (techniquement c'est pas vraiment des points de contacts, c'est plutot des "sommets" d'une enveloppe convexe qui englobe ton tank, mais on s'en fout c'est plus simple dit comme ca).
pour chaque point de contact, tu regarde si il est dans ton terrain ou pas.
une fois que t'as la liste des points qui penetrent le terrain, il faut que tu trouves un moyen d'estimer la profondeur de penetration.
typiquement, un truc qui marche bien pour des heightmaps comme ca, c'est juste de regarder la hauteur qu'a le terrain a la coordonnee x de la ou se trouve le point.

une fois que t'as la profondeur de penetration, tu peux calculer une force a appliquer a ce point (plus de details plus bas), qui va affecter la vitesse angulaire.
tu veux a priori aussi replacer le tank en dehors du terrain. pour faire ca, c'est juste prendre le point qui a la plus grande profondeur de penetration, et remonter ton tank de sorte que ce point soit sur le terrain. la modification de la vitesse angulaire par les forces appliquees par les autres points se chargera de faire pivoter le tank comme il faut.

apres, le reste de l'update du tank, ca reste simple, c'est juste, avec un integrateur d'euler trivial:

position += dt * velocity;
angle += dt * angularVelocity;

et "angle" sert a calculer une matrice (ou peu importe quoi d'autre) qui te sert a afficher le sprite rotate de ton tank, et a transformer les points de contact avant de faire les checks de collision et l'accumulation des forces.

juste quelques details en plus, sur le calcul de la vitesse angulaire.

lorsque t'as une penetration d'un point de contact, il faut changer la vitesse angulaire pour que le tank se reoriente comme il faut. pour faire ca, idealement, il faudrait un tenseur d'inertie specifique a un modele de tank. et pour chaque point de contact tu ferais:

tank.angularSpeed += inverseInertiaTensor * cross(contactPoint - tank.centerOfMass, impulse);

(ou bien juste, avec une acceleration angulaire):
tank.torque += cross(contactPoint - tank.centerOfMass, force);

avec impulse etant le vecteur du point de contact jusqu'a l'endroit ou il devrait etre pour qu'il n'y aie pas de penetration (en gros, dans le cas d'un terrain simple ou tu simplifie les choses en reprojetant a la verticale comme suggere au dessus, c'est juste le vecteur qui a pour coordonnees {0, profondeurDePenetration}.

en gros, le tenseur d'inertie, c'est une matrice qui represente la distribution de masse dans ton tank le long de chacun de ses deux axes principaux, pour pouvoir determiner l'inertie de l'objet le long d'un axe de rotation donne, ou pour pouvoir determiner de combien une force appliquee en un point de l'objet affecte son acceleration angulaire (d'ailleurs c'est souvent plus pratique d'avoir un tenseur d'inertie normalise (divise par la masse), et de re-multiplier par la masse de l'objet (ou la masse inverse dans la ligne de code au dessus), ca te permet de changer facilement la masse par tank, et ca simplifie quelques calculs par la suite...).

mais utiliser un tenseur d'inertie n'a pas vraiment beaucoup d'interet en 2D, vu que tu ne peux rotater que le long d'un seul axe. bref, c'etait juste a titre informatif.

la en gros tu prends ton impulse (cf plus haut pour comment le calculer)
et tu fais juste:

tank.angularSpeed += cross(contactPoint - tank.centerOfMass, impulse) * coeff;

avec cross = a.x * b.y - a.y * b.x (c'est juste la composante z du produit vectoriel 3D entre les deux vecteurs a et b)
et coeff, un coeff.. (sisi grin), plutot petit, a tweaker pour que ca se comporte bien (ca correspond a une simplification du tenseur d'inertie)

bon, j'ai pas relu, mais c'est probablement imbitable, donc v faire un schema...