Techniques d'intégration multimédia
Selon l'illustration ci-dessus, si on recherche la distance entre les centres du cercle et du carré, ce que l'on recherche correspond au côté c d'un triangle rectangle imaginaire. En appliquant le théorème de Pythagore, on obtient ce code:
float a = objet1.transform.position.x - objet2.transform.position.x;
float b = objet1.transform.position.y - objet2.transform.position.y;
float c = Mathf.Sqrt(a*a + b*b);
Si on désire calculer la distance entre un objet et la position du curseur de la souris, il suffit de substituer les coordonnées horizontale et verticale du clip 2 pour la position x et y de la souris (convertie d'abord en World Units):
Vector3 souris = Input.mousePosition;
Vector3 sourisWorld = Camera.main.ScreenToWorldPoint(souris);
float a = objet.transform.position.x - sourisWorld.x;
float b = objet.transform.position.y - sourisWorld.y;
float c = Mathf.Sqrt(a*a + b*b);
Pour calculer la distance entre les centres du cercle et du carré de l'illustration, on peut avoir recours à un simple calcul de vecteurs. En effet, en soustrayant les vecteurs de position des 2 objets, on obtient un nouveau vecteur dont la magnitude correspond à la distance entre les 2 objets. Cette approche est plus directe dans un logiciel comme Unity qui fonctionne avec des vecteurs pour décrire les positions des objets.
Vector3 v1 = objet1.transform.position;
Vector3 v2 = objet2.transform.position;
Vector3 vDifference = v1-v2;
float c = vDifference.magnitude;
Dans Unity, les classes Vector2 et Vector3 offrent même une méthode qui permet de calculer cette distance encore plus simplement.
Vector3 v1 = objet1.transform.position;
Vector3 v2 = objet2.transform.position;
float c = Vector3.Distance(v1,v2);
Supposons que nous voulons déplacer un objet à une vitesse donnée selon un angle précis. Ce qu'il faut déterminer, c'est de combien de pixels nous devrons avancer cet objet à l'horizontale (sur l'axe des x) et à la verticale (sur l'axe des y) de façon à maintenir la direction désirée (l'angle) et la vitesse souhaitée.
Nous connaissons la vitesse (puisque nous lui donnons la valeur de notre choix) et l'angle dans lequel l'objet doit se diriger (encore une valeur de notre choix, donc connue).
En nous basant sur le théorème de Pythagore et sur le cercle trigonométrique, nous pouvons établir que:
cos(angle)2 + sin(angle)2 = 1
Cette formule est obtenue à partir du théorème de Pythagore (a2 + b2 = c2), en considérant que l'Hypothénuse (le rayon de notre cercle trigonométrique) est de 1.
a2+ b2 = (1)2
a2 + b2 = 1 , ça ressemble à cos(angle)2 + sin(angle)2 = 1, n'est-ce pas?
Cela signifie donc que pour une hypothénuse de 1, la valeur correspondante à x est le cosinus de l'angle et la valeur correspondant à y est le sinus de l'angle. Ce qui nous amène à conclure que pour une hypothénuse de la longeur de notre choix, v par exemple,
x = v*cos(angle)
et
y = v*sin(angle)
Notez que l'angle ici doit être fourni en radians.
Si on souhaite déplacer un objet en mouvement continu, on obtient donc les équations de mouvement suivantes:
pX = pX + v * cos(angle)
pY = pY + v * sin(angle)
...où v est la vitesse de l'objet, pV sa position verticale et pH sa position horizontale. L'angle est en radians. Ce qui donne ceci en C#/Unity:
float dx = objet.transform.position.x + _vitesse * Mathf.Cos(_angleRad);
float dy = objet.transform.position.y + _vitesse * Mathf.Sin(_angleRad);
objet.transform.position = new Vector2(dx,dy);
Ou, si on veut déplacer un objet dans un Update() il est possible de calculer le vecteur de déplacement ainsi:
float angle = 37f * Mathf.Deg2Rad; // angle de 37 degrés, pour cet exemple, transformé en radians...
Vector2 dV= new Vector2(Mathf.Cos(angle),Mathf.Sin(angle));
transform.Translate(dV*_vitesse*Time.deltaTime);
Important!Important! En 2D dans Unity, l'angle zéro correspond à la direction de l'axe des x positif (3h sur le cadran d'une montre). Les valeurs d'angles augmentent ensuite dans le sens contraire des aiguilles d'une montre.
Ici l'approche du problème est moins évidente. Nous voulons assurer la rotation d'un clip sur lui même de façon à ce qu'il se tourne toujours vers la souris.
On pourrait aussi trouver mathématiquement l'angle théta par la loi du sinus, mais on obtient alors une équation beaucoup plus complexe à résoudre (je vous épargne ces calculs). Il suffit de savoir que le code qui découle de cette résolution d'équations ressemble à ceci:
Vector3 souris = Input.mousePosition;
Vector3 sourisWorld = Camera.main.ScreenToWorldPoint(souris);
float a = sourisWorld.x - transform.position.x;
float b = sourisWorld.y - transform.position.y;
float angleRad = Mathf.Atan2(b,a); // l'angle calculé est en radians
Remarque: On utilise Atan2() au lieu de Atan() pour déterminer la pente (l'angle) car avec Atan on obtiendrait des erreurs (valeurs infinies) lorsque b=0 (car l'ordinateur tenterait alors une division par 0 en faisant le calcul). L'utilisation de Atan2() prévient ces cas limites et donne toujours un angle valide comme réponse.
Voici donc un script qui applique ces principes:
//-----------------------------------------
// Retourne l'angle en radians formé
// par la pente entre 2 positions x,y d'objets
// Note: Retourne un angle en radians
private float TrouverAngle(Vector2 v1, Vector2 v2)
{
float a = v2.x - v1.x;
float b = v2.y - v1.y;
return Mathf.Atan2(b,a);
}
/// REMARQUE: En utilisant une soustraction de vecteurs,
/// on peut simplifier cette fonction. En voici donc une variante:
private float TrouverAngle(Vector2 v1, Vector2 v2)
{
Vector2 v = v2 - v1;
return Mathf.Atan2(v.y,v.x);
}
Ensuite, pour qu'un gameObject puisse tourner selon la position de la souris (se tourner vers elle en fait) en utilisant la méthode TrouverAngle(), on pourra utiliser un code comme celui-ci:
void Update()
{
Vector3 souris = Input.mousePosition;
Vector3 sourisWorld = Camera.main.ScreenToWorldPoint(souris);
// angle en radians
float angle = TrouverAngle(transform.position, new Vector2(sourisWorld.x, sourisWorld.y));
//// ROTATION AVEC UN ANGLE EULER ////
transform.eulerAngles = new Vector3(0f,0f,angle * Mathf.Rad2Deg); // angle transformé en degrés
}
Variante: la rotation peut aussi être appliquée avec un Quaternion:
void Update()
{
Vector3 souris = Input.mousePosition;
Vector3 sourisWorld = Camera.main.ScreenToWorldPoint(souris);
// angle en radians
float angle = TrouverAngle(transform.position, new Vector2(sourisWorld.x, sourisWorld.y));
//// ROTATION AVEC UN QUATERNION ////
transform.rotation = Quaternion.AngleAxis(angle* Mathf.Rad2Deg, Vector3.forward);
/// OU ENCORE AVEC CETTE VARIANTE ///
transform.rotation = Quaternion.Euler( new Vector3(0f,0f,angle* Mathf.Rad2Deg));
}
Avertissement: Unity propose plusieurs approches qui peuvent sembler intéressantes pour faire pointer un gameObject vers un autre en 3d (par exemple transfom.LookAt(), Vector3.RotateTowards(), Quaternion.LookRotation()...). L'ennui, c'est que la plupart de ces fonctions marchent très bien en 3d mais sont souvent inutilement compliquées à appliquer en 2d. Aussi, beaucoup de programmeurs vont préférer l'approche mathématique (avec Atan2) qui au final est plus facile à mettre en oeuvre en 2d.