4
\$\begingroup\$

I am developing an action platformer in Python, with Pygame. That said, my question is a general one about collision resolution strategies.

I use an axis-aligned bounding box for the purposes of collision detection. The bounding box changes sizes depending on the sprite's action and the frame of the action's animation.

When I update my sprite's position, I first update the y-coordinate, check for and resolve collision in the y-direction, then do the same for the x-coordinate. When I resolve collision, I simply move the bounding rect's sides to align flush with the appropriate walls.

Because the bounding rect changes sizes throughout my animations, I am noticing buggy behavior in certain situations when the frame moves from a smaller one to a larger one. The change of frame results in the sprite intersecting a wall and his position is resolved such that he falls off the map.

Here is a diagram illustrating the problem:

enter image description here

It doesn't matter whether I resolve in the x-direction or the y-direction first. They result in reciprocal buggy behavior when the next frame is wider or taller, respectively.

My question concerns general collision resolution strategies for axis-aligned bounding boxes that change size and shape. What are the best algorithms for collision resolution that avoid this issue?

Here are some of the functions and values I have access to:

  • colliderect -- tests if two rectangles collide
  • collidepoint -- boolean that tests if a point collides with a rectangle
  • rect.topleft, rect.topright, rect.bottomleft, rect.bottomright, rect.center, rect.centerx, rect.centery -- various points on the rectangles
  • rect.left, rect.right, rect.top, rect.bottom -- values for the sides of the rects
  • various vectors
  • in short, the usual stuff

Apologies if this question is a dupe. I imagine it is a pretty common one, though I couldn't find anything that helped me specifically.

\$\endgroup\$
2
  • \$\begingroup\$ In the specific case you have outlined above, I think the easiest solution is to not detect or resolve in the y-direction for vertical walls, or the x-direction for floors/ceilings \$\endgroup\$ Commented Oct 9, 2017 at 5:04
  • \$\begingroup\$ @RichardByron, interesting suggestion. Unfortunately, this would require typing the walls in some kind of way, wouldn't it? The walls are currently untyped and a single wall might function as both a horizontal and a vertical wall. \$\endgroup\$
    – DyingIsFun
    Commented Oct 9, 2017 at 13:01

2 Answers 2

1
+100
\$\begingroup\$

In brief there are two ways of doing this. Both are relatively fast and simple to run.

Method 1
If the AABB (axis aligned bounding box) changes every frame because of animations, then you can simply wait until after the sprite has been moved and the next frame of the animation has played before resolving the collisions

def MainLoop():
  #Update the position
  rect.center = rect.center + velocity

  #I don't know how you are doing your animations, but play the next frame here
  Animate.PlayNextFrame()

  #Now you want to check for collisions as normal now that everything has moved in the frame we can check for collisions once
  CheckCollisions()

This means that the collision detection occurs only once animation and the movement has occurred. This prevents the need to do collision detection more than once per frame and takes the animation into account.

Method 2
Make a dummy aabb for collision detection that follows the main character which has a fixed size. This will occasionally result in some minor intersection between the character and the wall but in most cases it looks fine.
Sprite intersection isn't a big deal
This is also relatively easy to implement and very fast to run.

\$\endgroup\$
1
\$\begingroup\$

You probably don't want to change the sizes of your bounding box.

Even if it works correctly, you'll an issue where players will "move" without player input:

Bounding box change issues

This makes the controls feel slippery and inaccurate .

While you can change bounding boxes safely in some cases, for the average case you'll want to keep the bounding box the same size. Usually, small amounts of sprite overlapping looks fine, as long as it's on the Y axis (See Legend of Zelda the Minish Cap). If you're still intent on changing the size of the box, I would recommend a second check to see if your new animation frame also doesn't collide with any walls.

The pseudo code would look something like this:

def CollisionPreCheck(rect,wall):
  theta = 0.001
  if(collidePoint(rect.centerx + (rect.width / 2), wall))
    rect.centerx = rect.centerx + (rect.width / 2) + (wall.width / 2) - theta; 
  if(collidePoint(rect.centerx - (rect.width / 2), wall))
    rect.centerx = rect.centerx - (rect.width / 2) - (wall.width / 2) + theta; 
  if(collidePoint(rect.centerY + (rect.height / 2), wall))
    rect.centerx = rect.centerY + (rect.height / 2) + (wall.height / 2) - theta; 
  if(collidePoint(rect.centerY - (rect.height / 2), wall))
    rect.centerx = rect.centerY - (rect.height / 2) - (wall.height / 2) + theta;

What we're doing here is a second collision resolution phase. If any of the points are colliding, I just bump the center point back, so that it looks like you're still touching the wall. I'm assuming that no point will have changes too drastically from the last time we've checked, and that your geometry won't be too complex. A major way that this can break is if the wall is too thin, which will cause the rect to teleport to the other side of the wall.

However, what I recommend, is to split the idea of a character into physics and visual parts.

The visual part will simply teleport to the location of the physics object every single frame, after you've handled movement. This lets you play with other cool visual effects - see juice it or lose it [ https://www.youtube.com/watch?v=Fy0aCDmgnxg ] for an idea of what I mean. Many of the techniques they describe are far easier once the gameplay and visuals are separate.

When using this technique, you are accepting a small amount of sprite overlap. In my experience players usually don't notice. If they do you'll want to either make the animation less dramatic, or use the method I outlined above.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .