Ioannis Panagopoulos blog

Tutorials on HTML5, Javascript, WinRT and .NET

XNA 2D Basic Collision Detection

by Ioannis Panagopoulos

In this post we will explore some basic techniques in XNA for 2D collision detection. Our goal is to provide a solution to the two following scenarios:

 

  • Detect the collision of two sprites
  • Detect the collision of a sprite with specific areas in the scenery (eg detect when a car hits the boundaries of the road when viewed from the top)

 

A sprite in XNA is usually realized by a DrawableGameComponent class (see previous posts 1,2,3). Therefore sprite collision detection is merely a DrawableGameComponent collision detection. Therefore, those two terms in this post will be used interchangeably.

Sprite/Sprite collision detection

The first and most basic appropach is collision detection with the use of bounding shapes. The theory works as follows. For each sprite find a primitive geometric shape that encloses its texture. Primitive shapes may be squares, rectangles, triangles or circles. The following figure shows some example of bounding shapes for some sprites:

Collision detection is based on an algorithm that finds whether two gemetric shapes intersect. Usually is is easier to stick to one geometric shape for all your sprites because otherwise you will have to provide an intersection algorithm for every possible shape combination.

For every DrawableGameComponent in the scene you must now have a property (or more) that defines the bounding shape of the texture of the property. For the basic shapes of a triangle, rectangle and circle the extra properties needed are as follows. Suppose you have the following sprite with its texture and position properties:

public class CrazyIvanSprite:DrawableGameComponent
{
    private Texture2D _ivanTexture;
    public Texture2D IvanTexture
    {
        get { return _ivanTexture; }
        set { _ivanTexture = value; }
    }
    private Vector2 _ivanPosition;
    public Vector2 IvanPosition
    {
        get { return _ivanPosition; }
        set { _ivanPosition = value; }
    }
    (...)
}

The bounding rectangle of CrazyIvanSprite  is given by the upper left corner position (_ivanPosition.X,_ivanPosition.Y) and the size of the rectangle which is _ivanTexture.Width and _ivanTexture.Height as in the following figure:

The bounding circle of CrazyIvanSprite  is given by its center (_ivanPosition.X+ _ivanTexture.Width/2, _ivanPoistion.Y+_ivanTexture.Height/2)  and radius max (_ivanTexture.Height/2,_ivanTexture.Width/2) as in the following figure:

Finally the bounding triangle can be defined as follows (the triangle needs some calculations of your own which are not necessarily so clearly based on the width and height of the texture. Therefore for a fixed width and height and variable position the triangle is defined by its three points as follows:

Now based on the bounding shape that you use for your sprite you can use the corresponding method from the CollisionDetection2D.cs file.

As you may have noticed already no matter what shape you choose to perform collisiong detection, there will be some areas of the actual texture that will be wrongly interpreted as collision or non collision areas. To eliminate this problem you can:

  • Use more that one basic shape of the same kind to bound a sprite and check collision for each one. This is mainly how collision detection is performed in 3D where each sprite consists of a number of triangles and collision detection is perfrmed for each one of them.
  • Use a bounding shape that encloses the whole sprite and check collisions with it. If you find that there is a collision perform a per-pixel collision within the overlapping area (explanation follows).

Take for example the following figure.

The algorithm will initially check if there is a collision between the bounding rectangles. If there is one (as in the example above) then the algorithm should check for collisions pixel by pixel only in the intersection area (in blue shade in the figure) if a pixel from the one sprite texture overlaps with a pixel from the other texture then there is a collision detected. Of course such a collision detection could be performed for the whole sprite with no need of bounding rectangles but it would be much slower than this approach.  

The CollisionDetection2D class with the Per-Pixel collision detection can be downloaded here. The complete project that contains a demo app of all of the aforementioned collision detection methods can be downloaded here.

Sprite to Scene collision detection

So how about a scenario such as this? We load Paint.Net and draw the following road network (in gray color).

We want to put a car on the road and detect when the car is about to go out of bounds. The key here is to detect whether all pixels in the enclosing rectangle of the car fall onto a pixel colored gray which is actually a road pixel. If we detect that even one pixel falls onto another than gray color a collistion to the road bounds is detected. A simple project containing this approach can be downloaded here. The key method is as follows:

public static bool CarIsOnRoad(Vector2 PositionCar, Texture2D TextureCar, Texture2D TextureRoad)
{
    Rectangle RectangleCar = new Rectangle((int)PositionCar.X, (int)PositionCar.Y, TextureCar.Width, TextureCar.Height);

 

    Color[] TextureDataRoad = new Color[TextureRoad.Width * TextureRoad.Height];
    TextureRoad.GetData(TextureDataRoad);

 

    for (int i = RectangleCar.Top; i < RectangleCar.Bottom; i++)
        for (int j = RectangleCar.Left; j < RectangleCar.Right; j++)
            if (TextureDataRoad[i * TextureRoad.Width + j] != Color.Gray)
                return false;

 

    return true;
}

This approach has two issues. The one has to do with the fact that it does not cover rotations of the car.This will not be covered in this post. The second is that we usually want to have a much nicer road which is not limited in one color. This is solved by using two Textures for the road. One with this single colour for collision detection and another with full blown graphics for display.

This concludes a first introduction to 2D collision detection.Note that this post did not cover rotations at all in either the first or the secton collision detection scenario.

 (You may also want to check out a newer post on integrating a Physics Engine with XNA here)

Shout it

kick it on DotNetKicks.com

blog comments powered by Disqus
hire me