selfmade collision system doesn't work well

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
Konikoko
Prole
Posts: 5
Joined: Sat Aug 03, 2019 3:14 pm

selfmade collision system doesn't work well

Post by Konikoko »

Hello!

I'm new to programming with LÖVE (but not with programming in general, however I consider myself not that good. I still have a lot to learn).

After getting to know Lua and LÖVE I decided to create a little platformer game to exercise.

At first I followed a platformer tutorial but after a bit I started going my own way.

So a couple of days ago I tried to implement my own collision system (I didn't want to use libraries like box2D).
It's a very simple one. Basically: My world is divided in tiles. Every tile has a specific property (solid, air, etc).
If the player moves and touches a solid block then I set the player to the previous position and update the velocity.

However after implementing this the collision is very glitchy. I think that this has to do with the dt value passed to the update function.

For my collision system to work, the collisions must be calculated pixel per pixel. So I calculate the new position, check the coordinate and if the new position is colliding with a solid tile then I stop velocity and I set the position to the previous position.

But I think when the collisions are calculated, the player moves multiple pixels (so the previous position is far too different)

Atleast that's what I think is wrong. I already searched the forums and I tried different solutions listed on those topics (rounding the coordinates, accumulating dt,...) but it didn't fix anything (maybe I did something wrong)

I hope you guys can point me to the right direction.

And for anyone reading my code: I tried to apply OOP in lua but I am not sure if I did it right (I tried to use a metatable in my renderer and it seems to work). If you have any remarks on my code, please tell! And sorry if made any mistakes writing this, english isn't my first language.
Attachments
platformer.love
(4.95 KiB) Downloaded 176 times
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: selfmade collision system doesn't work well

Post by pgimeno »

Sorry I missed this post. Due to a bug in the forum software, posts from new users are sometimes not displayed as unread.

Konikoko wrote: Sat Aug 03, 2019 3:42 pm For my collision system to work, the collisions must be calculated pixel per pixel. So I calculate the new position, check the coordinate and if the new position is colliding with a solid tile then I stop velocity and I set the position to the previous position.

But I think when the collisions are calculated, the player moves multiple pixels (so the previous position is far too different)
Yeah, nothing guarantees that the movement happens 1 pixel at a time. In fact that will rarely happen; for that, you need the speed of the object in pixels per second to match the frame rate exactly (e.g. 60 pixels per second in a 60 Hz monitor) and that you don't get any frame stutter. As additional problems, your program wouldn't run in monitors with a different frame rate, and you'd be stuck with a single resolution if you want the relative speed to be kept.

If you have a 60 Hz monitor, and try writing a simple program that advances 1 pixel per frame, you'll see that 60 pixels per second is typically very slow. The speeds of most objects in most games are typically higher than 1 pixel per frame.

So, that's not a viable solution. You do need to use dt, and the number of pixels per second of your objects is probably going to be higher than one.

As a consequence, it will most often be the case that in one frame, the object is at a distance of the wall, and in the next frame the object would be penetrating the wall. If you respond to that collision by going back to the previous position, your object will be placed again at a distance from the wall. The correct response is to place the object in contact with the wall (or the floor - there's no reason to treat these as separate cases).

Collision is a more complicated subject than it seems. On one hand there's collision detection (check that there is a collision of any kind), on the other hand there's collision response (correcting the position of objects in a way that makes sense).

Collision detection seems simple. It normally is, unless there's a possibility that the speed of an object can cause it to be on one side of another object in one frame, and on the other side of the same object in the next frame, without intersecting it in either case, causing the collision to be missed. This does not seem to apply to your game, so you're fine with simple rectangle intersection, but to ensure that this won't be a problem even with extreme lag, you should limit your dt (e.g. to 0.1 seconds max). The alternative is more complicated, as it involves checking if there's an obstacle between the object's position in one frame and in the next, to avoid skipping any collisions. That is called "swept collision detection". If you limit your dt, you can skip that and keep it as a simple rectangle intersection.

Collision response is not so simple. There's a lot of literature on it, and none of it is about a simple few-lines function that resolves a collision. If you really want to get into it all by yourself, just search the web for 'AABB collision handling'. For example, here's a fairly simple algorithm together with an explanation of many of the issues that may arise: https://hopefultoad.blogspot.com/2017/0 ... ponse.html

If you're more convinced about the complexity of the task and think it's too much for you alone, I recommend you to use a library with collision resolution. For your case, Bump is probably the best suited one: https://github.com/kikito/bump.lua

On a separate note, I've noticed you use ZQSD for movement, which tells me you use an AZERTY keyboard. If you want your game to work with any keyboard layout, instead of love.keyboard.isDown use love.keyboard.isScancodeDown and use W instead of Z, and A instead of Q. You won't notice the difference, but people with other keyboard layouts will be able to play your game too.
Konikoko
Prole
Posts: 5
Joined: Sat Aug 03, 2019 3:14 pm

Re: selfmade collision system doesn't work well

Post by Konikoko »

Thanks for answering! I kept trying and I improved the code a bit. It is not perfect yet (sometimes the player is a bit placed into the wall (like one pixel)) but the stuttering and blocking of movement is gone. I think if you add sprites and animation then the problem of being placed into the wall is far less noticeable (because of all the squares if it isn't aligned perfectly then it's pretty visible).
pgimeno wrote: Mon Aug 05, 2019 3:02 pm Yeah, nothing guarantees that the movement happens 1 pixel at a time. In fact that will rarely happen; for that, you need the speed of the object in pixels per second to match the frame rate exactly (e.g. 60 pixels per second in a 60 Hz monitor) and that you don't get any frame stutter. As additional problems, your program wouldn't run in monitors with a different frame rate, and you'd be stuck with a single resolution if you want the relative speed to be kept.

If you have a 60 Hz monitor, and try writing a simple program that advances 1 pixel per frame, you'll see that 60 pixels per second is typically very slow. The speeds of most objects in most games are typically higher than 1 pixel per frame.

So, that's not a viable solution. You do need to use dt, and the number of pixels per second of your objects is probably going to be higher than one.
You're right but I managed calculating collision based on the position of the player pixel per pixel. I think I explained it pretty bad in my post but basically what I do now is the following:

I split the dt given to my function in multiple parts so that when I move in the function, the position only changes by one pixel (x or y).

Basically, movement doesn't happen 1 pixel at a time but checking for collision now does happen one coordinate at a time.
pgimeno wrote: Mon Aug 05, 2019 3:02 pm As a consequence, it will most often be the case that in one frame, the object is at a distance of the wall, and in the next frame the object would be penetrating the wall. If you respond to that collision by going back to the previous position, your object will be placed again at a distance from the wall. The correct response is to place the object in contact with the wall (or the floor - there's no reason to treat these as separate cases).

Collision is a more complicated subject than it seems. On one hand there's collision detection (check that there is a collision of any kind), on the other hand there's collision response (correcting the position of objects in a way that makes sense).

Collision detection seems simple. It normally is, unless there's a possibility that the speed of an object can cause it to be on one side of another object in one frame, and on the other side of the same object in the next frame, without intersecting it in either case, causing the collision to be missed. This does not seem to apply to your game, so you're fine with simple rectangle intersection, but to ensure that this won't be a problem even with extreme lag, you should limit your dt (e.g. to 0.1 seconds max). The alternative is more complicated, as it involves checking if there's an obstacle between the object's position in one frame and in the next, to avoid skipping any collisions. That is called "swept collision detection". If you limit your dt, you can skip that and keep it as a simple rectangle intersection.
I also added collision checking in "advance" to tackle the exact problem you described in the first paragraph.

The collision response was exactly the part of collision detection I had a problem with. Putting a max on the dt won't be necessary because of the split that is now happening.
pgimeno wrote: Mon Aug 05, 2019 3:02 pm Collision response is not so simple. There's a lot of literature on it, and none of it is about a simple few-lines function that resolves a collision. If you really want to get into it all by yourself, just search the web for 'AABB collision handling'. For example, here's a fairly simple algorithm together with an explanation of many of the issues that may arise: https://hopefultoad.blogspot.com/2017/0 ... ponse.html

If you're more convinced about the complexity of the task and think it's too much for you alone, I recommend you to use a library with collision resolution. For your case, Bump is probably the best suited one: https://github.com/kikito/bump.lua
Thanks for the links. I know it's probably pointless to reinvent the wheel but as a challenge it definitely helped me understand collision detection a bit better. For serious projects I will probably use a library.
pgimeno wrote: Mon Aug 05, 2019 3:02 pm On a separate note, I've noticed you use ZQSD for movement, which tells me you use an AZERTY keyboard. If you want your game to work with any keyboard layout, instead of love.keyboard.isDown use love.keyboard.isScancodeDown and use W instead of Z, and A instead of Q. You won't notice the difference, but people with other keyboard layouts will be able to play your game too.
I completely forgot about the controls before posting here! Thanks for the tip and thanks for answering!

I going to try a bit to see if I can fix the 1 pixel of stuff so I'll keep this topic open for a bit if that is okay?
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 49 guests