0

My collision detection works by getting intersection between rectangles and reversing the effect. This is happening during each frame. It works great except in the case where the player is sitting on top of a corner and jumps. every once in a while the vertical intersection is greater than the horizontal intersection which makes my player slide down the side of the platform. Any suggestions?

enter image description here

    -- detect initial collision
    if mathy.hasCollided(player, platform) then

        local playerBoundaries = player:boundaries()

        -- list of intersections between platform and player
        local bottomBoundary = mathy.bottomBoundary( playerBoundaries, platform )
        local topBoundary = mathy.topBoundary( playerBoundaries, platform )
        local rightBoundary = mathy.rightBoundary( playerBoundaries, platform )
        local leftBoundary = mathy.leftBoundary( playerBoundaries, platform )

        local smallestDist = ""
        local smallestBoundary
        local boundaries = {
            bottom = bottomBoundary,
            top = topBoundary,
            right = rightBoundary,
            left = leftBoundary
        }

        -- get the smallest intersection (thats the side we're probably closest to)
        for direction, boundary in pairs(boundaries) do
            if not smallestBoundary then
                smallestBoundary = boundary
                smallestDist = direction
            end

            if smallestBoundary > boundary then
                smallestBoundary = boundary
                smallestDist = direction
            end
        end

        -- reverse effects depending on collision location
        if smallestDist == "bottom" then
            player.desiredPos:add(diffX, -bottomBoundary)
            player.velocity.y = 0
            player.onGround = true
        elseif smallestDist == "top" then
            player.velocity.y = 250
            player.desiredPos:add(0, topBoundary)
        elseif smallestDist == "right" then
            player.desiredPos:add(-rightBoundary, 0)
        elseif smallestDist == "left" then
            player.desiredPos:add(leftBoundary, 0)
        end
    end
hamobi
  • 7,940
  • 4
  • 35
  • 64
  • Either upgrade to swept AABB collisions, or decrease your maximum velocity/gravity so that you're less likely to clip through the geometry. – Colonel Thirty Two Dec 15 '15 at 18:09

1 Answers1

0

Its hard to tell from the short clip, but I think the issue is a result of checking for the smallest intersection instead of checking for the intersection in the direction of the object's velocity. You could try something like this in place of the smallestBoundary loop:

local boundary = nil
if (player.velocity.y > 0) then
  boundary = topBoundary
elseif (player.velocity.y < 0) then
  boundary = bottomBoundary
elseif (player.velocity.x > 0) then
  boundary = rightBoundary
elseif (player.velocity.x < 0) then
  boundary = leftBoundary
end

Of course, this isn't as robust as it can be. You might also try to combine the two approaches and do something like this in place of the smallestBoundary loop:

    local yMod = math.abs(player.velocity.y)
    local xMod = math.abs(player.velocity.x)
    local topMod = player.velocity.y > 0 and yMod or 1
    local bottomMod = player.velocity.y < 0 and yMod or 1
    local rightMod = player.velocity.x > 0 and xMod or 1
    local leftMod = player.velocity.x < 0 and xMod or 1
    local boundaries = {
        bottom = (bottomMod / MAX_VELOCITY) * bottomBoundary,
        top = (topMod / MAX_VELOCITY) * topBoundary,
        right = (rightMod / MAX_VELOCITY) * rightBoundary,
        left = (leftMod / MAX_VELOCITY) * leftBoundary
    }

    for direction, boundary in pairs(boundaries) do
        if not smallestBoundary then
            smallestBoundary = boundary
            smallestDist = direction
        end

        if smallestBoundary > boundary then
            smallestBoundary = boundary
            smallestDist = direction
        end
    end

This will do exactly what you're doing now, but will adjust the size of the boundary (in context of the comparison only) by the velocity of the player in that direction. So if you're moving in x with -5 and in y with -10, downward collisions in the y plane will have twice the weight of leftward collisions in the x plane. Of course, there's always the other option of adjusting the player in every plane of collision:

    if bottomBoundary > 0 then
        player.desiredPos:add(diffX, -bottomBoundary)
        player.velocity.y = 0
        player.onGround = true
    end
    if topBoundary > 0 then
        player.velocity.y = 250
        player.desiredPos:add(0, topBoundary)
    end
    if rightBoundary > 0 then
        player.desiredPos:add(-rightBoundary, 0)
    end
    if leftBoundary > 0 then
        player.desiredPos:add(leftBoundary, 0)
    end

This last method would make the most sense, except you don't seem to handle collisions in all directions uniformly, for whatever reason, so it might not fit your architecture.

Keep in mind that I'm not familiar with the framework you're using, so this code might not work out of the box. Also, this post assumes that +y is up, -y is down, +x is right, and -x is left.

Nick Cano
  • 437
  • 2
  • 7
  • thanks for the code. i was trying to do something with velocity .. but couldnt quite figure out how to use it in conjunction with my boundaries. i'll take a look at this and try it out later. – hamobi Dec 15 '15 at 22:31
  • wouldnt topMod etc return a boolean? maybe i'm reading it wrong? are you writing that in the form of a ternary operator? – hamobi Dec 15 '15 at 22:37
  • `local topMod = player.velocity.y > 0 and yMod or 1` says `local topMod` `if player.velocity.y > 0 then` `topMod = yMod` `else` `topMod = 1` `end`. It's an inline if statement for assignment. – Nick Cano Dec 15 '15 at 22:38
  • okay got it.. i will try this out later and see how it goes. otherwise i gotta go with colonel's suggestion and implement swept =/ – hamobi Dec 15 '15 at 22:40