Log In  

I'm working on a collision routine for pixel based movement (not grid movement). There's no jumping, it's a from-above perspective:

if btn(⬅️) then
 if intersect(pl.crect1,cage_crect)
 and pl.crect1.x>=cage_crect.x+1 then
  pl.x1+=1
 end
 pl.x1-=1
end
if btn(➡️) then
 if intersect(pl.crect1,cage_crect)
 and pl.crect1.x<=cage_crect.x then
  pl.x1-=1
 end  
 pl.x1+=1
end
if btn(⬆️) then
 if intersect(pl.crect1,cage_crect)
 and pl.crect1.y>=cage_crect.y+1 then
  pl.y1+=1
 end 
 pl.y1-=1
end
if btn(⬇️) then
 if intersect(pl.crect1,cage_crect)
 and pl.crect1.y<=cage_crect.y+cage_crect.h then
  pl.y1-=1
 end
 pl.y1+=1
end

This seems to work OK, but I am not sure how to adjust it so that the player can move along the object, e.g. bottom of player intersects top of object rect, and if player holds down+left the player should still move left. As it is, my code won't register anything other than the opposite direction after the initial direction that caused the collision. I had a quick search for collision examples but didn't find anything that met all my criteria (pixel based + uses collision rects, not tiles). Anyone have any pointers?

P#83164 2020-10-20 17:35 ( Edited 2020-10-20 17:37)

How is the player's collision rectangle being calculated? It seems like you're testing an intersection without actually moving the player's rectangle at all. Are you moving the player into the wall on the previous frame, then checking if the player's current position is in one on the next?

If so, you should instead check for an intersection at where the player wants to be on that frame (their "goal" position), and if there isn't any, move them there. Also, only test a single direction at a time, or else you'll technically be inside of a wall since you moved diagonally.

A quick pseudo-code example:

    function movePlayer()
        -- Read the player's inputs, and convert them to a direction vector
        local direction = readInput();

        -- Test for horizontal collisions first
        local goal = { x = player.x + direction.x, y = player.y)
        if intersection(goal, level)
            goal.x = player.x
        end

        -- Test for vertical collision (with the results from above)
        goal.y += direction.y
        if intersection(goal, level)
            goal.y = player.y
        end

        player = goal
    end

Let me know if that's not the issue, it's hard to be certain without knowing the context of the code.

P#83168 2020-10-20 19:18 ( Edited 2020-10-20 19:20)

Here's my intersection routine:

function intersect(rect1,rect2)
 if (rect1.x < rect2.x + rect2.w
 and rect1.x + rect1.w > rect2.x
 and rect1.y < rect2.y + rect2.h
 and rect1.y + rect1.h > rect2.y) then
  return true
 else
  return false
 end
end

And here's the bit I use to update the player collision rect that I forgot to include in the OP:

pl.crect1.x=pl.x1-1
pl.crect1.y=pl.y1-1
P#83169 2020-10-20 19:30 ( Edited 2020-10-20 19:32)

So, the collision box is being updated at the start to the player's current position, with a bias "skin" value of -1, correct? If so, it seems like my first post should address the issue.

But, if you want a quick fix, try doing something like

-- Set bounding box
pl.crect1.x = pl.x1 - 1
pl.crect1.y = pl.y1 - 1

-- Test for left then right
-- ...

-- Then set the rectangle to its new position
p1.crect.x=p1.x1 - 1

-- Now check for up then down.
-- ...

This should address the issue of the player getting stuck inside the wall and not being able to "slide" across.

P#83170 2020-10-20 19:58 ( Edited 2020-10-20 20:01)

Here's what I have changed it to (now includes additional screen border checks):

pl.crect1.x=pl.x1-1
pl.crect1.y=pl.y1-1

crect_tmp={
 x=pl.crect1.x,
 y=pl.crect1.y,
 w=pl.crect1.w,
 h=pl.crect1.h
}

if btn(⬅️) then
 crect_tmp.x=pl.x1-1
 if(pl.x1<1) pl.x1=1  
 if not intersect(crect_tmp,cage_crect) then
  pl.x1-=1
 end
end
if btn(➡️) then
 crect_tmp.x=pl.x1+1  
 if(pl.x1>119) pl.x1=119
 if not intersect(crect_tmp,cage_crect) then
  pl.x1+=1
 end
end

pl.crect1.x=pl.x1-1

if btn(⬆️) then
 crect_tmp.y=pl.y1-1
 if(pl.y1<8) pl.y1=8
 if not intersect(crect_tmp,cage_crect) then
  pl.y1-=1
 end
end
if btn(⬇️) then
 crect_tmp.y=pl.y1+1  
 if(pl.y1>119) pl.y1=119
 if not intersect(crect_tmp,cage_crect) then
  pl.y1+=1
 end
end

This works for when the bottom of the player collision rect intersects with the top of the object (down+left/right moves player left/right above object), however the player gets stuck on all other sides of the object - can only move back in the opposite direction.

P#83171 2020-10-20 20:35 ( Edited 2020-10-20 20:37)

Since you moved to using a temp collision box, you have to update that instead. So

-- update left/right

pl.crect1.x=pl.x1-1
crect_tmp.x = p1.crect1.x

-- update up/down

An additional note, since you're already updating the player's position at the start, you don't need to use a "goal" position, since the player's already there.

P#83175 2020-10-20 21:06

Player position isn't updated at the start, is it? First, the temp crect position is set, with player movement only happening if there is no intersection. Also, I added the update to crect_tmp between L/R and U/D sections, and intersection now works on left and top side of the object, but right and bottom sides still not working.

P#83176 2020-10-20 21:23 ( Edited 2020-10-20 21:23)

Woops, didn't see you where changing the temp box on each press, that's what I get for skimming.

I made a mistake earlier, when you do the vertical collisions, you should just take the current x value, and not negate it again. So:

-- left / right code
pl.crect1.x=pl.x1
-- up / down code

This should fix one of the other directions to slide correctly, but let me know.

P#83180 2020-10-21 00:44

Thanks, it's now working. Here's the full code in case anyone else needs it in future:

crect_tmp={
 x=pl.crect1.x,
 y=pl.crect1.y,
 w=pl.crect1.w,
 h=pl.crect1.h
}

if btn(⬅️) then
 crect_tmp.x=pl.x1-1
 pl.draw_offset=1
 if(pl.x1<1) pl.x1=1
 if not intersect(crect_tmp,cage_crect) then
  pl.x1-=1
 end
end
if btn(➡️) then
 crect_tmp.x=pl.x1+1
 pl.flp=true
 pl.draw_offset=0
 if(pl.x1>118) pl.x1=118
 if not intersect(crect_tmp,cage_crect) then
  pl.x1+=1
 end
end

pl.crect1.x=pl.x1
crect_tmp.x=pl.x1

if btn(⬆️) then
 crect_tmp.y=pl.y1-1
 if not intersect(crect_tmp,cage_crect) then
  pl.y1-=1
 end
end

if btn(⬇️) then
 crect_tmp.y=pl.y1+1
 if not intersect(crect_tmp,cage_crect) then
  pl.y1+=1
 end
end

pl.crect1.y=pl.y1

A couple of things to watch out for:

  1. The intersect function (in my second post in this thread) doesn't account for lines intersecting, the boxes have to intersect beyond the lines for it to return true. You can easily work around this by making your boxes a little bigger.
  2. My player sprite doesn't fill the full width of its 8x8 block, so moving left/right meant that the hitbox didn't properly align with the bounds of the sprite. Trying to account for this by changing the position of the hitbox to align with the sprite was breaking the intersect detections. A simpler fix is to adjust the drawn position of the sprite depending on its orientation. E.g. in your draw call, add 1 to the x position if the sprite is flipped, which is what the draw_offset variable is used for in the code above.
P#83187 2020-10-21 11:56 ( Edited 2020-10-21 12:01)

[Please log in to post a comment]

Follow Lexaloffle:          
Generated 2024-04-20 02:44:27 | 0.009s | Q:18