vrld / HC

General purpose collision detection library for the use with LÖVE.
http://hc.readthedocs.org/
404 stars 48 forks source link

Collision issues with adjacent rectangles #41

Closed sponge closed 8 years ago

sponge commented 8 years ago

Having a problem with collision detection of rectangles that are adjacent to each other. Not sure if's something I'm doing wrong but it seems when a shape collides with the seam of two other shapes, sometimes the moving shape will snap to the edge of one of the rectangles even though it should be falling straight down. I'll also sometimes see bouncing on falls, even though it should just be snapping right to the shape. Finally, although I'm not seeing it in my isolated example, standing on a seam seems to bounce up and down.

I've got an isolated example that seems to show most of the issues I'm having.

if arg and arg[#arg] == "-debug" then require("mobdebug").start() end

local HC = require 'hc'

local collider = nil
local shapes = {}
local player = nil

local dx, dy = 0, 0

function love.load()
  collider = HC.new(100)

  for x = 0, 256, 16 do
    shapes[#shapes+1] = collider:rectangle(x, 192, 16, 16)
  end

  for y = 0, 160, 16 do
    shapes[#shapes+1] = collider:rectangle(256, y, 16, 16)
  end

  player = collider:rectangle(0, 0, 14, 24)
end

function love.update(dt)

  if love.keyboard.isDown('right') then
    dx = dx + 2 * dt
  elseif love.keyboard.isDown('left') then
    dx = dx - 2 * dt
  elseif dx ~= 0 then
    dx = dx - (1*dt) * (dx<0 and -1 or 1)
    if dx > -1 and dx < 1 then dx = 0 end
  end

  if love.keyboard.isDown('z') then 
    dy = -2
  end

  dy = dy + dt*10

  player:move(dx, 0)
  for shape, delta in pairs(collider:collisions(player)) do
    player:move(delta.x, delta.y)

    if delta.x ~= 0 then
      dx = 0
    end
  end

  player:move(0, dy)
  for shape, delta in pairs(collider:collisions(player)) do
    player:move(delta.x, delta.y)

    if delta.y ~= 0 then
      dy = 0
    end
  end
end

function love.draw()
    love.graphics.scale(3)
    love.graphics.print("Hello World", 400, 300)
    collider.hash:draw('line')

    for i, v in pairs(collider.hash:shapes()) do
      v:draw('line')
    end

    player:draw('fill')
end
vrld commented 8 years ago

Sounds like a problem with collision resolution. From what I can tell, the issue might be that you resolve collisions that are no longer happening because a previous collision was resolved. Does the issue go away if you use HC.neighbors instead?

for shape in pairs(collider:neighbors(player)) do
    local collides, dx, dy = player:collidesWith(shape)
    if collides then
        player:move(dx, dy)
    end
end
sponge commented 8 years ago

For my example, this was the fix. It was indeed a collision response issue.

for shape in pairs(collider:neighbors(player)) do
    local collides, dx2, dy2 = player:collidesWith(shape)
    if collides then
        player:move(dx2, dy2)
        if dx2 ~= 0 then dx = 0 end
    end
end

The new API is a huge improvement, but it's probably worth noting in the docs that collisions isn't just a shortcut for neighbors/collidesWith, or perhaps collisions itself should return the vector to separate from all collisions. It definitely made sense after being pointed out. Thanks!