
Outil indispensable dans la création de jeux vidéos s'il en est, le vecteur sera l'objet de ce chapitre un brin mathématique. :)
Les vecteurs seront la base de toute la partie gestion physique de notre moteur, aussi convient-il qu'ils soient robustes et faciles à utiliser.
La classe est composée de 2 attributs de type float qui représentent les composantes en X et en Y du vecteur.
On pourrait penser qu'il est plus logique qu'ils soient de type int puisque nous allons travailler sur des surfaces 2D donc des espaces discrets, mais pourtant il apparaît qu'on perd énormement de précision en utilisant uniquement des nombres entiers.
Jetez un oeil au code du générateur de particules par exemple pour vous en convaincre; sans nombres décimaux, c'est une horreur. ^^
Commençons par les méthodes les plus évidentes :
Le constructeur par défaut prend en paramètre un float pour l'attribut en X (m_x) et un autre pour l'attribut en Y (m_y).
Le constructeur par recopie prend quand à lui un paramètre de type CVect. Il s'agit donc d'une copie de l'instance passée en paramètre.
La norme est la longueur d'un vecteur. La formule qui permet de le calculer est très simple : racine(x*x + y*y)
La méthode se contentera donc de renvoyer le résultat.
Dans beaucoup de calculs, il est utile de connaître la normale du vecteur, c'est à dire ce que serait ce vecteur si sa norme était égale à 1 mais tout en conservant les mêmes direction et sens.
Le calcul pour obtenir ce résultat est là encore assez simple, il suffit de diviser le vecteur par sa propre norme :
CVect buffVect = *this; buffVect /= buffVect.getNorme(); return buffVect;
On commence par copier le vecteur à normaliser à la ligne 1, puis on le divise par sa propre norme à la ligne 2, et pour finir on le renvoie.
L'opérateur /= ne permet pas de faire ça nativement, il va falloir le surcharger comme nous allons le voir pas plus tard que dans pas longtemps. :)
Pour des raisons pratiques, les attributs de cette classe sont publiques (Non ! Posez ce clavier !).
Passer par un accesseur à chaque fois serait un véritable calvaire.
Ceci dit les mutateurs ont leurs utilité puisqu'ils permettent de changer les 2 attributs d'un coup au lieu de 2.
Les surcharges d'opérateurs vont grandement nous faciliter l'écriture du code par la suite, et ils sont nombreux. Pour la plupart, c'est simple :
Il suffit de multiplier, diviser, soustraire ou additionner les coordonnées des deux vecteurs et de retourner le vecteur qui contient le résultat.
C'est très facile à partir du moment où vous maîtrisez la surcharge des opérateurs.
CVect& operator+(const CVect& other)
{
CVect result(m_x + other.m_x, m_y + other.m_y);
return result;
}
Pour l'affectation, il suffit de copier les coordonnées du vecteur de droite dans celles du vecteur de gauche.
Dans les calcul+affectation, utilisez une combinaison d'opérateurs de calcul et d'affectation :
CVect& operator+=(const CVect& other)
{
return (*this) = (*this) + other;
}
Pour l'égalité, c'est facile... Il suffit de tester si les coordonnées sont égales ou pas.
Pour l'inégalité, c'est encore plus simple : on teste si les deux CVect ne sont pas égaux :
bool operator!=(const CVect& other)
{
return !(*this == other);
}
Pour ce qui est des opérateurs <, >, <= et >=, comparez tout simplement la norme des vecteurs entre eux : un vecteur est supérieur à un autre s'il est plus long.
Vous n'êtes pas sans savoir qu'il existe 2 grande systèmes de coordonnées dans un plan en 2 dimensions : les coordonnées cartésiennes et les coordonnées polaires (sinon jetez un oeil ici).
Jusqu'à maintenant nous n'avons travaillé qu'en coordonnées cartésiennes, même les attributs de la classe contiennent les coordonnées cartésiennes du vecteur, mais cela va changer.
Nous n'allons bien sur pas modifier les attributs ni en ajouter d'autres, mais plutôt coder des méthodes permettant de travailler avec les coordonnées polaires du vecteur à partir de ses coordonnées cartésiennes et vice versa.
Pour pouvoir stocker ces coordonnées polaires, nous allons faire une structure :
struct SPolarCoord
{
float angle, radius;
};
Et voila, on ne peut plus simple. Une variable contenant le rayon, et l'autre l'angle.
La première méthode à coder est un constructeur par recopie pouvant prendre une instance de cette structure.
Son rôle sera de créer une instance de CVect évidement, et pour ce faire il convertira ces coordonnées polaires en coordonnées cartésiennes qu'il stockera dans les attributs de l'instance.
CVect::CVect(const SPolarCoord &c)
{
m_x = static_cast<float>(c.radius * cos(c.angle*M_PI/180));
m_y = static_cast<float>(c.radius * -sin(c.angle*M_PI/180));
}
Si vous avez du mal sur les formules, nous vous conseillons de jetez un oeil attentif à ce tuto de Kayl sur la trigonométrie. ;)
Les 2 autres méthodes concernant les coordonnées polaires permettent de modifier les attributs du vecteur en passant des coordonnées polaires, et de récupérer les coordonnées polaires du vecteur à partir de ses attributs actuels.
La première est simplissime, c'est exactement le même code que pour le constructeur.
La seconde est déjà un poil plus complexe : il s'agit non plus de modifier les attributs à partir de coordonnées polaires, mais de créer les coordonnées polaires à partir des attributs.
Finalement, il s'agit d'appliquer les mêmes formules mais à l'inverse :
//On cree une variable de type SPolarCoord. SPolarCoord bcp; //On calcule le rayon. Qui correspond a la norme du vecteur bcp.radius = static_cast<float>(sqrt(m_x*m_x + m_y*m_y)); //On calcule l'angle bcp.angle= static_cast<float>((asin(m_x/bcp.radius)*180)/M_PI); return bcp;
Les commentaires parlent d'eux mêmes. :)
Les vecteurs sont omniprésents en programmation de jeux vidéos, assurez vous d'avoir bien compris leurs fonctionnement pour pouvoir aborder la suite l'esprit serein.
Si vous avez besoin de plus d'explications sur tel ou tel point, nous vous recommandons de lire ce très bon tuto d'Yno : http://www.siteduzero.com/tuto-3-18391-1-les-vecteurs.html