Yop,
Je me retrouvais dans un cas un peu casse-tête dans le développement d'exo-lands, et comme j'ai fait mon calcul, je me suis dit "autant éviter aux autres de le faire". Donc je vous poste le resultat du calcul ici.
Donc je vous explique le principe général. J'ai un casse-brique, avec donc une balle qui se déplace dans deux direction (vertical et horizontal). Si je le gère uniquement avec des angles de 90°, pas de problèmes. Mais moi, j'aimerais pour varier un peu le jeu que les angles changent.
Donc, pour faire changer les angles, la solution la plus simple est de ne pas se déplacer à la même vitesse sur les repères X et Y : ainsi, si pour 1 pas à droite j'en fait 2 en haut, je me déplacerais pas au même angle que si pour 1 à droite j'en fait 1 en haut.
Mais arrive alors un autre problème : dans ce cas, la vitesse de la balle fluctue en fonction de l'angle avec lequel la balle se déplace. C'est logique, si sur une boucle je fais 3 pas en haut et 1 à droite, je me déplace plus que si je fais 1 en haut et 1 à droite.
Donc la solution pour éviter ça (du moins celle que j'ai trouvée, si vous en avez une meilleure je suis preneur) est de tracer une "ligne imaginaire" entre la position actuelle de la balle (qui est connue) et la position où la balle se trouverais si on la déplaçait selon l'angle prévu.
Je mathématise.
Je vais nommer Xt et Yt la position de la balle à l'instant t, et Xt1 et Yt1 la position de la balle à l'instant t+1, si on se déplace selon l'angle prévu.
Soit Xp et Yp les pas respectifs en X et en Y de la balle. Ces pas sont des données connues.
Actuellement, on a donc :
Xt1 = Xt + Xp
Yt1 = Yt + Yp
Je rappelle que SQRT signifie la racine carrée, et que la distance entre nos deux points est calculable par :
SQRT((Xt1 - Xt)² + (Yt1 - Yt)²)
Bien évidement, une fois remplacées on s'aperçoit de ce que j'indiquais tout à l'heure :
SQRT((Xt1 - Xt)² + (Yt1 - Yt)²) = SQRT((Xt + Xp - Xt)² + (Yt + Yp)²)
= SQRT(Xp² + Yp²)
La distance est donc fortement liée aux pas, ce qui entraine les variations de vitesse dont je parlais.
Un petit dessin pour expliquer la suite :
Maintenant, si on trace une ligne imaginaire entre ces deux points dont nous connaissons les coordonnés (en gras sur le schéma ci-dessus).
On veux donc un point sur cette ligne (pour conserver l'angle) mais dont la distance serait fixe et égale à une valeur que je nommerais V et qui est la vitesse de la balle. Se point se trouve aux coordonnés (X?, Y?), et est n'importe où sur la droite.
Notre but est donc de calculer X? et Y? afin de pouvoir positionner la balle au cycle suivant.
Nommons nos points T, T1 et T2 comme sur la figure.
Notre repère est orthonormal, donc on a T1aT2 et T1bT2 perpendiculaires, donc (aT2) et (bT2) parallèles. Par conscéquent, on a (j'utilise la notation des segment pour leur longeur):
[aT2] / [bT] = [T1a] / [T1b] = [T1T2] / [T1T]
Maintenant, on peut remplacer là-dedans pour obtenir nos coordonnés. Commençons par rappeller que, du fait de la position de nos points a et b, et en posant a (Xa, Ya) et b (Xb, Yb), on a :
Xa = Xb = Xt1
Ya = Y?
Yb = Yt
On a donc :
[aT2] = SQRT((X? - Xt1)² + (Y? - Y?)²)
= SQRT((X? - Xt1)²)
= |X? - Xt1|
[bT] = SQRT((Xt - Xt1)² + (Yt - Yt)²)
= SQRT((Xt - Xt1)²)
= |Xt - Xt1|
[T1a] = SQRT((Xt1 - Xt1)² + (Y? - Yt1)²)
= |Y? - Yt1|
[T1b] = SQRT((Xt1 - Xt1)² + (Yt - Yt1)²)
= |Yt - Yt1|
[T1T] = SQRT(Xp² + Yp²) (cf. démonstration plus haut)
Notez que j'utilise une valeure absolue, puisque pour tout X réel, SQRT(X²) = |X|.
Maintenant, pour le calcul des deux dernières mesures, on va utiliser une autre méthode. En effet, je sais que [TT2] = V par définition de V. Ici, il va falloir envisager deux cas :
- Si [TT1] < V, autrement dit si ma balle va plus lentement qu'elle ne devrait, j'ai [T1T2] = V - [TT1]
- Si [TT1] >= V, autrement dit si ma balle va plus vite qu'elle ne devrait, j'ai [T1T2] = [TT1] - V
Pour éviter de calculer dans les deux cas, je vais considérer que dans tous les cas, [TT1] >= V, autrement dit que SQRT(Xp² + Yp²) >= V. Dans les faits, cela veux dire que Xp >= V et Yp >= V puisque :
Xp >= V et Yp >= V
=> Xp² >= V et Yp² >= V (puisque la fonction carré est strictement croissante sur R)
=> Xp² + Yp² >= V
=> SQRT(Xp² + Yp²) >= V (puisque la fonction racine carrée est strictement croissante sur R*+, et que notre vitesse n'est pas négative (impossible, ce n'est même pas reculer puisque ce serait un changement d'angle) et que bien sur, la somme de deux carrés est strictement positive).
Donc voila, tant que Xp >= V et Yp >= V, on a [T1T2] = [TT1] - V = SQRT(Xp² + Yp²) - V.
Attention, je ne peux pas utiliser d'équivalences ici puisque j'ai une incertitude sur les signes.
On va donc commencer les vrais calculs :
[T1a] / [T1b] = [T1T2] / [T1T]
=>
|Y? - Yt1| / |Yt - Yt1| = SQRT(Xp² + Yp²) - V / SQRT(Xp² + Yp²)
=>
SQRT(Xp² + Yp²) * |Yt - Yt1| = (SQRT(Xp² + Yp²) - V) * |Y? - Yt1|
=>
|Y? - Yt1| = SQRT(Xp² + Yp²) * |Yt - Yt1| / (SQRT(Xp² + Yp²) - V)
Maintenant, la question est de savoir si Y? est superieur à Yt1. La réponse se trouve assez simplement puisque nous savons que la direction est la même sur les différents points pour le déplacement de la balle. Donc on peut dire que :
- Si Yt > Yt1, alors Y? > Yt1, d'où :
|Y? - Yt1| = SQRT(Xp² + Yp²) * |Yt - Yt1| / (SQRT(Xp² + Yp²) - V)
=>
Y? - Yt1 = SQRT(Xp² + Yp²) * |Yt - Yt1| / (SQRT(Xp² + Yp²) - V)
=>
Y? = ((SQRT(Xp² + Yp²) * |Yt - Yt1|) / (SQRT(Xp² + Yp²) - V)) + Yt1
- Si Yt < Yt1 (cas du dessin), alors Y? < Yt1, d'où :
|Y? - Yt1| = SQRT(Xp² + Yp²) * |Yt - Yt1| / (SQRT(Xp² + Yp²) - V)
=>
-(Y? - Yt1) = SQRT(Xp² + Yp²) * |Yt - Yt1| / (SQRT(Xp² + Yp²) - V)
=>
Y? + Yt1 = SQRT(Xp² + Yp²) * |Yt - Yt1| / (SQRT(Xp² + Yp²) - V)
=>
Y? = ((SQRT(Xp² + Yp²) * |Yt - Yt1|) / (SQRT(Xp² + Yp²) - V)) - Yt1
Notons au passage que :
Yt < Yt1
<=>
Yt < Yt + Yp
<=>
Yp > 0
Et vice versa.
De manière strictement analogue, on calcul que :
[aT2] / [bT] = [T1T2] / [T1T]
=>
|X? - Xt1| / |Xt - Xt1| = SQRT(Xp² + Yp²) - V / SQRT(Xp² + Yp²)
=>
- Si Xp > 0 :
X? = ((SQRT(Xp² + Yp²) * |Xt - Xt1|) / (SQRT(Xp² + Yp²) - V)) - Xt1
- Si Xp < 0 :
X? = ((SQRT(Xp² + Yp²) * |Xt - Xt1|) / (SQRT(Xp² + Yp²) - V)) + Xt1
Voila, ce qui nous permet au final de résumer :
- Si Xp > 0 :
X? = ((SQRT(Xp² + Yp²) * |Xt - Xt1|) / (SQRT(Xp² + Yp²) - V)) - Xt1
- Si Xp < 0 :
X? = ((SQRT(Xp² + Yp²) * |Xt - Xt1|) / (SQRT(Xp² + Yp²) - V)) + Xt1
- Si Yp > 0 :
Y? = ((SQRT(Xp² + Yp²) * |Yt - Yt1|) / (SQRT(Xp² + Yp²) - V)) - Yt1
- Si Yp < 0 :
Y? = ((SQRT(Xp² + Yp²) * |Yt - Yt1|) / (SQRT(Xp² + Yp²) - V)) + Yt1
Voila, démonstration terminée. Je crois que je n'ai pas fait d'erreur, il suffit de convertir ça dans le langage de votre choix et normalement ça marche. Indiquez-moi si vous avez une méthode plus simple pour le calcul, vu que là il n'est pas très simple quand même, je suis preneur ^^
Bonne soirée,
Ekilio
Edit : en reprenant ma démonstration pour l'adapter en code, j'ai simplifié un peu la formule au final. Je n'inscrit pas les simplifications en toutes lettres, mais en gros, ça donne les formules suivantes :
Posons Vd = sqrt(Xp² + Yp²)
- Si Xp > 0 :
X? = Xt + ((V * abs(Xp)) / Vd)
- Si Xp < 0 :
X? = Xt - ((V * abs(Xp)) / Vd)
- Si Yp > 0 :
Y? = Yt + ((V * abs(Yp)) / Vd)
- Si Yp < 0 :
Y? = Yt - ((V * abs(Yp)) / Vd)
Ce qui permet de dire d'une manière plus générale :
Soit P l'ancien pas pour une coordonnée et P' le pas recherché, donc respectivement le déplacement pour l'angle et le déplacement à la vitesse voulue, on a :
P' = (V * abs(P)) / Vd
Voila, et désolé si il y a des fautes dans la démonstration générale ^^" J'ai testé la dernière version en tous cas, celle ci-dessus, et elle marche.
Je me retrouvais dans un cas un peu casse-tête dans le développement d'exo-lands, et comme j'ai fait mon calcul, je me suis dit "autant éviter aux autres de le faire". Donc je vous poste le resultat du calcul ici.
Donc je vous explique le principe général. J'ai un casse-brique, avec donc une balle qui se déplace dans deux direction (vertical et horizontal). Si je le gère uniquement avec des angles de 90°, pas de problèmes. Mais moi, j'aimerais pour varier un peu le jeu que les angles changent.
Donc, pour faire changer les angles, la solution la plus simple est de ne pas se déplacer à la même vitesse sur les repères X et Y : ainsi, si pour 1 pas à droite j'en fait 2 en haut, je me déplacerais pas au même angle que si pour 1 à droite j'en fait 1 en haut.
Mais arrive alors un autre problème : dans ce cas, la vitesse de la balle fluctue en fonction de l'angle avec lequel la balle se déplace. C'est logique, si sur une boucle je fais 3 pas en haut et 1 à droite, je me déplace plus que si je fais 1 en haut et 1 à droite.
Donc la solution pour éviter ça (du moins celle que j'ai trouvée, si vous en avez une meilleure je suis preneur) est de tracer une "ligne imaginaire" entre la position actuelle de la balle (qui est connue) et la position où la balle se trouverais si on la déplaçait selon l'angle prévu.
Je mathématise.
Je vais nommer Xt et Yt la position de la balle à l'instant t, et Xt1 et Yt1 la position de la balle à l'instant t+1, si on se déplace selon l'angle prévu.
Soit Xp et Yp les pas respectifs en X et en Y de la balle. Ces pas sont des données connues.
Actuellement, on a donc :
Xt1 = Xt + Xp
Yt1 = Yt + Yp
Je rappelle que SQRT signifie la racine carrée, et que la distance entre nos deux points est calculable par :
SQRT((Xt1 - Xt)² + (Yt1 - Yt)²)
Bien évidement, une fois remplacées on s'aperçoit de ce que j'indiquais tout à l'heure :
SQRT((Xt1 - Xt)² + (Yt1 - Yt)²) = SQRT((Xt + Xp - Xt)² + (Yt + Yp)²)
= SQRT(Xp² + Yp²)
La distance est donc fortement liée aux pas, ce qui entraine les variations de vitesse dont je parlais.
Un petit dessin pour expliquer la suite :
Maintenant, si on trace une ligne imaginaire entre ces deux points dont nous connaissons les coordonnés (en gras sur le schéma ci-dessus).
On veux donc un point sur cette ligne (pour conserver l'angle) mais dont la distance serait fixe et égale à une valeur que je nommerais V et qui est la vitesse de la balle. Se point se trouve aux coordonnés (X?, Y?), et est n'importe où sur la droite.
Notre but est donc de calculer X? et Y? afin de pouvoir positionner la balle au cycle suivant.
Nommons nos points T, T1 et T2 comme sur la figure.
Notre repère est orthonormal, donc on a T1aT2 et T1bT2 perpendiculaires, donc (aT2) et (bT2) parallèles. Par conscéquent, on a (j'utilise la notation des segment pour leur longeur):
[aT2] / [bT] = [T1a] / [T1b] = [T1T2] / [T1T]
Maintenant, on peut remplacer là-dedans pour obtenir nos coordonnés. Commençons par rappeller que, du fait de la position de nos points a et b, et en posant a (Xa, Ya) et b (Xb, Yb), on a :
Xa = Xb = Xt1
Ya = Y?
Yb = Yt
On a donc :
[aT2] = SQRT((X? - Xt1)² + (Y? - Y?)²)
= SQRT((X? - Xt1)²)
= |X? - Xt1|
[bT] = SQRT((Xt - Xt1)² + (Yt - Yt)²)
= SQRT((Xt - Xt1)²)
= |Xt - Xt1|
[T1a] = SQRT((Xt1 - Xt1)² + (Y? - Yt1)²)
= |Y? - Yt1|
[T1b] = SQRT((Xt1 - Xt1)² + (Yt - Yt1)²)
= |Yt - Yt1|
[T1T] = SQRT(Xp² + Yp²) (cf. démonstration plus haut)
Notez que j'utilise une valeure absolue, puisque pour tout X réel, SQRT(X²) = |X|.
Maintenant, pour le calcul des deux dernières mesures, on va utiliser une autre méthode. En effet, je sais que [TT2] = V par définition de V. Ici, il va falloir envisager deux cas :
- Si [TT1] < V, autrement dit si ma balle va plus lentement qu'elle ne devrait, j'ai [T1T2] = V - [TT1]
- Si [TT1] >= V, autrement dit si ma balle va plus vite qu'elle ne devrait, j'ai [T1T2] = [TT1] - V
Pour éviter de calculer dans les deux cas, je vais considérer que dans tous les cas, [TT1] >= V, autrement dit que SQRT(Xp² + Yp²) >= V. Dans les faits, cela veux dire que Xp >= V et Yp >= V puisque :
Xp >= V et Yp >= V
=> Xp² >= V et Yp² >= V (puisque la fonction carré est strictement croissante sur R)
=> Xp² + Yp² >= V
=> SQRT(Xp² + Yp²) >= V (puisque la fonction racine carrée est strictement croissante sur R*+, et que notre vitesse n'est pas négative (impossible, ce n'est même pas reculer puisque ce serait un changement d'angle) et que bien sur, la somme de deux carrés est strictement positive).
Donc voila, tant que Xp >= V et Yp >= V, on a [T1T2] = [TT1] - V = SQRT(Xp² + Yp²) - V.
Attention, je ne peux pas utiliser d'équivalences ici puisque j'ai une incertitude sur les signes.
On va donc commencer les vrais calculs :
[T1a] / [T1b] = [T1T2] / [T1T]
=>
|Y? - Yt1| / |Yt - Yt1| = SQRT(Xp² + Yp²) - V / SQRT(Xp² + Yp²)
=>
SQRT(Xp² + Yp²) * |Yt - Yt1| = (SQRT(Xp² + Yp²) - V) * |Y? - Yt1|
=>
|Y? - Yt1| = SQRT(Xp² + Yp²) * |Yt - Yt1| / (SQRT(Xp² + Yp²) - V)
Maintenant, la question est de savoir si Y? est superieur à Yt1. La réponse se trouve assez simplement puisque nous savons que la direction est la même sur les différents points pour le déplacement de la balle. Donc on peut dire que :
- Si Yt > Yt1, alors Y? > Yt1, d'où :
|Y? - Yt1| = SQRT(Xp² + Yp²) * |Yt - Yt1| / (SQRT(Xp² + Yp²) - V)
=>
Y? - Yt1 = SQRT(Xp² + Yp²) * |Yt - Yt1| / (SQRT(Xp² + Yp²) - V)
=>
Y? = ((SQRT(Xp² + Yp²) * |Yt - Yt1|) / (SQRT(Xp² + Yp²) - V)) + Yt1
- Si Yt < Yt1 (cas du dessin), alors Y? < Yt1, d'où :
|Y? - Yt1| = SQRT(Xp² + Yp²) * |Yt - Yt1| / (SQRT(Xp² + Yp²) - V)
=>
-(Y? - Yt1) = SQRT(Xp² + Yp²) * |Yt - Yt1| / (SQRT(Xp² + Yp²) - V)
=>
Y? + Yt1 = SQRT(Xp² + Yp²) * |Yt - Yt1| / (SQRT(Xp² + Yp²) - V)
=>
Y? = ((SQRT(Xp² + Yp²) * |Yt - Yt1|) / (SQRT(Xp² + Yp²) - V)) - Yt1
Notons au passage que :
Yt < Yt1
<=>
Yt < Yt + Yp
<=>
Yp > 0
Et vice versa.
De manière strictement analogue, on calcul que :
[aT2] / [bT] = [T1T2] / [T1T]
=>
|X? - Xt1| / |Xt - Xt1| = SQRT(Xp² + Yp²) - V / SQRT(Xp² + Yp²)
=>
- Si Xp > 0 :
X? = ((SQRT(Xp² + Yp²) * |Xt - Xt1|) / (SQRT(Xp² + Yp²) - V)) - Xt1
- Si Xp < 0 :
X? = ((SQRT(Xp² + Yp²) * |Xt - Xt1|) / (SQRT(Xp² + Yp²) - V)) + Xt1
Voila, ce qui nous permet au final de résumer :
- Si Xp > 0 :
X? = ((SQRT(Xp² + Yp²) * |Xt - Xt1|) / (SQRT(Xp² + Yp²) - V)) - Xt1
- Si Xp < 0 :
X? = ((SQRT(Xp² + Yp²) * |Xt - Xt1|) / (SQRT(Xp² + Yp²) - V)) + Xt1
- Si Yp > 0 :
Y? = ((SQRT(Xp² + Yp²) * |Yt - Yt1|) / (SQRT(Xp² + Yp²) - V)) - Yt1
- Si Yp < 0 :
Y? = ((SQRT(Xp² + Yp²) * |Yt - Yt1|) / (SQRT(Xp² + Yp²) - V)) + Yt1
Voila, démonstration terminée. Je crois que je n'ai pas fait d'erreur, il suffit de convertir ça dans le langage de votre choix et normalement ça marche. Indiquez-moi si vous avez une méthode plus simple pour le calcul, vu que là il n'est pas très simple quand même, je suis preneur ^^
Bonne soirée,
Ekilio
Edit : en reprenant ma démonstration pour l'adapter en code, j'ai simplifié un peu la formule au final. Je n'inscrit pas les simplifications en toutes lettres, mais en gros, ça donne les formules suivantes :
Posons Vd = sqrt(Xp² + Yp²)
- Si Xp > 0 :
X? = Xt + ((V * abs(Xp)) / Vd)
- Si Xp < 0 :
X? = Xt - ((V * abs(Xp)) / Vd)
- Si Yp > 0 :
Y? = Yt + ((V * abs(Yp)) / Vd)
- Si Yp < 0 :
Y? = Yt - ((V * abs(Yp)) / Vd)
Ce qui permet de dire d'une manière plus générale :
Soit P l'ancien pas pour une coordonnée et P' le pas recherché, donc respectivement le déplacement pour l'angle et le déplacement à la vitesse voulue, on a :
P' = (V * abs(P)) / Vd
Voila, et désolé si il y a des fautes dans la démonstration générale ^^" J'ai testé la dernière version en tous cas, celle ci-dessus, et elle marche.