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?



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.



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 |



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.



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.



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.



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.



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.



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:
- 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.
- 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.
[Please log in to post a comment]