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:

objects = {}

objects["player"] = newObject(x, y, width, height)
objects["box"] = newObject(x, y, width, height)
objects["enemy"] = newObject(x, y, width, height)

objects[1] = newObject(x, y, width, height)
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...

10 
11 
12 
objects = {}

objects["players"] = {}
table.insert( objects["player"], newObject(x, y, width, height) )

objects["boxes"] = { newObject(x, y, width, height), newObject(x, y, width, height) }
objects["enemies"] = {}
objects["enemies"]["spike"] = newObject(x, y, width, height)
objects["enemies"]["cannon"] = newObject(x, y, width, height)

objects["animals"] = {}
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:

10 
objects = {}
objects["box"] = {}
objects["box"][1] = {x = 20, y = 30, width = 10, height = 10,
                    image = myTexture, friction = .98}
objects["box"][2] = {x = 50, y = 40, width = 10, height = 10,
                    image = myTexture, friction = .98}
objects["box"][3] = {x = 40, y = 30, width = 10, height = 10,
                    image = myTexture, friction = .98}
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):

10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
objects = {}
objects["box"] = {}
table.insert( objects["box"], newBox(20, 30) )
table.insert( objects["box"], newBox(50, 40) )
table.insert( objects["box"], newBox(40, 30) )
table.insert( objects["box"], newBox(70, 80) )

function newBox(x, y)
    local self = {}
    
    self.x = x
    self.y = y
    self.width = 10
    self.height = 10
    self.image = myTexture
    self.friction = .98
    
    return self
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:

10 
11 
12 
13 
14 
15 
16 
17 
18 
box = class:new()

function box:init(x, y)
    self.x = x
    self.y = y
    self.width = 10
    self.height = 10
    self.image = myTexture
    self.friction = .98
end

function box:update(dt)
    self.x = self.x+dt*5
end

function box:draw()
    love.graphics.rectangle("fill", self.x, self.y, self.width, self.height)
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:

function physics_load()
    --this is where you'll load the default variables
end

function physics_update(dt)
    --this is where collisions will be detected
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:

function inside(x1, y1, w1, h1, x2, y2, w2, h2)
    if x1 >= x2 and x1+w1 < x2+w2 and y1 >= y2 and y1+h1 < y2+h2 then
        return true
    end
    return false
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:

10 
11 
12 
13 
function checkCollision(x1, y1, w1, h1, x2, y2, w2, h2)
    local collided = false
    if inside(x1, y1, 0, 0, x2, y2, w2, h2) then
        collided = true
    elseif inside(x1+w1, y1, 0, 0, x2, y2, w2, h2) then
        collided = true
    elseif inside(x1, y1+h1, 0, 0, x2, y2, w2, h2) then
        collided = true
    elseif inside(x1+w1, y1+h1, 0, 0, x2, y2, w2, h2) then
        collided = true
    end
    return collided
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:

10 
11 
12 
13 
14 
15 
16 
function checkCollision(x1, y1, w1, h1, x2, y2, w2, h2)
    local collided = false
    if inside(x1, y1, 0, 0, x2, y2, w2, h2) or inside(x2, y2, 0, 0, x1, y1, w1, h1) then
        collided = true
    elseif inside(x1+w1, y1, 0, 0, x2, y2, w2, h2)
            or inside(x2+w2, y1, 0, 0, x1, y1, w1, h1) then
        collided = true
    elseif inside(x1, y1+h1, 0, 0, x2, y2, w2, h2)
            or inside(x2, y2+h2, 0, 0, x1, y1, w1, h1) then
        collided = true
    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
        collided = true
    end
    return collided
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:

10 
function checkCollision(x1, y1, w1, h1, x2, y2, w2, h2)
    local collided = false
    local p1x, p1y = math.max(x1, x2), math.max(y1, y2)
    local p2x, p2y = math.min(x1+w1, x2+w2), math.min(y1+h1, y2+h2)
    if p2x-p1x > 0 and p2y-p1y > 0 then
        collided = true
    end
    
    return collided
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!

10 
11 
12 
13 
14 
15 
16 
17 
18 
function checkCollision(x1, y1, w1, h1, x2, y2, w2, h2)
    local collided = false
    local p1x, p1y = math.max(x1, x2), math.max(y1, y2)
    local p2x, p2y = math.min(x1+w1, x2+w2), math.min(y1+h1, y2+h2)
    local sideBySide = false
    
    if sideBySide then
        if p2x-p1x >= 0 and p2y-p1y >= 0 then
            collided = true
        end
    else
        if p2x-p1x > 0 and p2y-p1y > 0 then
            collided = true
        end
    end
    
    return collided
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:

function physics_update(dt)
    for i, v in pairs(objects) do
        for j, w in pairs(objects) do
            if i ~= j then
                --collision
            end
        end
    end
end


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

10 
11 
12 
13 
14 
15 
16 
17 
function physics_update(dt)
    for m, obj1 in pairs(objects) do
        for i, v in pairs(obj1) do
            
            for n, obj2 in pairs(objects) do
                for j, w in pairs(obj2) do
                    
                    if not (m == n and i == j) then
                        --collision
                    end
                    
                end
            end
            
        end
    end
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.

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 
function physics_update(dt)
    for m, obj1 in pairs(objects) do
        for i, v in pairs(obj1) do
            
            for n, obj2 in pairs(objects) do
                for j, w in pairs(obj2) do
                    
                    if not (m == n and i == j) then
                        local sideBySide = false
                        -- You can also set this as a global variable in
                        -- the physics.load function
                        
                        
                        if checkCollision(v, w, sideBySide) then
                            -- Detect collision direction
                        end
                    end
                    
                end
            end
        end
    end
end

function checkCollision(obj1, obj2, sbs)
    local collided = false
    local p1x, p1y = math.max(obj1.x, obj2.x), math.max(obj1.y, obj2.y)
    local p2x = math.min(ob1.x+obj1.width, obj2.x+obj2.width)
    local p2y = math.min(obj1.y+obj1.height, obj2.y+obj2.height)
    local sideBySide = sbs or false
    
    if sideBySide then
        if p2x-p1x >= 0 and p2y-p1y >= 0 then
            collided = true
        end
    else
        if p2x-p1x > 0 and p2y-p1y > 0 then
            collided = true
        end
    end
    
    return collided
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:

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

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

10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
function checkCollision(obj1, obj2)
    local right = (obj1.x+obj1.width) - obj2.x
    local left = (obj2.x+obj2.width) - obj1.x
    local bottom = (obj1.y+obj1.height) - obj2.y
    local top = (obj2.y+obj2.height) - obj1.y
    
    if right < left and right < top and right < bottom then
        --Right collision for obj1, left for obj2
        return "right"
    elseif left < top and left < bottom then
        --Left collision for obj1, right for obj2
        return "left"
    elseif top < bottom then
        --Top collision for obj1, bottom for obj2
        return "top"
    else
        --Bottom collision for obj1, top for obj2
        return "bottom"
    end
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:

10 
11 
12 
13 
14 
function bump(obj1, obj2)
    local dir = checkCollision(obj1, obj2)
    --This will return the direction of the collision
    
    if dir == "top" then
        obj1.y = obj2.y + obj2.height
    elseif dir == "bottom" then
        obj1.y = obj2.y - obj1.height
    elseif dir == "left" then
        obj1.x = obj2.x + obj2.width
    elseif dir == "right" then
        obj1.x = obj2.x - obj1.width
    end
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 measurements 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).

10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
function bump(obj1, obj2)
    local dir = checkCollision(obj1, obj2)
    --This will return the direction of the collision
    
    local width = math.min(obj1.x+obj1.width, obj2.x+obj2.width)
                    - math.max(obj1.x, obj2.x)
    local height = math.min(obj1.y+obj1.height, obj2.y+obj2.height)
                    - math.max(obj1.y, obj2.y)
    --We're getting the measurements of the intersection rectangle here
    
    if dir == "top" then
        obj1.y = obj2.y + obj2.height
    elseif dir == "bottom" then
        obj1.y = obj2.y - obj1.height
    elseif dir == "left" then
        obj1.x = obj2.x + obj2.width
    elseif dir == "right" then
        obj1.x = obj2.x - obj1.width
    end
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:

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

12 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
  3. Hey all,
    I know this is an old post, but I should let you guys know there is a little bug.
    For 'checkCollision' function of method #3, left and right are reversed.

    ReplyDelete
    Replies
    1. You were right. My apologies. I have corrected this mistake now, thanks for the heads up!

      Delete
  4. Hello, this tutorial really helped me. I have a problem tough, when the bottom of a medium sized square collides with the top of a thin long rectangle, it gets pushed beneath the long rectangle.

    ReplyDelete
    Replies
    1. Hello, thanks for reaching out! I'm glad my tutorials are helping you! Which of the three side detection algorithms did you end up using? I believe algorithms 1 and 2 might cause such issues, while 3 seems to be consistently reliable.

      Delete