Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm making a game, it's a terraria-like game, with 20x20 blocks, and you can place and remove those blocks. Now, I am trying to write collisions, but it isn't working as I want, the collision successfully stops the player from going through the ground, but, when I press a key like A + S, that means if I walk down and left (Noclip is on atm), my player will go into the ground, bug up, and exit the ground somewhere else in the level. I made a video of it. The red text means which buttons I am pressing.

You see, if I press A and S together, I go into the ground. Here is my collision code:

Vector2 collisionDist, normal;

private bool IsColliding(Rectangle body1, Rectangle body2)
{
    normal = Vector2.Zero;

    Vector2 body1Centre = new Vector2(body1.X + (body1.Width / 2), body1.Y + (body1.Height / 2));
    Vector2 body2Centre = new Vector2(body2.X + (body2.Width / 2), body2.Y + (body2.Height / 2));

    Vector2 distance, absDistance;

    float xMag, yMag;

    distance = body1Centre - body2Centre;

    float xAdd = ((body1.Width) + (body2.Width)) / 2.0f;
    float yAdd = ((body1.Height) + (body2.Height)) / 2.0f;

    absDistance.X = (distance.X < 0) ? -distance.X : distance.X;
    absDistance.Y = (distance.Y < 0) ? -distance.Y : distance.Y;

    if (!((absDistance.X < xAdd) && (absDistance.Y < yAdd)))
        return false;

    xMag = xAdd - absDistance.X;
    yMag = yAdd - absDistance.Y;

    if (xMag < yMag)
        normal.X = (distance.X > 0) ? xMag : -xMag;
    else
        normal.Y = (distance.Y > 0) ? yMag : -yMag;

    return true;

}

private void PlayerCollisions()
{
    foreach (Block blocks in allTiles)
    {
        collisionDist = Vector2.Zero;
        if (blocks.Texture != airTile && blocks.Texture != stoneDarkTexture && blocks.Texture != stoneDarkTextureSelected && blocks.Texture != airTileSelected && blocks.Texture != doorTexture && blocks.Texture != doorTextureSelected)
        {
            if (IsColliding(player.plyRect, blocks.tileRect))
            {
                if (normal.Length() > collisionDist.Length())
                {
                    collisionDist = normal;
                }

                player.Position.X += collisionDist.X;
                player.Position.Y += collisionDist.Y;

                break;
            }
        }
    }
}

I got PlayerCollisions() running in my Update method. As you can see it works partly, but if it runs perfectly, it would be awesome, though I have no idea how to fix this problem. Help would be greatly appreciated.

EDIT:

If I remove the break; it works partly, then it is just the thing that it spasms when it hits two or more blocks at once, like, if I touch 2/3 blocks at once, it does twice the force up. How can I make it so that it only does the force for one block, so it stays correct, and does not spasm? Thanks.

share|improve this question

2 Answers 2

I suggest you have a look at the platformer starter kit provided on MSDN, it has working collision detection code. Download and MSDN Description

Pseudocode:

Narrow down tiles to only those adjacent to the player (Excluded from the code below)

Begin loop through tiles
    Check if the current tile is impassable
    Get the bounding rectangle of the tile
    Get the depth of intersection between player and tile bounds
    If depth != (0,0)
        Resolve collision by moving the player using the depth
End loop

Set the new player position

The code looks like this (Position is the bottom centre of the player):

        // Reset flag to search for ground collision.
        isOnGround = false;

        // For each potentially colliding tile,
        for (int y = topTile; y <= bottomTile; ++y)
        {
            for (int x = leftTile; x <= rightTile; ++x)
            {
                // If this tile is collidable,
                TileCollision collision = Level.GetCollision(x, y);
                if (collision != TileCollision.Passable)
                {
                    // Determine collision depth (with direction) and magnitude.
                    Rectangle tileBounds = Level.GetBounds(x, y);
                    Vector2 depth = RectangleExtensions.GetIntersectionDepth(bounds, tileBounds);
                    if (depth != Vector2.Zero)
                    {
                        float absDepthX = Math.Abs(depth.X);
                        float absDepthY = Math.Abs(depth.Y);

                        // Resolve the collision along the shallow axis.
                        if (absDepthY < absDepthX || collision == TileCollision.Platform)
                        {
                            // If we crossed the top of a tile, we are on the ground.
                            if (previousBottom <= tileBounds.Top)
                                isOnGround = true;

                            // Ignore platforms, unless we are on the ground.
                            if (collision == TileCollision.Impassable || IsOnGround)
                            {
                                // Resolve the collision along the Y axis.
                                Position = new Vector2(Position.X, Position.Y + depth.Y);

                                // Perform further collisions with the new bounds.
                                bounds = BoundingRectangle;
                            }
                        }
                        else if (collision == TileCollision.Impassable) // Ignore platforms.
                        {
                            // Resolve the collision along the X axis.
                            Position = new Vector2(Position.X + depth.X, Position.Y);

                            // Perform further collisions with the new bounds.
                            bounds = BoundingRectangle;
                        }
                    }
                }
            }
        }

        // Save the new bounds bottom.
        previousBottom = bounds.Bottom;

This should work in a majority of cases, including moving diagonally.

share|improve this answer
    
Isn't there a simpler way to do it? Besides, it's just checking if the player is touching 2/3 tiles or 1, though I don't know how. –  Stan Nov 23 '11 at 0:07
    
This is about as simple as it gets, it's hardly complex. Some of the code I posted can be removed if you only have one sort of tile. –  Stephen Tierney Nov 23 '11 at 6:13
    
I have 8+ tiles right now, which will be alot more later. –  Stan Nov 23 '11 at 7:51
    
But do they behave differently for collisions or is it "bluetile" and "redtile"? By different types I meant whether they are solid, passable from the bottom, or non-solid. Those are the three types that my example code caters for. –  Stephen Tierney Nov 23 '11 at 8:36
    
I don't have any of these types, only blocks which you cannot pass through, and background blocks, which you can pass trough. I don't have tiles which are passable from the bottom, top or anything. –  Stan Nov 23 '11 at 9:06

If you are checking XNA Rectangles for collisions you might want to just use

if (body1.Intersects(body2) ...

Maybe there is some bug in your custom collision check.

share|improve this answer
    
Well, it seems, if I remove the break, it works partly, i'll edit op. –  Stan Nov 22 '11 at 22:08

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.