• 01 Mai 2024, 22:58:52


Auteur Sujet: [TUTO] Bien faire ses tests de distance/position  (Lu 4253 fois)

0 Membres et 1 Invité sur ce sujet

Hors ligne S!m

  • *
  • Messages: 2341
    • Voir le profil
[TUTO] Bien faire ses tests de distance/position
« le: 26 Juillet 2011, 01:03:37 »
Bien faire ses tests de distance

Table des matières

1. Présentation

2. Mathématiques

   2.1 Calcul de distance
   2.2 Tests de position

3. Fonctions en PAWN

4. Conclusion


1. Présentation

Salut et bienvenue à vous sur ce tutoriel.

Des votre arrivée à la programmation sur SA:MP, vous avez surement eu des idées nécessitant des tests de distance ou de position.
Ce tutoriel est très simple, il présente des fonctions permettant d'effectuer ces tests, explique leur fonctionnement, leur provenance (pour les simplifications mathématiques) et leur utilisation.

J'espère donc que vous apprendrez beaucoup grâce à cette lecture. ;D

2. Développement mathématique des formules de base

La section suivante comporte les développements mathématiques des formules utilisées pour les tests de position et de distance. Je cherche ici à faire comprendre d'où proviennent les codes en question, mais vous pouvez néanmoins sauter et passer directement aux codes si vous ne désirez pas comprendre.

2.1 Calcul de distance
Le calcul de distance consiste simplement à l'évaluation de la distance entre deux points.
Ce calcul est simplement l'évaluation de la norme d'un vecteur en trois dimension.
Il s'agit du vecteur reliant les deux points en question.
Pour ceux qui ne sont pas familier avec les vecteurs, on peut voir ce calcul comme une généralisation du théorème de Pythagore.

Mathématique

Ceux qui connaissent bien le côté mathématique de ce type de calcul, vous pouvez passer à la section implantation.
Dans les formules que j'utiliserai prochainement pour montrer le principe de ce type de calcul, j'utiliserai la nomenclature suivante:

  • X1 = position sur l'axe X du point 1
  • Y1 = position sur l'axe Y du point 1
  • Z1 = position sur l'axe Z du point 1
  • X2 = position sur l'axe X du point 2
  • Y2 = position sur l'axe Y du point 2
  • Z2 = position sur l'axe Z du point 2
  • dX = différence des positions des points sur l'axe X
  • dY = différence des positions des points sur l'axe Y
  • dZ = différence des positions des points sur l'axe Z
Donc, quand on parle du théorème de pythagore, on pense tout de suite à la formule suivante:

A² + B² = C²


En posant un point sur chaque coin du triangle, on obtient la figure suivante:

IMAGES À REDIMENSIONNER

Ainsi:
  • A est la distance entre le point 1 et le 3
  • B est la distance entre le point 2 et le 3
  • C est la distance entre le point 1 et le 2
En utilisant le système de coordonnée illustré et les conventions décrites ci-haut, nous obtenons les informations suivantes:

A = Y1 - Y3
B = X2 - X3

Nous pouvons désormais substituer ces données dans la formule de Pythagore:

(Y1 - Y3)² + (X2 - X3)² = C²

Le résultat recherché est donc la distance entre le point 1 et le point 2, ce qui correspond donc à C.
La formule précédente nous donne la valeur de C, mais comporte deux inconvénients majeurs:

  • Nous devons passer par un troisième point hypothétique pour trouver la réponse
  • Nous n'obtenons pas la valeur de C mais son carré

Pour résoudre ces problèmes, il va falloir réfléchir un peu...
que peut-on faire... Substituer? trouver des équivalence? un peu d'algèbre?...

Commençons par régler le problème du carré.
Au fond, il nous faut simplement trouver l'inverse du carré afin d'isoler notre C.
Quel exposant annule l'exposant 2? 0.5 bien sur! Il s'agit de la racine carrée.

Concernant le problème du troisième point, il faut chercher un peu plus.
Il faut principalement utiliser un dessin bien fait pour nous mettre la puce à l'oreille.
Comme nos axes concordent avec les cathètes de notre triangle, les coordonnées Y2 et Y3 sont en fait identiques, de même que X3 et X1.

On peut donc substituer dans la formule:

(Y1 - Y2)² + (X2 - X1)² = C²

Et en ajoutant la racine carrée:

((Y1 - Y2)² + (X2 - X1)²)½ = C = Distance

Nous avons donc finalement atteint notre formule finale, celle qui donne directement la distance, sans passer par un point fictif ni besoin de calcul supplémentaire.

Petit ennui par contre: GTA:SA est en 3D, pas en 2D!!!

Alors là, je vous demande de me croire sur parole (on peut refaire le même principe avec un triangle supplémentaire, le A de ce nouveau triangle serait le C de celui que nous avons vu et le B l'axe des Z), nous avons la formule suivante:

((Z1 - Z2)² + (Y1 - Y2)² + (X2 - X1)²)½ = C = Distance

2.2 Tests de position

Dans la section précédente, j'ai donné le développement de la formule pour évaluer la distance entre deux points.
Toutefois, il faut remarquer que dans une majorité de cas, on veut savoir si la distance entre les points est inférieur à une autre distance (souvent constante) ou si un point se trouve dans une zone.
C'est le genre de tâche qui est effectué par la fonction PlayerToPoint (qui est obsolète, désormais IsPlayerInRangeOfPoint) ou encore IsPlayerInZone (qui test une zone carrée).
Heureusement, quand on ne veut que savoir si la distance est inférieur à une autre distance, il existe une optimisation au niveau mathématique, ce qui nous permettra d'améliorer l'efficacité de notre fonction.
Par contre, pour savoir si un joueur est dans un rectangle (zone), il n'existe pas vraiment d'optimisation possible, il ne s'agit que de bien faire sa fonction.

Tests de distance

Nous avons vu précédemment la formule suivante:

((Z1 - Z2)² + (Y1 - Y2)² + (X2 - X1)²)½ = C = Distance

Nous avions ajouté la racine carrée afin d'avoir la valeur exacte de la distance. Dans ce cas ci, nous comparons deux distances. La formules est donc transformée à quelque chose de ce genre:

((Z1 - Z2)² + (Y1 - Y2)² + (X2 - X1)²)½ <= Distance max

Nous savons tous, n'es-ce pas, qu'il est relativement difficile d'implanter la racine carrée, la fonction est relativement complexe et demande beaucoup de calcul. Nous voudrions donc éviter d'utiliser cette fonction démoniaque  >:(.
Quelle est la fonction inverse de la racine carrée?
Mais l'exposant 2 bien sur!
Attention: il faut appliquer l'opération des deux côtés de l'inégalité, nous conservons ainsi notre test. Si une valeur était plus petite avant d'être mise au carrée, elle le sera toujours après.
En l'appliquant de chaque côté, et sachant que les exposant ainsi placés se multiplient, nous avons:

(((Z1 - Z2)² + (Y1 - Y2)² + (X2 - X1)²)½)² <= (Distance max)²
(Z1 - Z2)² + (Y1 - Y2)² + (X2 - X1)² <= (Distance max)²

Nous avons donc grandement réduit notre temps de calcul. La fonction permettant d'obtenir l'exponentielle d'une valeur est avantageuse lorsque l'exposant est relativement grand, dans le cas de 2, il vaut mieux simplement utiliser : x² = x * x.

Tests de zone

Les tests de zone sont très simples, il ne s'agit que de vérifier si la position d'un point est entre certaines bornes.

En gros:

minimum X <= X     ET     maximum X >= X

Le tout se fait sur chacun des axes testés (2 ou 3 selon une zone de forme rectangulaire ou un prisme à base rectangulaire).

3. Fonctions de tests (PAWN)

Commençons avec une fonction qui permet de connaître la distance entre deux points:

DistanceBetweenPoints(Float:X1, Float:Y1, Float:Z1, Float:X2, Float:Y2, Float:Z2)
{
new Float:dX, Float:dY, Float:dZ;

dX = X1 - X2;
dY = Y1 - Y2;
dZ = Z1 - Z2;

return floatsqroot(dX * dX + dY * dY + dZ * dZ);
}

Explication:
J'utilise des variables intermédiaires pour les distances sur chaque axe pour éviter de faire ces calculs 2 fois, on gagne ainsi en efficacité. Pour le reste, la multiplication des distances par elles-mêmes permet de les mettre au carrée de façon plus efficace que floatpower.
Cette fonction retourne la valeur CORRECTE de la distance entre les deux points passés en paramètres.

On peut utiliser cette fonction dans d'autres fonctions pour la distance entre différents éléments, voici quelques exemples:

Float:DistanceBetweenPlayerAndPoint(playerid, Float:X, Float:Y, Float:Z)
{
new Float:pX, Float:pY, Float:pZ, Float:distance = 999999999.9999;//je préférerais utiliser NAN au lieu de 9999999999.9999

if(IsPlayerConnected(playerid))
{
GetPlayerPos(playerid, pX, pY, pZ);

distance = DistanceBetweenPoints(pX, pY, pZ, X, Y, Z);//récupération de la distance
}

return distance;
}

Float:DistanceBetweenPlayerAndVehicle(playerid, vehicleid)
{
new Float:pX, Float:pY, Float:pZ;
new Float:vX, Float:vY, Float:vZ;
new Float:distance = 999999999.9999;//je préférerais utiliser NAN au lieu de 9999999999.9999

if(IsPlayerConnected(playerid) && GetVehicleModel(vehicleid) >= 200)//permet de savoir si le véhicule est valide
{
GetPlayerPos(playerid, pX, pY, pZ);
GetVehiclePos(vehicleid, vX, vY, vZ);

distance = DistanceBetweenPoints(pX, pY, pZ, vX, vY, vZ);//récupération de la distance
}

return distance;
}

Maintenant, on passe à la seconde explication: Le tests de grandeur de distance.

PointToPoint(Float:X1, Float:Y1, Float:Z1, Float:X2, Float:Y2, Float:Z2, Float:distanceMax)
{
new Float:dX, Float:dY, Float:dZ;

dX = X1 - X2;
dY = Y1 - Y2;
dZ = Z1 - Z2;

return (dX * dX + dY * dY + dZ * dZ <= distanceMax * distanceMax);
}

Explication:
Tout comme dans la fonction précédente, nous calculons en premier lieu la différence de position sur chacun des axes. Par la suite, nous additionnons les carrées de chaque différence et comparons la valeur (la distance au carrée entre les points) avec la valeur de la distance maximale autorisée mise au carré. De la même façon, nous pouvons nous en servir pour créer des fonctions subalternes.

Finalement, nous arrivons au test de position de type test de zone.

//on peut retirer le Z des paramètres, mais gardons le pour les apparences (question d'uniformité)
IsPointInZone(Float:X, Float:Y, Float:Z, Float:minX, Float:maxX, Float:minY, Float:maxY)
{
return ((X >= minX && X <= maxX) && (Y >= minY && Y <= maxY));
}

IsPointInCube(Float:X, Float:Y, Float:Z, Float:minX, Float:maxX, Float:minY, Float:maxY, Float:minZ, Float:maxZ)
{
return ((X >= minX && X <= maxX) && (Y >= minY && Y <= maxY) && (Z >= minZ && Z <= maxZ));
}

Explication:

Il y a très peu à expliquer ici, j'ai fait le tout sur une ligne, sans condition pour réduire les calculs inutiles.

4. Conclusion

....À FAIRE

Merci de passer vos commentaires, si vous ne trouver pas que mon explication est claire, avez des idées pour améliorer, formulations différentes peut-être... voir passer par un chemin complètement différent.

Concernant le niveau de difficulté, je ne sais pas quoi mettre, mais comme le côté programmation est très très simple...





Hors ligne Xolokos

  • PAWN/XHTML/CSS/PHP/SQL/JS
  • *
  • Grand Banditisme
  • What are you waiting for ?
  • Messages: 681
    • Voir le profil
Re : [TUTO] Bien faire ses tests de distance/position
« Réponse #1 le: 26 Juillet 2011, 11:06:28 »
J'y pensé la nuit mais plus dans le sens graphique absice ordonnée.
Tu me diras c'est un peut la même chose.
Merci pour les formules.
Nippah !

Hors ligne Ssk

  • *
  • Lulu's Stunt - Le serveur stunt de Lulu !
  • Messages: 8154
    • Voir le profil
Re : [TUTO] Bien faire ses tests de distance/position
« Réponse #2 le: 26 Juillet 2011, 12:40:09 »
Salut, le tuto est bien écris malgré quelques petites fautes d'orthographes qui se sont glissées dans le tuto qui reste avant tout bien compréhensible, tu expliques très bien les différentes formules mathématiques permettant de calculer les distances, tu abordes aussi l'optimisation du code. GG


Xolokos oui c'est exactement ça, les coordonnées X et Y représente les abscisses ainsi que les ordonnées, la dimension Z représente ce que l'on peut qualifier de hauteur (certains préféreront dire profondeur).


Et merci Sim pour ce tuto, rare sont les Français (Francophone) qui à l'image d'Y_Less essayent de faire en sorte que tout le monde puissent comprendre certaines choses dans la programmation.



Derrière tout programme se cache un programmeur, je considère le monde comme un programme.
Mon blog

Hors ligne Gilux

  • *
  • Mafioso
  • GOOBY PLS
  • Messages: 2209
    • Voir le profil
Re : [TUTO] Bien faire ses tests de distance/position
« Réponse #3 le: 26 Juillet 2011, 12:58:16 »
Merci, en plus de m'avoir appris ça, tu viens de m'apprendre à faire des ancres dans un post, ce qui va me permettre de mieux organiser mon tuto x)

++

Hors ligne R@f

  • *
  • GTAOnline Addict
  • Messages: 4655
    • Voir le profil
Re : [TUTO] Bien faire ses tests de distance/position
« Réponse #4 le: 26 Juillet 2011, 14:11:20 »
Ah sympa, un tuto avec un peu de maths :D

Bon boulot Sim.

++
R@f

Hors ligne cristab

  • *
  • Messages: 8379
    • Voir le profil
Re : [TUTO] Bien faire ses tests de distance/position
« Réponse #5 le: 26 Juillet 2011, 14:44:24 »
superbe :o
pas d'aide en PM, vous êtes sur un forum est il me semble que vous êtes la pour avoir de l'aide donc pourquoi MP une seul personne qui ne vous répondra pas alors qu'il y a plein de membre ici

Hors ligne Syg

  • Expert programmeur C/C++/PAWN
  • *
  • The GTAOnline Jesus
  • Messages: 3908
    • Voir le profil
Re : [TUTO] Bien faire ses tests de distance/position
« Réponse #6 le: 26 Juillet 2011, 16:11:34 »
Pour tes fonctions DistanceBetweenXXX, tu peux utiliser -1.0 comme distance par défaut au lieu de 999999999.9999.
Une distance étant toujours positive, le fait de retourner une valeur négative montre qu'il y a eu une erreur.

Sinon, dans tes schémas, il n'est pas très heureux de donner des chiffres comme nom pour les sommets du triangle.
Il vaut mieux les appeler A, B et C. De cette façon, on sait tout de suite que la distance AB est la distance entre le point A et le point B. Dans ton cas, ce n'est pas très intuitif.
De plus, les coordonnées de A sont (Xa;Ya) ce qui reste très intuitif aussi.

++
Syg
Courtesy of GtaManiac

Hors ligne S!m

  • *
  • Messages: 2341
    • Voir le profil
Re : Re : [TUTO] Bien faire ses tests de distance/position
« Réponse #7 le: 27 Juillet 2011, 00:01:32 »
Salut,

merci à tous pour les commentaires :P

Pour tes fonctions DistanceBetweenXXX, tu peux utiliser -1.0 comme distance par défaut au lieu de 999999999.9999.
Une distance étant toujours positive, le fait de retourner une valeur négative montre qu'il y a eu une erreur.

Ah j'avais en tête une fonction qui retourne la distance de l'élément le plus proche, c'est pour ça que j'ai mis une grande valeur... merci de la remarque

En ce qui concerne les lettres, je vais changer ça en même temps de changer la valeur de la distance, merci^^

++Sim++