Wednesday, October 8, 2014

Making a Simple 2D Physics Engine - Part 1

Hello again! I've seen a few physics engine (mostly with the framework I work with, Love2D). I always thought they were complicated and too confusing for someone like me trying to venture out and make something similar. Turns out I decided to participate in the Love-Jam #2, a competition where you have a very limited time to make a game based on a given theme. This Jam's theme was "FUSION", and I had a more or less general idea of what I wanted to make.
The problem is that, even though I was allowed to use open-source, free libraries to use with it (I could simply download a physics simulating library and do the rest of the game), I decided I should give it a try and have something 100% mine.

If you'd like to see more about the LÖVE Forums' post for this tutorial, just CLICK HERE!

In this post, I want to help (however I can) you guys making (or at least understanding) how a *SIMPLE* 2D physics engine works. Hopefully, you'll also be less "afraid" of trying it, when you see that, even though it requires a bit of math and computer logic, it's not as hard as it looks.

This is the part 1 of a 3-part tutorial.
Part 1:  Collision detection/handling;
Part 2: Gravity, friction, speed, masks and other global/local concepts;
Part 3: Drawing objects and optimizing your engine.

So click on "Read more" to go to the tutorial!


I'll try to make this "cross-coding language". That means that, even though I use Lua scripting, I'll explain it in a way that you can reproduce it very easily on most other languages.


The VERY first step is having objects. Lua is not exactly made for object-oriented scripts, but for our luck they made it so you can very simply implement objects. You can either use a class function ("converts" tables into objects) or simply use tables alone.

There are two major ways of having objects tables, but you surely can make it your own way, as long as you can understand it and call it again later.


Objects Table: Method 1
 - Unidimensional table

The first one is a simple, single table:

1:  objects = {}  
2:    
3:  objects["player"] = newObject(x, y, width, height)  
4:  objects["box"] = newObject(x, y, width, height)  
5:  objects["enemy"] = newObject(x, y, width, height)  
6:    
7:  objects[1] = newObject(x, y, width, height)  
8:  table.insert( objects, newObject(x, y, width, height, value) )  


As you see, you can either have named objects or numbered objects. The way we're going to make the checking analyzes both strings and numbers on a table. This also keeps the table simple and the collision checking cleaner and slightly faster.

Objects Table: Method 2
 - Bidimensional table

This method is a little more organized, and personally my favorite...

1:  objects = {}  
2:    
3:  objects["players"] = {}  
4:  table.insert( objects["player"], newObject(x, y, width, height) )  
5:    
6:  objects["boxes"] = { newObject(x, y, width, height), newObject(x, y, width, height) }  
7:  objects["enemies"] = {}  
8:  objects["enemies"]["spike"] = newObject(x, y, width, height)  
9:  objects["enemies"]["cannon"] = newObject(x, y, width, height)  
10:    
11:  objects["animals"] = {}  
12:  objects["animals"][1] = newObject(x, y, width, height)  


In this case, we can have a little more organization, since similar objects can be stored together. So, if you want to have several objects in the same level/map/world that have the same basic configurations, you'll find this method more organized. Just like method 1, this one allows for either strings or numbers in the table's organization...

Now, you'll probably want to make objects creation easier. If you're gonna make several similar objects, you'll probably not want to make this:

1:  objects = {}  
2:  objects["box"] = {}  
3:  objects["box"][1] = {x = 20, y = 30, width = 10, height = 10, image = myTexture, friction = .98}  
4:  objects["box"][2] = {x = 50, y = 40, width = 10, height = 10, image = myTexture, friction = .98}  
5:  objects["box"][3] = {x = 40, y = 30, width = 10, height = 10, image = myTexture, friction = .98}  
6:  objects["box"][4] = {x = 70, y = 80, width = 10, height = 10, image = myTexture, friction = .98}  


So you'll most likely want to make a function (or more than one, in case you're doing more advanced and specific objects):

1:  objects = {}  
2:  objects["box"] = {}  
3:  table.insert( objects["box"], newBox(20, 30) )  
4:  table.insert( objects["box"], newBox(50, 40) )  
5:  table.insert( objects["box"], newBox(40, 30) )  
6:  table.insert( objects["box"], newBox(70, 80) )  
7:    
8:  function newBox(x, y)  
9:      local self = {}  
10:        
11:      self.x = x  
12:      self.y = y  
13:      self.width = 10  
14:      self.height = 10  
15:      self.image = myTexture  
16:      self.friction = .98  
17:        
18:      return self  
19:  end  


You can also use a class function, which makes object creation even easier, so you can have an entire file dedicated to an specific object and have it all set up and organized in a simpler way. My favorite class function is Bart van Strien's Simple Educative Class System.

This allow for even more sophisticated object loading/controlling:

1:  box = class:new()  
2:    
3:  function box:init(x, y)  
4:      self.x = x  
5:      self.y = y  
6:      self.width = 10  
7:      self.height = 10  
8:      self.image = myTexture  
9:      self.friction = .98  
10:  end  
11:    
12:  function box:update(dt)  
13:      self.x = self.x+dt*5  
14:  end  
15:    
16:  function box:draw()  
17:      love.graphics.rectangle("fill", self.x, self.y, self.width, self.height)  
18:  end  


If you add a code that draws objects, you'll end up with something more or less like this:


I'll explain later how to draw objects, this is just for demonstration purposes.

You already have your objects organized and "stored". You can already add in some extra data to them (in most cases, we'll have standard, default values, but we'll still be able to customize them per object). For example: gravity, friction, air friction, maximum speed, collision mask, physics world, active, static, etc. Things like weight or rotation or inertia won't be considered here, since this tutorial is just for simple physics games.

Moving on: we should already set up our physics file, so we can have everything organized. You'll need two physics functions: load and update. If you're using Love2D, like me, the update function will be called with a delta time variable, which makes things A LOT more easy (in resume, delta time (or dt) is the time passed between each frame, based on your game's framerate). If your game framework don't have this, you can set this up on a loop. You should have something like this now:

1:  function physics_load()  
2:      --this is where you'll load the default variables  
3:  end  
4:    
5:  function physics_update(dt)  
6:      --this is where collisions will be detected  
7:  end  


The ideal is to have this in an independent file, so you don't get lost nor have problems organizing your code.

You can already set up default variables, but let's first get to the most basic thing we should do right now: detect collisions. For now, we'll only detect them, but not handle effects after collisions.

There are several ways of detecting collisions, and you can search for them on Google, if you feel like it, but in this case I'll showcase the way I came up with.


Collision Checking: Method 1
 - Matching corners

First of all, I made a function to detect when something is inside a given area. So this:

1:  function inside(x1, y1, w1, h1, x2, y2, w2, h2)  
2:      if x1 >= x2 and x1+w1 < x2+w2 and y1 >= y2 and y1+h1 < y2+h2 then  
3:          return true  
4:      end  
5:      return false  
6:  end  


Will, in the future, result in this:

I can't believe I wrote a program just to draw this...

This is not ideal to detect when two objects collide, but in the way I set this up it'll be just right.
The way I set up this: in the object you're checking, you will see if any of it's four corners is inside the other object:


And the code for it:

1:  function checkCollision(x1, y1, w1, h1, x2, y2, w2, h2)  
2:      local collided = false  
3:      if inside(x1, y1, 0, 0, x2, y2, w2, h2) then  
4:          collided = true  
5:      elseif inside(x1+w1, y1, 0, 0, x2, y2, w2, h2) then  
6:          collided = true  
7:      elseif inside(x1, y1+h1, 0, 0, x2, y2, w2, h2) then  
8:          collided = true  
9:      elseif inside(x1+w1, y1+h1, 0, 0, x2, y2, w2, h2) then  
10:          collided = true  
11:      end  
12:      return collided  
13:  end  


The good thing about this method is that it'll always work, right? NO! I just realized (literally, while making this tutorial) that, if the object you're checking is BIGGER than the other object, a point may not match:


In most cases, this will be fixed in the other object collision checking. In my case, specifically, I'm not checking the second object's collision because it is static (in order to optimize my code and reduce lag, I removed collision checking for static objects). To fix that, I quickly made this:

1:  function checkCollision(x1, y1, w1, h1, x2, y2, w2, h2)  
2:      local collided = false  
3:      if inside(x1, y1, 0, 0, x2, y2, w2, h2) or inside(x2, y2, 0, 0, x1, y1, w1, h1) then  
4:          collided = true  
5:      elseif inside(x1+w1, y1, 0, 0, x2, y2, w2, h2) or inside(x2+w2, y1, 0, 0, x1, y1, w1, h1) then  
6:          collided = true  
7:      elseif inside(x1, y1+h1, 0, 0, x2, y2, w2, h2) or inside(x2, y2+h2, 0, 0, x1, y1, w1, h1) then  
8:          collided = true  
9:      elseif inside(x1+w1, y1+h1, 0, 0, x2, y2, w2, h2) or inside(x2+w2, y2+h2, 0, 0, x1, y1, w1, h1) then  
10:          collided = true  
11:      end  
12:      return collided  
13:  end  


What I did there was checking if the second object have any points inside the first, no matter if it is static or not. This works pretty well with the example above:


But then I realized (still while making this tutorial; we're learning together!): there's still one last problem related to this method: a complete lack of points intersections:


Which was solved by the following method...


Collision Checking: Method 2
 - Intersection rectangle

In my physics engine this problem is very unlikely to happen, since I have codes to assure that, after a collision is detected, the object is bumped away from the other object. But, either way, we want our codes to be as optimized as possible, right? Well, at least I do. In the rare event that we generate two objects intersecting each other while no point is inside the other object, we have to make a last checking:

1:  function checkCollision(x1, y1, w1, h1, x2, y2, w2, h2)  
2:      local collided = false  
3:      local p1x, p1y = math.max(x1, x2), math.max(y1, y2)  
4:      local p2x, p2y = math.min(x1+w1, x2+w2), math.min(y1+h1, y2+h2)  
5:      if p2x-p1x > 0 and p2y-p1y > 0 then  
6:          collided = true  
7:      end  
8:        
9:      return collided  
10:  end  


What I'm doing there: I'm mapping two points that represent the rectangle that is formed between the two rectangle's extreme points. If the rectangle sizes are bigger than 0, then it represents an actual collision:


I don't know if you guys noticed, but I've just reinvented the wheel: with this simple new way of checking collision, I don't need to check the four corners anymore.

I'm sorry if you got confused up to this point, since I showed a method then replaced it, but, as I said, I just realized that this method is the best option. Since we're still looking for the concepts of physics and not getting to the coding itself, I think it is better to showcase other methods, their pros and their cons. Just like you, I'm learning right now, literally!

So, this method can detect collisions in any circumstances: corner collisions, "one inside other", crossed objects, etc. It is way simpler, faster and easier than my last method. I'm glad I realized this while making this post, so now my games will be way faster!

Another great point about this is that, with this method, I can not only detect "intersection" collisions (when one object is slightly inside the other), but also side-by-side collisions (when one object is exactly side-by-side with another object). And even better: you can make this optional!

1:  function checkCollision(x1, y1, w1, h1, x2, y2, w2, h2)  
2:      local collided = false  
3:      local p1x, p1y = math.max(x1, x2), math.max(y1, y2)  
4:      local p2x, p2y = math.min(x1+w1, x2+w2), math.min(y1+h1, y2+h2)  
5:      local sideBySide = false  
6:        
7:      if sideBySide then  
8:          if p2x-p1x >= 0 and p2y-p1y >= 0 then  
9:              collided = true  
10:          end  
11:      else  
12:          if p2x-p1x > 0 and p2y-p1y > 0 then  
13:              collided = true  
14:          end  
15:      end  
16:        
17:      return collided  
18:  end  


Now let's stop talking about concepts and let's get to the actual coding. First of all, you'll have to make a loop for each object. This way, on each update, you'll check every object and see if it is colliding! This is a slow process most of the time. The more objects you have, the more lag you'll get. That is normal, there aren't many solutions for that other than optimizing your code, like I said (ignoring static objects, having masks, ignoring the same object being checked twice, etc.).

First thing first: you'll make a loop that checks every physics object you have, and, in order to have it working properly, you ought to organize your code and objects-handling tables. Also, do not add non-physics objects, like particles, for example, to the objects table, so you don't run the risk of ruining everything.

If you chose table organization method 1 (unidimensional), you'll make something more or less like this:

1:  function physics_update(dt)  
2:      for i, v in pairs(objects) do  
3:          for j, w in pairs(objects) do  
4:              if i ~= j then  
5:                  --collision  
6:              end  
7:          end  
8:      end  
9:  end  


But if you chose the second method (bidimensional), you'll make this instead:

1:  function physics_update(dt)  
2:      for m, obj1 in pairs(objects) do  
3:          for i, v in pairs(obj1) do  
4:                
5:              for n, obj2 in pairs(objects) do  
6:                  for j, w in pairs(obj2) do  
7:                        
8:                      if not (m == n and i == j) then  
9:                          --collision  
10:                      end  
11:                        
12:                  end  
13:              end  
14:                
15:          end  
16:      end  
17:  end  


Even though this seems more complicated, in fact it isn't, This is just like the unidimensional method, except that you go through each group of objects, instead of straight through every object. In my opinion, this is still better, since we can have the objects labeled AND get their data, but you're totally free to choose. In fact, if you're making a small game with just a few objects, the first method is better for you. Also, you can identify that, in both cases, I added an if statement. That is there just to make sure we don't check the same object twice, otherwise we'd have to calculate a collision with itself or with a "ghost".

To avoid making this tutorial too much big, I'll stick with the object table's second method, which is my favorite, but if you want to use method 1 it'll be easy for you to figure out how to adapt the code. Also, I'll use the letters "v" and "w" to refer to the colliding objects, so it is even easier for you to identify what is what. I'll also stick with the second collision checking method, since it is the most efficient one, but if you want to use method 1 or a different method, it should still be easy to work around it.


Now it's time to get to the true collision checking. After here, you'll understand how to check collision, how to identify in which side your object collided and how to bump them.

First: collision checking. You already know how to make collision checking, but how do we apply this on our codes? Simple: in the collision checking functions, we always required the objects positions and their sizes, right? Now that we have our objects stored in tables, it becomes even easier to spot these values.

1:  function physics_update(dt)  
2:      for m, obj1 in pairs(objects) do  
3:          for i, v in pairs(obj1) do  
4:                
5:              for n, obj2 in pairs(objects) do  
6:                  for j, w in pairs(obj2) do  
7:                        
8:                      if not (m == n and i == j) then  
9:                          local sideBySide = false  
10:                          -- You can also set this as a global variable in  
11:                          -- the physics.load function  
12:                            
13:                            
14:                          if checkCollision(v, w, sideBySide) then  
15:                              -- Detect collision direction  
16:                          end  
17:                      end  
18:                        
19:                  end  
20:              end  
21:          end  
22:      end  
23:  end  
24:    
25:  function checkCollision(obj1, obj2, sbs)  
26:      local collided = false  
27:      local p1x, p1y = math.max(obj1.x, obj2.x), math.max(obj1.y, obj2.y)  
28:      local p2x = math.min(ob1.x+obj1.width, obj2.x+obj2.width)  
29:      local p2y = math.min(obj1.y+obj1.height, obj2.y+obj2.height)  
30:      local sideBySide = sbs or false  
31:        
32:      if sideBySide then  
33:          if p2x-p1x >= 0 and p2y-p1y >= 0 then  
34:              collided = true  
35:          end  
36:      else  
37:          if p2x-p1x > 0 and p2y-p1y > 0 then  
38:              collided = true  
39:          end  
40:      end  
41:        
42:      return collided  
43:  end  


As you see, the function remains the same, except that we replaced x1, x2, y1, y2, w1, w2, h1 and h2 by obj1 and obj2. Much better, isn't it?

Now that we already know when a collision happens between two objects, it's time to check the collision direction. This way, we know which side of each object (top, bottom, left or right) collided and bump them according to it.


Collision side detection: Method 1
 - Intersection rectangle measuring

Since we already know the collision happened, we know that the "intersection rectangle" we talked about on collision checking: method 2 will always have positive values. This way, we can differentiate a horizontal collision from a vertical collision. After that, it'll be ever easier to get the collision side. Let's get to the actual code:

1:  function checkCollision(obj1, obj2, sbs)  
2:      local collided = false  
3:      local p1x, p1y = math.max(obj1.x, obj2.x), math.max(obj1.y, obj2.y)  
4:      local p2x = math.min(ob1.x+obj1.width, obj2.x+obj2.width)  
5:      local p2y = math.min(obj1.y+obj1.height, obj2.y+obj2.height)  
6:      local sideBySide = sbs or false  
7:        
8:      if sideBySide then  
9:          if p2x-p1x >= 0 and p2y-p1y >= 0 then  
10:              collided = true  
11:          end  
12:      else  
13:          if p2x-p1x > 0 and p2y-p1y > 0 then  
14:              collided = true  
15:          end  
16:      end  
17:        
18:      if collided then  
19:          --Case 1: the width of the intersection is bigger than the height  
20:          --That means that the collision is mostly VERTICAL  
21:          if p2x-p1x > p2y-p1y then  
22:              collided = "vertical"  
23:                
24:              --Case 2: the height of the intersection is bigger than the width  
25:              --That means that the collision is mostly HORIZONTAL  
26:          else  
27:              collided = "horizontal"  
28:          end  
29:      end  
30:        
31:      return collided  
32:  end  


As you see, you don't need to reinvent the wheel (by that I mean checking the intersection rectangle once more). You can simply add this to your already done collision-checking function. What I'm doing here is checking the dimensions of the intersection rectangle; if it's width is greater than it's height, then the collision was vertical, and if it's height is greater than it's width, then the collision was horizontal. To get this concept clear, here's an example:


Now that we've got the basic orientation of the collision, getting the side of it is even easier! You just need to know the position of each rectangle. In a vertical collision, the rectangle above will get a bottom collision, and the rectangle below will have a top collision. On a horizontal collision, the rectangle on the left will get a right collision, and the rectangle on the right will get a left collision.

This method gives us a few problems, thought, that the following method will fix, but I still want you to know that this method works fine in most cases. I'm trying to teach you how to think about the collisions and how to understand them, so this method is not "disposable".

But, still, the problem that this method have happens in the following situation:


In this case, this method would interpret the collision as horizontal, even though we know that it is, in fact, a vertical one. In the early development of my library/engine, I faced this problem but didn't realize what was causing it, until I decided to look closer at the collision side checking code, and noticed that it wasn't the most optimal. Then I came across another solution, much simpler and elegant!


Collision side detection: Method 2
 - Middle-point distances

This method was much better for me, so for now on I'll stick with it in this tutorial. This method not only fixes the problems with the method above, but also gives me the direction of the collision in a single checking, and not only after checking the orientation! What I did here was getting the middle point of each object (their position plus their sizes divided by two). Then, I measure the distance between both middle points. If the horizontal difference is bigger than the vertical, we had a horizontal collision (and not a vertical collision like in the last time). If the difference is a positive number, we had a bottom collision, and if it's negative we had a top collision. The same applies to horizontal collisions. Here's an example, to make things easier to understand:


Having that in mind, doing the actual code is actually pretty easy:

1:  if collided then  
2:      local m1x, m1y = obj1.x+obj1.width/2, obj1.y+obj1.height/2  
3:      local m2x, m2y = obj2.x+obj2.width/2, obj2.y+obj2.height/2  
4:        
5:      --Since the object 1 is the focus, we'll only check it's collision side  
6:      if math.abs(m1x-m2x) > math.abs(m1y-m2y) then --Mostly horizontal  
7:          if m1x-m2x < 0 then --Object 1 is behind object 2, thus a right collision  
8:              collided = "right"  
9:          else  
10:              collided = "left"  
11:          end  
12:      else --Mostly vertical  
13:          if m1y-m2y < 0 then --Object 1 is above object 2, thus a bottom collision  
14:              collided = "bottom"  
15:          else  
16:              collided = "top"  
17:          end  
18:      end  
19:  end  


First, we check if the absolute horizontal distance is bigger than the absolute vertical distance. After knowing if the collision is mostly horizontal or vertical, we can very easily detect in which side the collision occurred: if the distance is smaller than 0, it is either a right or a bottom collision, if bigger or equal to 0, it is either left or top collision. This gives left/top collision slightly more priority: if the two objects are perfectly inside each other (with their central points overlapping one another), the priority will be first to vertical collision (since we check vertical collisions only if the difference between the middle points is equal to or smaller than 0). Later, we give preference to top collision, since it only happens when the difference is equal to or smaller than 0.

What I'm trying to say is that, even though I set the "global preference" to top collision, you can give it to any direction. You can even give it to bottom collision, which makes the objects be pulled upwards instead of downwards. This doesn't matter much, since this kind of situation is preeeeeety much rare.


Collision side detection: Method 3
 - Opposite sides difference

After a while using method 2, I realized it wasn't so perfect as I originally though. Although problems mostly happen with objects with very distinct sizes (which, at the time, I wasn't using), that method is still good for simple games. But this method should fix those problems from the two previous methods. Thanks for the LÖVE Forums' user "Tjakka5" for pointing this out!

In this method, we analyze the difference between the object's extreme points (the object's sides) that are opposite to each other (left-right, right-left, top-bottom, bottom-top), and detect which difference is smaller. Here's a little example:


At first glance it may look a bit complicated, because I simply didn't thrive in making it look simpler, but the concept itself is REALLY simple, and if you look closely, all we're doing is tracing lines between the objects' opposite sides. The red ones are the biggest differences, and the green ones are the smallest. From that, you can already say which sides collided, without needing to detect the collision axis (horizontal or vertical) first! Here's how simple you can do it:

1:  function checkCollision(obj1, obj2)  
2:      local left = (obj1.x+obj1.width) - obj2.x  
3:      local right = (obj2.x+obj2.width) - obj1.x  
4:      local bottom = (obj1.y+obj1.height) - obj2.y  
5:      local top = (obj2.y+obj2.height) - obj1.y  
6:        
7:      if right < left and right < top and right < bottom then  
8:          --Right collision for obj1, left for obj2  
9:          return "right"  
10:      elseif left < top and left < bottom then  
11:          --Left collision for obj1, right for obj2  
12:          return "left"  
13:      elseif top < bottom then  
14:          --Top collision for obj1, bottom for obj2  
15:          return "top"  
16:      else  
17:          --Bottom collision for obj1, top for obj2  
18:          return "bottom"  
19:      end  
20:  end  


In this case, though, it doesn't detect if a collision happened: it only detects the direction. So, if you plan on using this method, I highly recommend using one of the other 2 methods for collision detection only, and not for collision side detection! Then, you use this method to specify the side. In this case, we're returning the collision side of object 1, so if you want to apply effects on the second object, just remember that it got a collision on the opposite side. So if collision 1 was "left", collision 2 will be "right", if collision 1 was "top", collision 2 will be "bottom", and so on.


Objects bumping

Now to the last part of this tutorial: bumping. For those of you who don't know, bumping is pretty much "avoiding objects to occupy the same space at the same time". That is pretty much what happens in Super Mario Bros., but instead of making you slowly slide when you're inside a wall, in our physics engine we'll make that happen instantly. You can surely make this however you want, but for the sake of this tutorial I'll be making the most common method. As you've seen before, we can detect collisions when two bodies overlay each other, but why don't we see that happening in most games? The reason is that we bump the objects just when we detect the collision, leaving no chance of it showing itself being overlapped. You can make the bumping effects in several ways, but I'll just showcase two better ways of doing it (in my opinion).


Objects bumping: Method 1
 - Pushing the first object

This method makes it so the first object to detect a collision will be pushed away. Here's a quick example:


Making this, as you may expect, is quite easy:

1:  function bump(obj1, obj2)  
2:      local dir = checkCollision(obj1, obj2)  
3:      --This will return the direction of the collision  
4:        
5:      if dir == "top" then  
6:          obj1.y = obj2.y + obj2.height  
7:      elseif dir == "bottom" then  
8:          obj1.y = obj2.y - obj1.height  
9:      elseif dir == "left" then  
10:          obj1.x = obj2.x + obj2.width  
11:      elseif dir == "right" then  
12:          obj1.x = obj2.x - obj1.width  
13:      end  
14:  end  


Of course, you can include this either in the collision checking function or in the physics.update function itself. What we're doing here is simply move the object 1 to the closest point outside object 2. This is, basically, the principle of most bumping methods. Now, let's get to the second method...


Objects bumping: Method 2
 - Pushing the objects equally

I personally prefer this one, since it seems more subtle and slightly more realistic, but, again, your choice. Either method will work.

In this method we're moving both objects apart from each other. We're doing this the same way we'd do in the first method, but instead we just move the object half the way and also move the second object:


As you see, since they're moving apart from each other by the same distance, all we have to do is to get the measures of the intersection rectangle. Then we move each object by half of that measure (one half for each object, which results in both not colliding anymore).

1:  function bump(obj1, obj2)  
2:      local dir = checkCollision(obj1, obj2)  
3:      --This will return the direction of the collision  
4:        
5:      local width = math.min(obj1.x+obj1.width, obj2.x+obj2.width) - math.max(obj1.x, obj2.x)  
6:      local height = math.min(obj1.y+obj1.height, obj2.y+obj2.height) - math.max(obj1.y, obj2.y)  
7:      --We're getting the measures of the intersection rectangle here  
8:        
9:      if dir == "top" then  
10:          obj1.y = obj2.y + obj2.height  
11:      elseif dir == "bottom" then  
12:          obj1.y = obj2.y - obj1.height  
13:      elseif dir == "left" then  
14:          obj1.x = obj2.x + obj2.width  
15:      elseif dir == "right" then  
16:          obj1.x = obj2.x - obj1.width  
17:      end  
18:  end  


There are a few more complicated method to do this, but I'm not going to show them in here. You can either search for them on the internet or figure them out by yourselves, since now you have the basic concepts in your mind. Some method include: organizing the objects by position and bump them after a common center, bump every object at a given direction, invert/decrease/remove the objects' speeds, etc.


Conclusion

But that's all for this tutorial. If you need a bit more help with organizing your code, here's a little example:

1:  function physics_update(dt)  
2:      for each object do  
3:          for each object do  
4:                
5:              if objects are not the same then  
6:                    
7:                  if objects are colliding then  
8:                        
9:                      detect collision side  
10:                        
11:                      if collision side is "top" then  
12:                          push object 1 downwards  
13:                          push object 2 upwards  
14:                      elseif collision side is "bottom" then  
15:                          push object 1 upwards  
16:                          push object 2 downwards  
17:                      elseif collision side is "left" then  
18:                          push object 1 to the right  
19:                          push object 2 to the left  
20:                      elseif collision side is "right" then  
21:                          push object 1 to the left  
22:                          push object 2 to the right  
23:                      end  
24:                        
25:                  end  
26:                    
27:              end  
28:                
29:          end  
30:      end  
31:  end  


If you're still wondering "How about gravity?", "How do I add friction?", "How to handle static objects?", "What if I want certain objects not to collide?" or something similar, don't worry: I'll be doing another tutorial pretty soon with some more "advanced" stuff. Thanks for reading, leave your ideas, feedback and suggestions in the comment section if you want, and I see you guys in my next post!

PART 2 >>    |    PART 3 >>

8 comments:

  1. Awesome! it really help me finally doing collisions properly, but is there a way that you can put the code in boxes and not like images so we can copy past it? like the [code][/code] tags in the LÖVE forums.

    ReplyDelete
    Replies
    1. Unfortunately Blogger don't have this, but I'll try to add it in a way that's easy to copy and don't take a bunch of space. Thanks for the feedback!

      EDIT: I got them working now, so check the post again!

      Delete
    2. Thanks! now I can finally make collisions work in my game. Thanks again!

      Delete
    3. No problem, I'm glad I could help. If you have any other suggestions, feel free to let me know too!

      Delete
  2. Great tutorial! You used a lot of smarrt tricks for collision detection.

    For me cillision detection and resolving is one of the hardest things, so I'll definatly save some of this as code snippets on my laptop :)

    ReplyDelete
    Replies
    1. I'm glad I could help you and that I made it simple for you to understand :)

      Delete