Angles et distances avec love2d

Créons un cercle qui suit le curseur de la souris. Commençons par créer un cercle :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function love.load()
  --Create an object called circle
  circle = {}

  --Give it the properties x, y, radius and speed
  circle.x = 100
  circle.y = 100
  circle.radius = 25
  circle.speed = 200
end

function love.draw()
  --Draw the circle
  love.graphics.circle("line", circle.x, circle.y, circle.radius)
end

Pour déplacer le cercle jusqu’à la souris, nous devons connaître l’angle. Nous pouvons l’obtenir avec la fonction math.atan2. Le premier argument est la position y que nous souhaitons atteindre moins la position y de notre objet. Le second argument est le même mais pour x. C’est l’une des rares occasions où y vient avant x.

Fondamentalement, ce que fait atan2, c’est qu’il prend un vecteur vertical et horizontal (distance + direction) et avec cette information, il retourne un angle :

arc tangente
arc tangente

Pour obtenir la vitesse, nous avons besoin de soustraire la position du cercle de la position de la cible :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
function love.update(dt)
  --love.mouse.getPosition returns the x and y position of the cursor.
  mouse_x, mouse_y = love.mouse.getPosition()

  angle = math.atan2(mouse_y - circle.y, mouse_x - circle.x)
end

function love.draw()
  love.graphics.circle("line", circle.x, circle.y, circle.radius)

  --Print the angle
  love.graphics.print("angle: " .. angle, 10, 10)

  --Here are some lines to visualize the velocities
  love.graphics.line(circle.x, circle.y, mouse_x, circle.y)
  love.graphics.line(circle.x, circle.y, circle.x, mouse_y)

  --The angle
  love.graphics.line(circle.x, circle.y, mouse_x, mouse_y)
end
Animation d’un angle
Animation d’un angle

Si atan2 vous déroute, ne vous inquiétez pas. Tout ce que vous devez savoir est que math.atan2(cible_y – objet_y, cible_x – objet_x) vous donne un angle. Dans notre cas, l’objet est le cercle et la cible est le curseur de la souris.

Vous êtes sur le point d’y arriver. Ne vous laissez pas effrayer. Ce n’est pas difficile et si vous ne comprenez pas tout, ce n’est pas grave pour un débutant.

Quand vous lancez le jeu, vous remarquerez que l’angle ne dépassera pas 3,14 (Pi, π). C’est parce que atan2 ne retourne pas un angle en degrés mais plutôt en radians.

Cette image animée (gif) explique les radians :

Explication radian
Explication radian

Si vous êtes encore perdu, je vous recommande de lire cet article sur Wikipedia.

Quelques points clés :

  • math.atan2 retourne un angle en radians
  • l’angle retourné est compris entre -3,14 et 3,14
  • 360 degrés vaut 2×π radians. Donc 90 degrés équivaut à $\dfrac{π}{2}$ radians
  • Le nombre π, écrit aussi Pi, est le rapport de la circonférence d’un cercle à son diamètre, ce qui signifie que si nous devions prendre le diamètre d’un cercle et le diviser par la circonférence du cercle (périmètre), nous obtiendrions π
Explication pi
Explication pi

En Lua, nous pouvons obtenir π en utilisant math.pi.

Si vous ne comprenez pas tout correctement maintenant, ne vous découragez pas, surtout si c’est la première fois.

Sinus et cosinus

Maintenant, nous avons besoin de faire avancer notre cercle vers la souris. Pour cela, nous allons utiliser math.cos et math.sin.

Ces deux fonctions retournent un nombre entre -1 et 1, basé sur l’angle que nous passons en paramètre.

Voici un gif pour vous aider à visualiser le sinus et le cosinus :

Animation sinus et cosinus
Animation sinus et cosinus

Et une image pour montrer exactement ce qu’il se passe sur l’image animée :

Explications sinus et cosinus
Explications sinus et cosinus

Sinus et cosinus sont des nombres compris entre -1 et 1 qui sont basés sur l’angle.

Si l’angle pointe vers la gauche, alors le cosinus sera -1 et le sinus sera 0 :

Explications sinus et cosinus première partie
Explications sinus et cosinus première partie

Si l’angle est vers le bas, alors le cosinus vaut 0 et le sinus 1 :

Explications sinus et cosinus deuxième partie
Explications sinus et cosinus deuxième partie

Alors, comment pouvons-nous utiliser ces valeurs pour faire avancer notre cercle vers la souris ? Eh bien, en multipliant notre vitesse par ces valeurs. Par exemple, si la souris est sur une diagonale, disons en haut à droite, le sinus vaudrait $\approx −0,7$ et le cosinus $\approx 0,7$.

Maintenant, si nous faisons ceci :

1
2
circle.x = circle.x + circle.speed * dt
circle.y = circle.y + circle.speed * dt

Notre cercle se déplacerait directement en bas à droite. Mais en multipliant par le sinus et le cosinus comme ceci :

1
2
circle.x = circle.x + circle.speed * cos * dt
circle.y = circle.y + circle.speed * sin * dt

Notre cercle se déplace horizontalement avec $circle.speed×0.7$

Et il se déplace verticalement avec $circle.speed\times −0.7$
Ce qui signifie qu’il devrait se déplacer directement vers notre souris. Essayons cela !

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function love.update(dt)
  --love.mouse.getPosition returns the x and y position of the cursor.
  mouse_x, mouse_y = love.mouse.getPosition()

  angle = math.atan2(mouse_y - circle.y, mouse_x - circle.x)

  cos = math.cos(angle)
  sin = math.sin(angle)

  --Make the circle move towards the mouse
  circle.x = circle.x + circle.speed * cos * dt
  circle.y = circle.y + circle.speed * sin * dt
end

function love.draw()
  love.graphics.circle("line", circle.x, circle.y, circle.radius)

  --The angle
  love.graphics.line(circle.x, circle.y, mouse_x, mouse_y)
  love.graphics.line(circle.x, circle.y, mouse_x, circle.y)
  love.graphics.line(circle.x, circle.y, circle.x, mouse_y)

end
Suivi de la souris
Suivi de la souris

Les distances

Maintenant, disons que nous voulons seulement déplacer notre cercle quand il est loin de notre souris. Pour faire cela, nous avons besoin de calculer la distance entre eux. Nous utilisons donc le théorème de Pythagore.

Avec lui, nous pouvons calculer la ligne la plus longue (hypoténuse) dans un triangle rectangle.

Pythagore
Pythagore

Fondamentalement, ce que vous faites est d’utiliser la longueur des côtés les plus courts en les mettant au carré. Ensuite, vous en faites la somme pour finalement en prendre la racine carrée et obtenir la longueur du côté le plus long que l’on appelle aussi hypoténuse.

Lorsque vous avez 2 points, dans notre cas le cercle et la souris, il existe également un triangle invisible. Vérifions-le :

1
2
3
4
5
6
function love.draw()
  love.graphics.circle("line", circle.x, circle.y, circle.radius)
  love.graphics.line(circle.x, circle.y, mouse_x, mouse_y)
  love.graphics.line(circle.x, circle.y, mouse_x, circle.y)
  love.graphics.line(mouse_x, mouse_y, mouse_x, circle.y)
end
Triangle de Pythagore
Triangle de Pythagore

Si nous utilisons le théorème de Pythagore sur notre triangle, nous pouvons trouver son hypoténuse et ainsi déterminer la distance entre les 2 points.

Créons une nouvelle fonction pour cela. Premièrement, nous avons besoin des côtés horizontaux et verticaux :

1
2
3
4
function getDistance(x1, y1, x2, y2)
  local horizontal_distance = x1 - x2
  local vertical_distance = y1 - y2
end

Ensuite, nous avons besoin de mettre au carré ces nombres. Nous pouvons les multiplier par eux-mêmes ou utiliser ^2.

1
2
3
4
5
6
7
8
function getDistance(x1, y1, x2, y2)
  local horizontal_distance = x1 - x2
  local vertical_distance = y1 - y2

  --Both of these work
  local a = horizontal_distance * horizontal_distance
  local b = vertical_distance ^2
end

Nous avons besoin de faire la somme de ces nombres et d’en prendre la racine carrée. Si vous faites $5\times 5$ ou $5^2$, vous obtenez 25. La racine carrée de 25 est 5. Nous pouvons obtenir la racine carrée avec math.sqrt :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function getDistance(x1, y1, x2, y2)
  local horizontal_distance = x1 - x2
  local vertical_distance = y1 - y2
  --Both of these work
  local a = horizontal_distance * horizontal_distance
  local b = vertical_distance ^2

  local c = a + b
  local distance = math.sqrt(c)
  return distance
end

Pour prouver que cela fonctionne, dessinons un cercle avec un rayon égal à la distance :

1
2
3
4
5
6
7
8
9
function love.draw()
  love.graphics.circle("line", circle.x, circle.y, circle.radius)
  love.graphics.line(circle.x, circle.y, mouse_x, mouse_y)
  love.graphics.line(circle.x, circle.y, mouse_x, circle.y)
  love.graphics.line(mouse_x, mouse_y, mouse_x, circle.y)

  local distance = getDistance(circle.x, circle.y, mouse_x, mouse_y)
  love.graphics.circle("line", circle.x, circle.y, distance)
end
Animation suivi de la souris avec Pythagore
Animation suivi de la souris avec Pythagore

Cela fonctionne !
Maintenant, amusons-nous. Je veux seulement que le cercle bouge quand la distance est supérieure à 200 pixels et que plus il est près, plus il se déplace doucement :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
function love.update(dt)
  mouse_x, mouse_y = love.mouse.getPosition()
  angle = math.atan2(mouse_y - circle.y, mouse_x - circle.x)
  cos = math.cos(angle)
  sin = math.sin(angle)

  local distance = getDistance(circle.x, circle.y, mouse_x, mouse_y)

  if distance < 400 then
    circle.x = circle.x + circle.speed * cos * (distance/100) * dt
    circle.y = circle.y + circle.speed * sin * (distance/100) * dt
  end
end
Animation suivi souris en fonction de la distance
Animation suivi souris en fonction de la distance

Images

Utilisons une image et changeons le look de la souris :

Flèche à utiliser pour l’exemple
Flèche à utiliser pour l’exemple

L’argument optionnel pour la rotation est par défaut à 0.

Avec un angle à 0, le cosinus vaut 1 et le sinus 0, ce qui veut dire que l’objet se déplacerait vers la droite.

Donc quand vous utilisez une image, vous devriez la voir orientée vers la droite par défaut :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function love.load()
  arrow = {}
  arrow.x = 200
  arrow.y = 200
  arrow.speed = 300
  arrow.angle = 0
  arrow.image = love.graphics.newImage("arrow_right.png")
end

function love.update(dt)
  mouse_x, mouse_y = love.mouse.getPosition()
  arrow.angle = math.atan2(mouse_y - arrow.y, mouse_x - arrow.x)
  cos = math.cos(arrow.angle)
  sin = math.sin(arrow.angle)

  arrow.x = arrow.x + arrow.speed * cos * dt
  arrow.y = arrow.y + arrow.speed * sin * dt
end

function love.draw()
  love.graphics.draw(arrow.image, arrow.x, arrow.y, arrow.angle)
  love.graphics.circle("fill", mouse_x, mouse_y, 5)
end

Quand vous lancez le jeu, vous devriez noter que la flèche est un peu décalée :

Problème avec la flèche
Problème avec la flèche

C’est parce que l’image tourne autour de son coin supérieur gauche au lieu de son centre. Pour corriger cela, nous avons besoin de placer l’origine au centre de l’image :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
function love.load()
  arrow = {}
  arrow.x = 200
  arrow.y = 200
  arrow.speed = 300
  arrow.angle = 0
  arrow.image = love.graphics.newImage("arrow_right.png")
  arrow.origin_x = arrow.image:getWidth() / 2
  arrow.origin_y = arrow.image:getHeight() / 2
end

function love.draw()
  love.graphics.draw(arrow.image,
    arrow.x, arrow.y, arrow.angle, 1, 1,
    arrow.origin_x, arrow.origin_y)
  love.graphics.circle("fill", mouse_x, mouse_y, 5)
end

Et maintenant, elle pointe correctement vers notre souris.

Animation de la flèche qui suit la souris
Animation de la flèche qui suit la souris

Résumé

Nous pouvons faire bouger un objet avec un angle en obtenant le cosinus et le sinus de cet angle. Ensuite, nous le déplaçons horizontalement avec sa coordonnée x et une vitesse multipliée par le cosinus. De même pour y, mais avec le sinus. Nous pouvons calculer la distance entre 2 points en utilisant le théorème de Pythagore. Quand vous utilisez une image, celle-ci devrait être orientée vers la droite par défaut. N’oubliez pas de placer son origine au centre pour qu’elle pivote correctement.