Page 1 of 1

Collision detection response issue

Posted: Sat Jan 11, 2014 8:48 pm
by Gorus
Hello fellow coders.
I have stumbled upon a problem while writing a code for my top-down tile-map game.
Note that the player's movement isn't restricted to tiles, it's free movement.

Firstly, to get any idea of what I'm trying to do I'll post the way I use the basic default

Code: Select all

for i,lev in ipairs(level_draw) do
	if CheckCollision(player.x + (player.xvel * dt)*2, player.y + (player.yvel * dt)*2, player.height, player.width, lev.x, lev.y, level.tile_h, level.tile_w) then
		if lev.tile_id == '1' then
			player.xvel = 0
			player.yvel = 0
		end
	end
end
And here's the movement code

Code: Select all

if love.keyboard.isDown('d') and player.xvel < player.speed then
	player.xvel = player.xvel + player.speed * dt
end
	
if love.keyboard.isDown('a') and player.xvel > -player.speed then
	player.xvel = player.xvel - player.speed * dt
end
	
if love.keyboard.isDown('w') and player.yvel > -player.speed then
	player.yvel = player.yvel - player.speed * dt
end
	
if love.keyboard.isDown('s') and player.yvel > -player.speed then
	player.yvel = player.yvel + player.speed * dt
end
And the friction code (pretty much copy-paste from a tutorial on youtube)

Code: Select all

player.x = player.x + player.xvel * dt
player.y = player.y + player.yvel * dt
player.xvel = player.xvel * (1 - math.min(dt*player.friction, 1))
player.yvel = player.yvel * (1 - math.min(dt*player.friction, 1))


The collision works perfectly fine, however, the issue I found is that this makes the controls bad, for example, if I move down causing a collision with a tile below me, and then, while still holding the move down key I press left, I would expect to be able to move left while not being allowed to move down. Note that I know that when the collision is detected, it sets both velocities to 0 therefor disabling the behavior I want from even happening, but that is the entire problem here, I have spent 2 days now stumped on how to make it happen.

Any input would be appreciated.

P.S. to the board admins, sorry I submitted this issue twice, I apparently missed the part where it says posts have to be approved and though it was just somehow eaten.

Re: Collision detection response issue

Posted: Sat Jan 11, 2014 10:36 pm
by micha
If the colliding tiles are all on the grid, then it is enough to decouple the two coordinate axes:

First move the player only in x direction and check for collision, if so then correct the x-velocity.
Then do the same for the y direction.

Re: Collision detection response issue

Posted: Sun Jan 12, 2014 12:25 am
by Gorus
You mean like run a check on when the friction is calculated for each direction?
The problem with this kind of friction function is that even when the player is still, the xvel and yvel variables have a value, that's constantly changing from 0 to 3 or something like that, because of the delta time used in the function.
By the way, the movement is not restricted to one direction, it's fully analog.

I will, however, give this a shot and edit this post with the results.

EDIT: well I tried attaching the check for each direction of movement (inside the keyboard.isDown functions) and it now does what I intended it to do, however, I'm worried if so many checks don't cause a huge amount of calculations?

EDIT2: attached a check after the velocity calculations if they are higher than the oscillating dt number and it seems to work perfectly now. I kinda feel like the movement is stuttering a bit, doesn't really eat up a lot of cpu or memory, but it seems to be stuttering anyway. Also noticed that x axis collision while moving on y axis as well now causes x axis to decelerate to 80% of the speed and then jump back up, while the same thing on y axis doesn't do that and the movement is fluid. Any ideas?

Re: Collision detection response issue

Posted: Sun Jan 12, 2014 9:04 am
by ivan
And the friction code (pretty much copy-paste from a tutorial on youtube)

Code: Select all

player.x = player.x + player.xvel * dt
player.y = player.y + player.yvel * dt
player.xvel = player.xvel * (1 - math.min(dt*player.friction, 1))
player.yvel = player.yvel * (1 - math.min(dt*player.friction, 1))
This is not friction but "damping". "Friction" generally means loss in velocity when the edges of two shapes are touching.
Also I believe that the way you apply damping might be incorrect. You want to apply it to both the X and Y axis at the same:

Code: Select all

-- clamps number between two values
function clamp(n, low, high)
  return math.min(math.max(n, low), high)
end

    -- damping
    local damp = clamp(1 - DAMPING*dt, 0, 1)
    player.xvel = player.xvel*damp
    player.yvel = player.yvel*damp
Where DAMPING is the damping value for the moving object.
Gorus wrote:

Code: Select all

if love.keyboard.isDown('d') and player.xvel < player.speed then
	player.xvel = player.xvel + player.speed * dt
end
	
if love.keyboard.isDown('a') and player.xvel > -player.speed then
	player.xvel = player.xvel - player.speed * dt
end
	
if love.keyboard.isDown('w') and player.yvel > -player.speed then
	player.yvel = player.yvel - player.speed * dt
end
	
if love.keyboard.isDown('s') and player.yvel > -player.speed then
	player.yvel = player.yvel + player.speed * dt
end
The collision works perfectly fine, however, the issue I found is that this makes the controls bad, for example, if I move down causing a collision with a tile below me, and then, while still holding the move down key I press left, I would expect to be able to move left while not being allowed to move down
There are a few problems with your code. First, your velocity will be greater when you move diagonally.
If you want to be restrict movement to 1 direction at a time you have to write something like:

Code: Select all

if isDown('w') then

elseif isDown('a') then

elseif isDown('s') then

elseif isDown('d') then

end
Notice the elseif instead of "if".
Note that I know that when the collision is detected, it sets both velocities to 0 therefor disabling the behavior I want from even happening, but that is the entire problem here, I have spent 2 days now stumped on how to make it happen.
This is a non-trivial issue in a sense that there is no simple, general solution.
You can't just set the velocity to 0 for several reasons.
Your code checks for a collision in the NEXT frame and sets the player's velocity to 0. This will leave a gap between the player and the shape he is supposed to collide with.
Usually, you want to:
1. move the player based on his velocity
2. check for overlap (intersection) between the player and all other shapes
3. move the player so that he is not overlapping any shapes
4. adjust the player's velocity accordingly
The most complicated part is (4) because you have to figure out how you want your player to respond to collisions. Do you want him to "slide" along edges of walls or should he stop (aka friction)? Do you want your player to bounce off walls or stick to them (aka bounce or restitution).
I wrote a short tutorial on collisions: http://love2d.org/forums/viewtopic.php?f=5&t=75931 that you might find helpful.

Also take a look at some of the other (simpler) collisions libs like kikito's bump.lua or my humble fizzx for inspiration.

Re: Collision detection response issue

Posted: Sun Jan 12, 2014 2:15 pm
by Gorus
Thanks for the insight, I'll have a look at that.
I have however fixed all the issues by placing some checks above the velocity calculations, but I feel like there's way too much work the CPU has to do so maybe the solutions you provided will help solve that.
I'll make a new post if something goes terribly wrong, but for now I think I got all the info I need.
Thanks

EDIT: I uploaded the love file so that you can check out the result of what I have so far. Press M to generate a map, wasd to move, currently I have 2 maps (very stupid maps, just for testing) that have a 50% chance of appearing when you press M again, try the movement and tell me how it feels.

Re: Collision detection response issue

Posted: Mon Jan 13, 2014 7:11 am
by ivan
Gorus wrote:Thanks for the insight, I'll have a look at that.
I have however fixed all the issues by placing some checks above the velocity calculations, but I feel like there's way too much work the CPU has to do so maybe the solutions you provided will help solve that.
Runs fine on my machine. If you plan to have many projectiles in there you might have some slowdown because the number of collision checks increases exponentially without some sort of partitioning.
Gorus wrote:I'll make a new post if something goes terribly wrong, but for now I think I got all the info I need.
Cool. One thing I noticed however: the player slows down as he approaches walls. Since your collision checks are frame based and dt varies each frame, the larger the dt the greater the gap between the player and the wall. This is not a big deal but if you have fast moving objects it might become more noticeable.

Re: Collision detection response issue

Posted: Mon Jan 13, 2014 9:21 am
by Gorus
ivan wrote: Cool. One thing I noticed however: the player slows down as he approaches walls. Since your collision checks are frame based and dt varies each frame, the larger the dt the greater the gap between the player and the wall. This is not a big deal but if you have fast moving objects it might become more noticeable.
Would replacing dt in those calculations with a set value impact the game in a bad way?
Also, dt can be drastically different on each machine right? I guess I'll have to find a way to do it without dt, as if someone has a slower pc and the dt is bigger, the movement could be different.

Re: Collision detection response issue

Posted: Mon Jan 13, 2014 9:42 am
by micha
If the objects stop with a short distance before the wall, then this is probably because of a programming error:

When you detect a collision, then you should not fully reverse the last step by putting the object back to the original position. Instead you should move the object to the closest position, where no collision occurs.

Here an example: The object is at position x=1. The wall at position x=2. Then in one timestep you move the object to position x=2.5. Which makes a collision. Your code puts the object back to x=1, which makes a gap again. Instead it should be moved to x=2.

Re: Collision detection response issue

Posted: Mon Jan 13, 2014 10:15 am
by Gorus
micha wrote:If the objects stop with a short distance before the wall, then this is probably because of a programming error:

When you detect a collision, then you should not fully reverse the last step by putting the object back to the original position. Instead you should move the object to the closest position, where no collision occurs.

Here an example: The object is at position x=1. The wall at position x=2. Then in one timestep you move the object to position x=2.5. Which makes a collision. Your code puts the object back to x=1, which makes a gap again. Instead it should be moved to x=2.
I don't change the position of the object, I make the velocity = 0 when object is near a wall. I do see your point though, and I think I have some ideas on how to fix all of that. Probably will have to rewrite the whole collision handle. Thanks for the insight.