Mathématiques vectorielles

Introduction

Ce tutoriel est une introduction courte et pratique à l’algèbre linéaire telle qu’elle s’applique au développement de jeux. L’algèbre linéaire est l’étude des vecteurs et de leurs utilisations. Les vecteurs ont de nombreuses applications dans le développement 2D et 3D et Godot les utilise largement. Développer une bonne compréhension des mathématiques vectorielles est essentiel pour devenir un bon développeur de jeux.

Note

Ce tutoriel n’est** pas** un manuel formel sur l’algèbre linéaire. Nous n’examinerons que la façon dont il est appliqué au développement de jeux. Pour un regard plus large sur les mathématiques, voir https://www.khanacademy.org/math/linear-algebra

Systèmes de coordonnées (2D)

Dans l’espace 2D, les coordonnées sont définies par un axe horizontal (x) et un axe vertical (y). Une position particulière dans l’espace 2D s’écrit comme une paire de valeurs telles que (4, 3).

../../_images/vector_axis1.png

Note

Si vous êtes nouveau dans l’infographie, il peut sembler étrange que les points positifs de l’axe y soient orientés vers le bas plutôt que vers le haut, comme vous l’avez probablement appris en cours de mathématiques. Cependant, c’est commun dans la plupart des applications d’infographie.

Toute position dans le plan 2D peut être identifiée par une paire de chiffres de cette façon. Cependant, on peut aussi considérer la position (4, 3) comme un décalage du point (0, 0), ou origine. Dessinez une flèche pointant de l’origine vers le point :

../../_images/vector_xy1.png

C’est un vecteur. Un vecteur représente beaucoup d’informations utiles. En plus de nous dire que le point est à (0, 0), nous pouvons aussi le considérer comme un angle θ et une longueur (ou une magnitude) m. Dans ce cas, la flèche est un vecteur de position - elle indique une position dans l’espace, par rapport à l’origine.

Un point très important à considérer au sujet des vecteurs est qu’ils ne représentent que la direction et l’amplitude relative. Il n’y a pas de notion de position d’un vecteur. Les deux vecteurs suivants sont identiques :

../../_images/vector_xy2.png

Les deux vecteurs représentent un point 4 unités à droite et 3 unités en dessous d’un point de départ. Peu importe où dans le plan vous dessinez le vecteur, il représente toujours une direction et une magnitude relatives.

Opérations vectorielles

Vous pouvez utiliser l’une ou l’autre méthode (coordonnées x et y ou angle et magnitude) pour faire référence à un vecteur, mais pour des raisons pratiques, les programmeurs utilisent généralement la notation des coordonnées. Par exemple, dans Godot, l’origine est le coin supérieur gauche de l’écran, donc pour placer un nœud 2D nommé Node2D 400 pixels à droite et 300 pixels en bas, utilisez le code suivant :

$Node2D.position = Vector2(400, 300)
var node2D = (Node2D) GetNode("Node2D");
node2D.Position = new Vector2(400, 300);

Godot supporte à la fois Vector2 et Vector3 pour une utilisation 2D et 3D, respectivement. Les mêmes règles mathématiques discutées dans cet article s’appliquent aux deux types.

Accès aux membres

Les composants individuels du vecteur sont directement accessibles par leur nom.

# create a vector with coordinates (2, 5)
var a = Vector2(2, 5)
# create a vector and assign x and y manually
var b = Vector2()
b.x = 3
b.y = 1
// create a vector with coordinates (2, 5)
var a = new Vector2(2, 5);
// create a vector and assign x and y manually
var b = new Vector2();
b.x = 3;
b.y = 1;

Additionner des vecteurs

Lors de l’addition ou de la soustraction de deux vecteurs, les composants correspondants sont ajoutés :

var c = a + b  # (2, 5) + (3, 1) = (5, 6)
var c = a + b;  // (2, 5) + (3, 1) = (5, 6)

Nous pouvons aussi le voir visuellement en ajoutant le deuxième vecteur à la fin du premier :

../../_images/vector_add1.png

Notez que l’ajout de a + b donne le même résultat que b + a.

Multiplication scalaire

Note

Les vecteurs représentent à la fois la direction et la magnitude. Une valeur ne représentant que la magnitude est appelée scalaire.

Un vecteur peut être multiplié par un scalaire :

var c = a * 2  # (2, 5) * 2 = (4, 10)
var d = b / 3  # (3, 6) / 3 = (1, 2)
var c = a * 2;  // (2, 5) * 2 = (4, 10)
var d = b / 3;  // (3, 6) / 3 = (1, 2)
../../_images/vector_mult1.png

Note

Multiplier un vecteur par un scalaire ne change pas sa direction, seulement sa magnitude. C’est ainsi que vous mettez à l’échelle un vecteur.

Applications pratiques

Examinons deux utilisations courantes de l’addition et de la soustraction de vecteurs.

Mouvement

Un vecteur peut représenter n’importe quelle quantité avec une magnitude et une direction. Les exemples typiques sont : la position, la vitesse, l’accélération et la force. Dans cette image, le vaisseau à l’étape 1 a un vecteur de position de (1,3)``et un vecteur de vitesse de ``(2,1). Le vecteur vitesse représente la distance parcourue par le vaisseau à chaque pas. Nous pouvons trouver la position de l’étape 2 en ajoutant la vitesse à la position actuelle.

../../_images/vector_movement1.png

Astuce

La vitesse mesure le changement de position par unité de temps. La nouvelle position est trouvée en ajoutant la vitesse à la position précédente.

Pointer vers une cible

Dans ce scénario, vous avez un tank qui souhaite diriger sa tourelle vers un robot. En soustrayant la position du tank de la position du robot, on obtient le vecteur pointant du tank vers le robot.

../../_images/vector_subtract2.png

Astuce

Pour trouver un vecteur pointant de A vers B utilisez B - A.

Vecteurs unitaires

Un vecteur avec une magnitude de 1 est appelé un vecteur unitaire. Ils sont aussi parfois appelés vecteurs de direction ou normales. Les vecteurs unitaires sont utiles lorsque vous avez besoin de suivre une direction.

Normalisation

Normaliser un vecteur, c’est réduire sa longueur à 1 tout en conservant sa direction. Pour ce faire, on divise chacune de ses composantes par son ampleur :

var a = Vector2(2, 4)
var m = sqrt(a.x*a.x + a.y*a.y)  # get magnitude "m" using the Pythagorean theorem
a.x /= m
a.y /= m
var a = new Vector2(2, 4);
var m = Mathf.Sqrt(a.x*a.x + a.y*a.y);  // get magnitude "m" using the Pythagorean theorem
a.x /= m;
a.y /= m;

Comme il s’agit d’une opération très courante, Vector2 et Vector3 fournissent une méthode de normalisation :

a = a.normalized()
a = a.Normalized();

Avertissement

Parce que la normalisation implique de diviser par la longueur du vecteur, vous ne pouvez pas normaliser un vecteur de longueur 0. Toute tentative en ce sens entraînera une erreur.

Réflexion

Une utilisation courante des vecteurs unitaires est d’indiquer les normales. Les vecteurs normales sont des vecteurs unitaires alignés perpendiculairement à une surface, définissant sa direction. Ils sont couramment utilisés pour l’éclairage, les collisions et d’autres opérations impliquant des surfaces.

Par exemple, imaginez que nous avons une balle en mouvement que nous voulons faire rebondir sur un mur ou un autre objet :

../../_images/vector_reflect1.png

La normale de surface a une valeur de (0, -1) parce que c’est une surface horizontale. Lorsque la balle entre en collision, nous prenons son mouvement restant (la quantité restante lorsqu’elle touche la surface) et nous la réfléchissons en utilisant la normale. Dans Godot, la classe Vector2 <class_Vector2> a une méthode ``bounce()` pour gérer cela. Voici un exemple GDScript du diagramme ci-dessus utilisant un KinematicBody2D :

# object "collision" contains information about the collision
var collision = move_and_collide(velocity * delta)
if collision:
    var reflect = collision.remainder.bounce(collision.normal)
    velocity = velocity.bounce(collision.normal)
    move_and_collide(reflect)
// KinematicCollision2D contains information about the collision
KinematicCollision2D collision = MoveAndCollide(_velocity * delta);
if (collision != null)
{
    var reflect = collision.Remainder.Bounce(collision.Normal);
    _velocity = _velocity.Bounce(collision.Normal);
    MoveAndCollide(reflect);
}

Produit scalaire

Le produit produit scalaire est l’un des concepts les plus importants en mathématiques vectorielles, mais il est souvent mal compris. Le produit scalaire est une opération sur deux vecteurs qui retourne un scalaire. Contrairement à un vecteur, qui contient à la fois la magnitude et la direction, une valeur scalaire n’a que la magnitude.

La formule du produit scalaire prend deux formes courantes :

A \cdot B = \left \| A \right \|\left \| B \right \|\cos \Theta

et

A \cdot B = A_{x}B_{x} + A_{y}B_{y}

Cependant, dans la plupart des cas, il est plus facile d’utiliser la méthode intégrée. Notez que l’ordre des deux vecteurs n’a pas d’importance :

var c = a.dot(b)
var d = b.dot(a)  # these are equivalent
float c = a.Dot(b);
float d = b.Dot(a);  // these are equivalent

Le produit scalaire est le plus utile lorsqu’il est utilisé avec des vecteurs unitaires, ce qui fait que la première formule est réduite à seulement cosθ. Cela signifie que nous pouvons utiliser le produit scalaire pour nous dire quelque chose sur l’angle entre deux vecteurs :

../../_images/vector_dot3.png

Lorsque vous utilisez des vecteurs unitaires, le résultat sera toujours compris entre -1 (180°) et 1 (0°).

Orienté vers

Nous pouvons utiliser ce fait pour détecter si un objet est orienté vers un autre objet. Dans le diagramme ci-dessous, le joueur P essaie d’éviter les zombies A et B. En supposant que le champ de vision d’un zombie est de 180°, peuvent-ils voir le joueur ?

../../_images/vector_facing2.png

Les flèches vertes fA et fB sont des vecteurs unitaires représentant la direction dans laquelle les zombies sont orientés et le demi-cercle bleu représente leur champ de vision. Pour le zombie A, nous trouvons le vecteur de direction AP pointant vers le lecteur en utilisant P - A et le normaliser. Si l’angle entre ce vecteur et le vecteur faisant face est inférieur à 90°, alors le zombie peut voir le joueur.

En code, cela ressemblerait à ceci :

var AP = (P - A).normalized()
if AP.dot(fA) > 0:
    print("A sees P!")
var AP = (P - A).Normalized();
if (AP.Dot(fA) > 0)
{
    GD.Print("A sees P!");
}

Produit Vectoriel

Comme le produit scalaire, le produit vectoriel est une opération sur deux vecteurs. Cependant, le résultat du produit vectoriel est un vecteur dont la direction est perpendiculaire aux deux. Son magnitude dépend de leur angle relatif. Si deux vecteurs sont parallèles, le résultat de leur produit vectoriel sera un vecteur nul.

\left \|a \times b  \right \| = \left \| a \right \|\left \| b \right \|\ |\sin(a,b)|

../../_images/tutovec16.png

Le produit vectoriel est calculé de cette façon :

var c = Vector3()
c.x = (a.y * b.z) - (a.z * b.y)
c.y = (a.z * b.x) - (a.x * b.z)
c.z = (a.x * b.y) - (a.y * b.x)
var c = new Vector3();
c.x = (a.y * b.z) - (a.z * b.y);
c.y = (a.z * b.x) - (a.x * b.z);
c.z = (a.x * b.y) - (a.y * b.x);

Avec Godot, vous pouvez utiliser la méthode intégrée :

var c = a.cross(b)
var c = a.Cross(b);

Note

Dans le produit vectoriel, l’ordre compte. a.cross(b)``ne donne pas le même résultat que ``b.cross(a). Les vecteurs résultants pointent dans des directions contraires.

Calcul des normales

Une utilisation courante des produits vectoriels est de trouver la normale de surface d’un plan ou d’une surface dans l’espace 3D. Si nous avons le triangle ABC, nous pouvons utiliser la soustraction de vecteur pour trouver deux bords AB et AC. En utilisant le produit vectoriel, AB x AC produit un vecteur perpendiculaire aux deux : la normale de surface.

Voici une fonction pour calculer la normale d’un triangle :

func get_triangle_normal(a, b, c):
    # find the surface normal given 3 vertices
    var side1 = b - a
    var side2 = c - a
    var normal = side1.cross(side2)
    return normal
Vector3 GetTriangleNormal(Vector3 a, Vector3 b, Vector3 c)
{
    // find the surface normal given 3 vertices
    var side1 = b - a;
    var side2 = c - a;
    var normal = side1.Cross(side2);
    return normal;
}

Pointer vers une cible

Dans la section sur le produit scalaire ci-dessus, nous avons vu comment il pouvait être utilisé pour trouver l’angle entre deux vecteurs. Cependant, en 3D, cette information n’est pas suffisante. Il faut aussi savoir autour de quel axe tourner. On peut le trouver en calculant le produit vectoriel de la direction actuelle et de la direction cible. Le vecteur perpendiculaire résultant est l’axe de rotation.

Plus d’information

Pour plus d’informations sur l’utilisation des mathématiques vectorielles dans Godot, voir les articles suivants :