Move two objects apart at collision time

Hi All,

I’ve got a pretty good handle on collision detection between my player object and the surrounding (static) environment tiles. I’ve also just coded in some additional checks to make sure that the player can walk through enemies on screen, which again seems to work fine.

The problem I have is that if the player stands still and lets an enemy walk into him, the player will remain ‘stuck’ inside until the enemy has passed.

Ideally, what I want to do is force the player out of the enemy’s collision area, but I’m a little bit stuck on what approach to use.

My collision detection routines currently just stop the player’s movement speed if a collision is about to happen. Naturally, I’m already running a per-enemy check for the player’s collisions with the enemy, and don’t want to add another loop for the enemies to do the same thing again (which would be silly, since I’m certain I could capture this overlap possibility in the initial collision detection?)

How has this problem been approached before?

Many thanks!

Well, typically, a lot of games where collisions cause damage have several effects:

  1. Your character flinches (You lose control for a moment.)
  2. A limited immunity from some set of collision types/damage.
  3. A change position, typically a knock back associated with the flinch.

Think about in Megaman, when he walks into an enemy, flinches, gets knocked back and starts flashing. While he’s flashing, he can continue on without taking certain types of damage (Typically from enemy collisions).

So, you can probably set an ‘immunity’ flag, with a count down, somewhere and while the immunity flag is up, you can move through enemies.

If you want to move your character, then you’d probably apply a knock-back velocity to him, preferably large enough that the player will then have a bit of time to react and not get hit again.

Yes, I was thinking along the same lines. Since the game is top-down, i’m thinking about the style used in Zelda: A Link to the Past. In that, you had a severe enough knock-back to allow you to escape, and was enough to prevent constant interlocking of the sprites.

I suppose I could wait until a collision is detected between the player and the mob, measure the vector between them, then push the player in the reverse direction.

I’ll give that a try, thanks!

Update!

That works a treat! Thanks for the suggestion.

In case anyone else has this problem, I’ve posted my code below to handle player-vs-enemy collisions. It will push the player away from the enemy by a set amount (the variable for this in the code is called ‘pushStrength’). The beauty of this is that I don’t need to now do any per-enemy checks either, since the pushing back means you can’t really ever be ‘intersecting’ with an enemy, even if you’re standing still.

Here’s some notes about the code:

This is the code block from the collision detection algorithm that runs every tick. There is an arraylist that stores Mob class objects - just an abstractions of an enemy definition. The code cycles through each mob in the playfield, testing its bounding rectangle with the bounding rectangle points of the player object.

I’ve restricted this so that only one collision can be solved per tick, which should help clear up redundant calculations when colliding with more than one enemy at once (unlikely).

When a collision is detected (collision = true), I simply calculate the angle of the vector between the two objects - the player and the enemy - then use this as the ‘push’ vector to force the player away from the mob by a set amount (pushStrength).

It works great!


// check collisions with mobs
        if (!collision)
        {
            for (Mob m : moblist)
            {
                if (!collision)
                {
                    // test collision
                    Rectangle mobBox = new Rectangle();
                    mobBox.x = (int)(m.actualX + Mob.collBoxMargin);
                    mobBox.y = (int)(m.actualY + Mob.collBoxMargin);
                    mobBox.width = (int)(Level.TILE_SIZE - (Mob.collBoxMargin * 2));
                    mobBox.height = (int)(Level.TILE_SIZE - (Mob.collBoxMargin * 2));

                    if (pointInside(tl, mobBox)) { canGoTL = false; collideRefX = m.tileX; collideRefY = m.tileY; collision = true; }
                    if (pointInside(tr, mobBox)) { canGoTR = false; collideRefX = m.tileX; collideRefY = m.tileY; collision = true; }
                    if (pointInside(bl, mobBox)) { canGoBL = false; collideRefX = m.tileX; collideRefY = m.tileY; collision = true; }
                    if (pointInside(br, mobBox)) { canGoBR = false; collideRefX = m.tileX; collideRefY = m.tileY; collision = true; }

                    if (collision)
                    {
                        double mcx = mobBox.x + (Level.TILE_SIZE / 2);
                        double mcy = mobBox.y + (Level.TILE_SIZE / 2);
                        
                        double ang = Math.atan2(cy - mcy, cx - mcx) * (180 / Math.PI);
                        
                        speedX = 0;
                        speedY = 0;
                        dx = 0;
                        dy = 0;
                        
                        double newPX = pushStrength * Math.cos(GameUtils.DegToRad(ang));
                        double newPY = pushStrength * Math.sin(GameUtils.DegToRad(ang));
                        
                        x += newPX;
                        y += newPY;
                        
                        actualX = level.scrollX + x;
                        actualY = level.scrollY + y;
                    }
                }
            }
        }