Résolution des collisions avec love2d

Au chapitre 13, nous avons expliqué comment détecter une collision. Dans ce chapitre, nous allons plus loin. Imaginez que vous ayez un jeu où vous pouvez vous déplacer et où il y a des murs que vous ne pouvez pas traverser. Pour y parvenir, nous devons non seulement détecter la collision, nous devons également nous assurer que le joueur s’arrête de bouger et ne reste pas coincé à l’intérieur du mur. Nous devons repousser le joueur.

Avant de commencer, je tiens à vous dire que la résolution des collisions est très difficile, et c’est quelque chose que même les professionnels ont du mal à gérer. Regardez les speedruns par exemple. Il y a beaucoup de problèmes où vous pouvez passer à travers les murs et autres. La collision que nous allons faire est assez solide mais loin d’être parfaite. Et ne vous sentez pas mal si vous avez du mal à comprendre ce que nous faisons ici.

Pour ce tutoriel, nous avons besoin de deux choses : un joueur qui peut marcher dans 4 directions et quelques murs. Créons une classe de base pour les deux. Une classe d’entité.

Nous avons donc les fichiers suivants :

  • main.lua
  • payer.lua
  • wall.lua
  • entity.lua
  • classic.lua

Commençons par la classe Entity. Il a un x, y, une largeur et une hauteur. Utilisons une image pour nos murs et notre lecteur, et utilisons la largeur et la hauteur de cette image.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
--! file: entity.lua
Entity = Object:extend()

function Entity:new(x, y, image_path)
  self.x = x
  self.y = y
  self.image = love.graphics.newImage(image_path)
  self.width = self.image:getWidth()
  self.height = self.image:getHeight()
end

function Entity:draw()
  love.graphics.draw(self.image, self.x, self.y)
end

end Et ajoutons la détection de collision. Savez-vous toujours comment le faire et pourquoi cela fonctionne comme ça? Peut-être relisez le chapitre 13.

1
2
3
4
5
6
7
8
9
function Entity:checkCollision(e)
  -- e will be the other entity with which we check if there is collision.
  -- This is the compact version.
  -- This is one action (return), but spread over multiple lines.
  return self.x + self.width > e.x
  and self.x < e.x + e.width
  and self.y + self.height > e.y
  and self.y < e.y + e.height
end

Maintenant que nous avons notre sous-classe, nous pouvons créer notre lecteur et notre mur. Voici les images que nous utiliserons (les bordures colorées nous aident à voir s’il y a collision) :

Image du joueur
Image du joueur
Image du mur
Image du mur
1
2
3
4
5
6
--! file: player.lua
Player = Entity:extend()

function Player:new(x, y)
  Player.super.new(self, x, y, "player.png")
end
1
2
3
4
5
6
--! file: wall.lua
Wall = Entity:extend()

function Wall:new(x, y)
  Wall.super.new(self, x, y, "wall.png")
end

Faisons en sorte que nous puissions déplacer notre joueur en utilisant les touches fléchées.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
--! file: player.lua
Player = Entity:extend()

function Player:new(x, y)
  Player.super.new(self, x, y, "player.png")
end

function Player:update(dt)
  if love.keyboard.isDown("left") then
    self.x = self.x - 200 * dt
  elseif love.keyboard.isDown("right") then
    self.x = self.x + 200 * dt
  end

  if love.keyboard.isDown("up") then
    self.y = self.y - 200 * dt
  elseif love.keyboard.isDown("down") then
    self.y = self.y + 200 * dt
  end
end

Bon maintenant, créons ces objets dans main.lua

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
--! file: main.lua
function love.load()
  -- First require classic, since we use it to create our classes.
  Object = require "classic"
  -- Second require Entity, since it's the baseclass for our other classes.
  require "entity"
  require "player"
  require "wall"

  player = Player(100, 100)
  wall = Wall(200, 100)
end

function love.update(dt)
  player:update(dt)
  wall:update(dt)
end

function love.draw()
  player:draw()
  wall:draw()
end

Maintenant, nous pouvons nous promener, et bien sûr, nous pouvons traverser notre mur. Voici donc une idée, que se passe-t-il si nous gardons une trace de notre position précédente, puis chaque fois que nous heurtons un mur, nous revenons simplement à cette position. De cette façon, le joueur ne peut jamais traverser un mur.

Nous devons apporter quelques modifications à notre classe Entity. Nous ajoutons un dernier objet pour nos positions précédentes, et nous ajoutons une fonction qui vérifie s’il y a collision et si c’est le cas, nous ramène à notre position précédente.

Puisqu’il y a beaucoup de petites modifications au code dans ce chapitre, j’ai ajouté —- AJOUTEZ CECI aux endroits où vous devez ajouter / changer quelque chose au code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
--! file: entity.lua
Entity = Object:extend()

function Entity:new(x, y, image_path)
  self.x = x
  self.y = y
  self.image = love.graphics.newImage(image_path)
  self.width = self.image:getWidth()
  self.height = self.image:getHeight()

---- ADD THIS
  self.last = {}
  self.last.x = self.x
  self.last.y = self.y
-------------
end

-- I have a habit of passing dt regardless of if we need it.
---- ADD THIS
function Entity:update(dt)
  -- Set the current position to be the previous position
  self.last.x = self.x
  self.last.y = self.y
end

function Entity:resolveCollision(e)
  if self:checkCollision(e) then
    -- Reset the position.
    self.x = self.last.x
    self.y = self.last.y
  end
end
-------------

Nous devons appeler la fonction baseclass dans notre classe Player pour que cela fonctionne.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
--! file: player.lua
function Player:update(dt)
  -- It's important that we do this before changing the position
  ---- ADD THIS
  Player.super.update(self, dt)
  -------------

  if love.keyboard.isDown("left") then
    self.x = self.x - 200 * dt
  elseif love.keyboard.isDown("right") then
    self.x = self.x + 200 * dt
  end

  if love.keyboard.isDown("up") then
    self.y = self.y - 200 * dt
  elseif love.keyboard.isDown("down") then
    self.y = self.y + 200 * dt
  end
end

Et nous avons besoin d’appeler la fonction resolveCollision() dans main.lua

1
2
3
4
5
6
--! file: main.lua
function love.update(dt)
  player:update(dt)
  wall:update(dt)
  player:resolveCollision(wall)
end

Essaye le. Vous remarquerez que cela fonctionne … en quelque sorte. Si vous regardez attentivement, vous verrez peut-être qu’il y a parfois un petit écart entre le lecteur et le mur. C’est parce que les joueurs se déplacent si vite qu’ils sautent sur cette partie, touchent le mur et sont renvoyés à leur position précédente.

Résultat du code précédent
Résultat du code précédent

Au lieu de placer le joueur sur sa position précédente, nous devons déplacer le joueur jusqu’à ce qu’il ne touche plus le mur. Donc, fondamentalement, nous le repoussons. Pas de retour à sa position précédente, mais juste assez pour qu’il ne chevauche plus le mur (pour que les bords se touchent).

Pour cela, nous devons savoir deux choses :

  • Jusqu’où faut-il déplacer le joueur pour qu’il ne chevauche plus le mur
  • Dans quelle direction devons-nous éloigner le joueur du mur ?

Pour l’instant, supposons que le joueur vient de la gauche, puis plus tard, nous pourrons le faire fonctionner dans toutes les directions.

Nous devons donc calculer combien le joueur chevauche le mur. Nous pouvons le faire en calculant le côté droit du joueur – le côté gauche du mur. Si le côté droit du joueur est en position x 225 et le côté gauche du mur en position x 205, alors le joueur doit être poussé vers la gauche avec 20 pixels.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
--! file: entity.lua
function Entity:resolveCollision(e)
  if self:checkCollision(e) then
    -- The player's right side is x + width
    -- The wall's left side is x
    -- Calculate the difference and subtract that from the player's position
    local pushback = self.x + self.width - e.x
    self.x = self.x - pushback
  end
end

Maintenant, il n’y a plus d’espace. Et comme nous ne poussons le joueur que horizontalement, il peut désormais se déplacer verticalement tout en touchant le mur.

Faisons-le fonctionner dans toutes les directions. Pour déterminer de quelle direction vient le joueur, nous pouvons utiliser notre position précédente. Parce qu’à moins que nous venions du coin, notre position précédente était déjà alignée verticalement ou horizontalement avec le mur.

Résultat définitif du code précédent
Résultat définitif du code précédent

Si nous venions de la gauche, nous sommes déjà alignés verticalement. Nous entrons déjà en collision avec le mur à un niveau vertical. Rappelez-vous les quatre conditions du chapitre 13? Juste avant de toucher le mur de gauche, deux de ces conditions sont remplies.

Le côté inférieur de A est plus bas que le côté supérieur de B.

Le côté supérieur de A est plus haut que le côté inférieur de B.

Dans ce cas, nous devons nous déplacer horizontalement pour entrer en collision avec le mur, ce qui signifie que nous savons que le joueur est venu du côté gauche ou droit.

Nous vérifions donc si sur notre position précédente si nous étions déjà en collision horizontale ou verticale avec le mur et sur cette base, nous pouvons déterminer si nous devons repousser le joueur verticalement ou horizontalement.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
function Entity:wasVerticallyAligned(e)
  -- It's basically the collisionCheck function, but with the x and width part removed.
  -- It uses last.y because we want to know this from the previous position
  return self.last.y < e.last.y + e.height and self.last.y + self.height > e.last.y
end

function Entity:wasHorizontallyAligned(e)
  -- It's basically the collisionCheck function, but with the y and height part removed.
  -- It uses last.x because we want to know this from the previous position
  return self.last.x < e.last.x + e.width and self.last.x + self.width > e.last.x
end

function Entity:resolveCollision(e)
  if self:checkCollision(e) then
    if self:wasVerticallyAligned(e) then

    elseif self:wasHorizontallyAligned(e) then

    end
  end
end

Maintenant que nous savons cela, nous devons vérifier de quel côté ils se touchent. Un moyen simple consiste à vérifier le centre du mur et du lecteur. Dans le cas, il était déjà aligné verticalement: si le centre du joueur est plus à gauche que le centre du mur, nous le poussons vers la gauche. Essayons-le.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function Entity:resolveCollision(e)
  if self:checkCollision(e) then
    if self:wasVerticallyAligned(e) then
      if self.x + self.width/2 < e.x + self.width/2  then
        -- pusback = the right side of the player - the left side of the wall
        local pushback = self.x + self.width - e.x
        self.x = self.x - pushback
      else
        -- pusback = the right side of the wall - the left side of the player
        local pushback = e.x + e.width - self.x
        self.x = self.x + pushback
      end
    elseif self:wasHorizontallyAligned(e) then
      if self.y + self.height/2 < e.y + self.height/2 then
        -- pusback = the bottom side of the player - the top side of the wall
        local pushback = self.y + self.height - e.y
        self.y = self.y - pushback
      else
        -- pusback = the bottom side of the wall - the top side of the player
        local pushback = e.y + e.height - self.y
        self.y = self.y + pushback
      end
    end
  end
end

Ça marche! Mais d’accord, c’est beaucoup d’informations à la fois. Récapitulons cela.

Image récapitulative afin d’aider à comprendre le code précédent
Image récapitulative afin d’aider à comprendre le code précédent
  1. Le joueur et le mur sont déjà à la même hauteur. Avec ces informations, nous pouvons déterminer que le joueur doit avoir bougé horizontalement pour entrer en collision avec le mur.
  2. Le centre du joueur est plus à gauche que le centre du mur, nous devons donc le pousser vers la gauche.
  3. Le côté droit du lecteur est à 5 pixels à droite du côté gauche du mur. Ou en termes plus simples: le joueur doit être repoussé de 5 pixels pour ne plus se chevaucher avec le mur.
  4. Le joueur est repoussé correctement! Je suis à court de mots .. nous l’avons fait!

Donc ça marche. Mais il y a encore une chose que je veux corriger. Parce qu’en ce moment, nous appelons player: resolverCollision(mur) mais si nous devions l’appeler dans l’autre sens autour du mur: resolverCollision (joueur), ce ne serait pas le cas. Bien sûr, dans ce cas, nous savons que le mur est plus fort que le joueur, mais supposons que nous ne le savons pas.

Pour résoudre ce problème, nous pouvons ajouter une variable appelée force, et la propriété avec une force plus faible est repoussée.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Entity = Object:extend()

function Entity:new(x, y, image_path)
  self.x = x
  self.y = y
  self.image = love.graphics.newImage(image_path)
  self.width = self.image:getWidth()
  self.height = self.image:getHeight()

  self.last = {}
  self.last.x = self.x
  self.last.y = self.y

  -- Add a default value to the variable
  ---- ADD THIS
  self.strength = 0
  -------------
end
1
2
3
4
5
6
7
8
--! file: wall.lua
Wall = Entity:extend()

function Wall:new(x, y)
  Wall.super.new(self, x, y, "wall.png")
  -- Give the wall a strength of 100
  self.strength = 100
end

Maintenant, nous vérifions la valeur dans Entity: resolverCollision (e) et si la valeur de self.strength est supérieure à la valeur de e.strength, alors nous appelons e: resolverCollision (self). Nous inversons donc les rôles.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
--! file: entity.lua
function Entity:resolveCollision(e)
  ---- ADD THIS
  if self.strength > e.strength then
    e:resolveCollision(self)
    -- Return because we don't want to continue this function.
    return
  end
  -------------
  if self:checkCollision(e) then
    if self:wasVerticallyAligned(e) then
      if self.x + self.width/2 < e.x + e.width/2 then
        local pushback = self.x + self.width - e.x
        self.x = self.x - pushback
      else
        local pushback = e.x + e.width - self.x
        self.x = self.x + pushback
      end
    elseif self:wasHorizontallyAligned(e) then
      if self.y + self.height/2 < e.y + e.height/2 then
        local pushback = self.y + self.height - e.y
        self.y = self.y - pushback
      else
        local pushback = e.y + e.height - self.y
        self.y = self.y + pushback
      end
    end
  end
end

Et maintenant, peu importe l’ordre dans lequel nous appelons la fonction.

Poussez une caisse

Le mur repousse le joueur. Mais que faire si nous voulons que le joueur bouge quelque chose? Pour ce faire, nous devons apporter quelques modifications à notre code.

Créons d’abord une classe Box. Nous pouvons utiliser cette image pour cela :

Image de la boite que l’on va pousser
Image de la boite que l’on va pousser
1
2
3
4
5
6
--! file: box.lua
Box = Entity:extend()

function Box:new(x, y)
  Box.super.new(self, x, y, "box.png")
end

Ensuite, nous voulons ajouter la boîte, et pendant que nous y sommes, créons un tableau d’objets.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
--! file: main.lua
function love.load()
  Object = require "classic"
  require "entity"
  require "player"
  require "wall"
  -- Require box!
  require "box"

  player = Player(100, 100)
  wall = Wall(200, 100)
  box = Box(400, 150)

  objects = {}
  table.insert(objects, player)
  table.insert(objects, wall)
  table.insert(objects, box)
end

Nous pouvons maintenant utiliser une boucle ipair pour mettre à jour et dessiner les objets, mais qu’en est-il de la résolution de la collision? Parce que nous voulons que tous les objets vérifient la collision avec tous les autres objets. Comment pouvons-nous y parvenir? Nous pourrions simplement avoir deux boucles for, où nous devons également vérifier s’il ne s’agit pas du même objet. Ainsi :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
-- Example, no need to copy
for i,v in ipairs(objects) do
  for j,w in ipairs(objects) do
    -- Make sure they're not the same
    -- Else it would be resolving collision with itself
    if v ~= w then
      v:resolveCollision(w)
    end
  end
end

Le problème avec ceci est cependant que nous vérifions maintenant la collision deux fois pour chaque objet. Après avoir appelé player: resolverCollision (mur), nous n’avons pas besoin d’appeler wall: resolverCollision (joueur) mais c’est essentiellement ce que nous faisons. Donc, pour éviter cela, nous parcourons la liste des objets dans la première boucle, et dans la deuxième boucle, nous parcourons la liste, en commençant par l’objet à côté de l’objet dans la première boucle.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
function love.update(dt)
  -- Update all the objects
  for i,v in ipairs(objects) do
    v:update(dt)
  end

  -- Go through all the objects (except the last)
  for i=1,#objects-1 do
    -- Go through all the objects starting from the position i + 1
    for j=i+1,#objects do
      objects[i]:resolveCollision(objects[j])
    end
  end
end

function love.draw()
  -- Draw all the objects
  for i,v in ipairs(objects) do
    v:draw()
  end
end
Image représentant la recherche de collisions
Image représentant la recherche de collisions

(J’ai ajouté un mur et une boîte supplémentaires pour le rendre plus clair)

Ainsi, le premier objet, le joueur, résout la collision avec les quatre objets suivants.

Le deuxième objet, le mur, n’a pas besoin de résoudre la collision avec le joueur, car cela a déjà été fait précédemment par le joueur.

Etc.

Quoi qu’il en soit, nous ne pouvons pas encore pousser la boîte. Nous devons rendre le joueur plus fort que la boîte.

1
2
3
4
5
6
7
--! file: player.lua
Player = Entity:extend()

function Player:new(x, y)
  Player.super.new(self, x, y, "player.png")
  self.strength = 10
end

Et maintenant, nous pouvons pousser la boîte. Génial, alors avons-nous terminé? Nan! Essayez de pousser la boîte contre le mur. Cela ne semble pas correct, n’est-ce pas ?

Animation du code précédent
Animation du code précédent

Pourquoi cela arrive-t-il ? Parce que le joueur pousse la boîte contre le mur, mais le mur repousse la boîte au joueur. Ce que vous voulez, c’est que la boîte et le joueur cessent de bouger lorsqu’ils entrent en collision avec le mur. Pour résoudre ce problème, nous devons passer la force du mur sur la boîte, de sorte que lorsque la boîte est suffisamment solide pour repousser le joueur.

Mais cette force ne devrait être que temporaire, sinon vous ne pourriez pas pousser la boîte dès que vous la touchez, car elle obtient la même force que le joueur. Nous créons donc une propriété distincte pour ce qu’on appelle tempStrength.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
--! file: entity.lua
Entity = Object:extend()

function Entity:new(x, y, image_path)
  self.x = x
  self.y = y
  self.image = love.graphics.newImage(image_path)
  self.width = self.image:getWidth()
  self.height = self.image:getHeight()

  self.last = {}
  self.last.x = self.x
  self.last.y = self.y

  -- Add a default value to the variable
  self.strength = 0
  -- Temp is short for temporary
  ---- ADD THIS
  self.tempStrength = 0
  -------------
end

function Entity:update(dt)
  self.last.x = self.x
  self.last.y = self.y
  -- Reset the strength
  ---- ADD THIS
  self.tempStrength = self.strength
  -------------
end

function Entity:resolveCollision(e)
  -- Compare the tempStrength
  ---- ADD THIS
  if self.tempStrength > e.tempStrength then
    e:resolveCollision(self)
    -- Return because we don't want to continue this function.
    return
  end
  -------------

  if self:checkCollision(e) then
    -- Copy the tempStrength
    ---- ADD THIS
    self.tempStrength = e.tempStrength
    -------------
    if self:wasVerticallyAligned(e) then
      if self.x + self.width/2 < e.x + e.width/2 then
        local pushback = self.x + self.width - e.x
        self.x = self.x - pushback
      else
        local pushback = e.x + e.width - self.x
        self.x = self.x + pushback
      end
    elseif self:wasHorizontallyAligned(e) then
      if self.y + self.height/2 < e.y + e.height/2 then
        local pushback = self.y + self.height - e.y
        self.y = self.y - pushback
      else
        local pushback = e.y + e.height - self.y
        self.y = self.y + pushback
      end
    end
  end
end

Presque fini! Le problème est maintenant que le joueur pousse la boîte dans le mur et que le mur repousse la boîte dans le joueur, mais après cela, nous ne résolvons plus jamais la collision entre la boîte et le joueur. Parce que nous l’avons déjà fait dans cette boucle. Donc, ce que nous devons faire, c’est continuer à vérifier la collision entre les objets jusqu’à ce que toute collision soit résolue.

Faisons en sorte que la fonction Entity:resolverCollision (e) renvoie vrai ou faux en fonction de la collision. S’il y a collision, nous parcourons à nouveau les objets et vérifions une fois de plus s’il y a collision.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
function Entity:resolveCollision(e)
  -- Compare the tempStrength
  if self.tempStrength > e.tempStrength then
    -- We need to return the value that this method returns
    -- Else it will never reach main.lua
    ---- ADD THIS
    return e:resolveCollision(self)
    -------------
  end

  if self:checkCollision(e) then
    self.tempStrength = e.tempStrength
    if self:wasVerticallyAligned(e) then
      if self.x + self.width/2 < e.x + e.width/2 then
        local pushback = self.x + self.width - e.x
        self.x = self.x - pushback
      else
        local pushback = e.x + e.width - self.x
        self.x = self.x + pushback
      end
    elseif self:wasHorizontallyAligned(e) then
      if self.y + self.height/2 < e.y + e.height/2 then
        local pushback = self.y + self.height - e.y
        self.y = self.y - pushback
      else
        local pushback = e.y + e.height - self.y
        self.y = self.y + pushback
      end
    end
    -- There was collision! After we've resolved the collision return true
    ---- ADD THIS
    return true
    -------------
  end
  -- There was NO collision, return false
  -- (Though not returning anything would've been fine as well)
  -- (Since returning nothing would result in the returned value being nil)
  ---- ADD THIS
  return false
  -------------
end

Maintenant, nous voulons faire en sorte qu’il continue de vérifier la collision tant qu’il y a eu résolution de la collision. Pour cela, nous pouvons utiliser une boucle while. Cela continue de boucler tant que la déclaration est vraie. Attention cependant! Si vous utilisez une boucle while dans le mauvais sens, cela peut créer une boucle sans fin et planter votre jeu. Pour cette raison, ajoutons également une limite au nombre de boucles. Il peut arriver que, dans une occasion étrange, nous continuions à avoir des collisions et nous nous retrouvions coincés dans une boucle inifite. Il vaut mieux être en sécurité. Si après 100 boucles il y a encore collision, nous rompons la boucle while.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
--! file:main.lua
function love.update(dt)
  -- Update all the objects
  for i,v in ipairs(objects) do
    v:update(dt)
  end

  local loop = true
  local limit = 0

  while loop do
    -- Set loop to false, if no collision happened it will stay false
    loop = false

    limit = limit + 1
    if limit > 100 then
      -- Still not done at loop 100
      -- Break it because we're probably stuck in an endless loop.
      break
    end

    for i=1,#objects-1 do
      for j=i+1,#objects do
        local collision = objects[i]:resolveCollision(objects[j])
        if collision then
          loop = true
        end
      end
    end
  end
end

Ça marche! Ça marche enfin! On peut même avoir plusieurs boites et ça marche toujours !

Animation résultat final avec plusieurs boites
Animation résultat final avec plusieurs boites

Bibliothèques

Notre collision fonctionne très bien, mais loin d’être parfaite. Pour une gestion plus avancée des collisions, consultez Bump. Pour une collision avec des formes autres que des rectangles, consultez HC.

Résumé

Nous pouvons résoudre la collision en poussant le joueur hors de l’objet avec lequel il entre en collision. Nous pouvons utiliser une propriété de force pour déterminer quel objet doit pousser lequel. Nous pouvons transmettre cette force de sorte que lorsque vous poussez une boîte dans un mur, la boîte repoussée par le mur repousse le joueur. Nous devons continuer à résoudre les collisions avec les objets jusqu’à ce qu’il n’y ait plus de collision. Avec une boucle while, nous pouvons parcourir quelque chose tant que la déclaration utilisée est vraie. Nous devons être prudents avec l’utilisation d’une boucle while, car une boucle sans fin fera planter notre jeu.