RecentComments

Comment RSS

XNA 2D Basic Collision Detection with Rotation (Scene2Sprite)

by Ioannis 16. February 2010 07:54

In the previous post we have worked with collision detection between roated sprites. In this post we will deal with the situtation where we want to detect whether a specific sprite that can be rotated is in a specific area in the scene (eg a car is on the road in the scene).

Suppose that you have the following scenario:

 

A car is driven by the user around the scene. When the car hits the boundaries of the road, the car should lose all of its speed. The user uses the UP/DOWN keys to acelerate/break and LEFT/RIGHT for steering.

You want to detect the situation where the car is going out of the road bounds. A way to do this is as follows:

Use a second texture of the road, one that you will use for collision detection which has a specific plain color for the road:

Then get the bounding rectangle of the rotated texture that is parallel to the window's axis:

Within this rectangle check whether there is one point that is on the car's texture but the equivalent point on the road texture is not gray.

This programmatically is achieved as follows:

To get a rotated texture alone from scene, you use an additional back-buffer where you draw the rotated car's texture and then get a snapshot of it. The AdditionalRenderTargetForCollision is initialized once in the main game loop (download the project to see how):

public static RenderTarget2D AdditionalRenderTargetForCollision { get; set; }

private static Color[] _getSingleSpriteAloneFromScene(SpriteBatch spriteBatch,Texture2D textureToDraw,
                             Vector2 Position,Vector2 Origin,float Theta,List<Vector2> RectanglePoints)
{
    spriteBatch.GraphicsDevice.SetRenderTarget(0, AdditionalRenderTargetForCollision);
    spriteBatch.GraphicsDevice.Clear(Color.Black);
    spriteBatch.Begin();
    spriteBatch.Draw(textureToDraw, Position, null, Color.White, 
                                     Theta, Origin, 1.0f, SpriteEffects.None, 0);
    spriteBatch.End();
    spriteBatch.GraphicsDevice.SetRenderTarget(0, null);
    Texture2D SceneTexture = AdditionalRenderTargetForCollision.GetTexture();
    Rectangle BoundingRectangle = _getBoundingRectangleOfRotatedRectangle(RectanglePoints);
    int PixelSize = BoundingRectangle.Width * BoundingRectangle.Height;
    Color[] TextureData = new Color[PixelSize];
    SceneTexture.GetData(0, BoundingRectangle, TextureData, 0, PixelSize);

    return TextureData;
}

The code that checks for bounds is as follows:

public static bool SpriteIsOnValidArea(Texture2D TextureScene, Texture2D TextureSprite, Vector2 PosSprite, Vector2 OrigSprite, List<Vector2> RectSprite,float ThetaSprite,SpriteBatch spriteBatch,Color OKColor)
 {
     Color[] TextureDataSprite = _getSingleSpriteAloneFromScene(spriteBatch, TextureSprite, PosSprite, OrigSprite, ThetaSprite, RectSprite);
     Color[] TextureDataScene =  new Color[TextureScene.Width*TextureScene.Height];
     TextureScene.GetData(TextureDataScene);


     Rectangle RectangleSpriteBounding = _getBoundingRectangleOfRotatedRectangle(RectSprite);
     for (int i = RectangleSpriteBounding.Top; i < RectangleSpriteBounding.Bottom; i++)
         for (int j = RectangleSpriteBounding.Left; j < RectangleSpriteBounding.Right; j++)
             if (TextureDataScene[i * TextureScene.Width + j] != OKColor && TextureDataSprite[(i-RectangleSpriteBounding.Top) *
                 RectangleSpriteBounding.Width + (j-RectangleSpriteBounding.Left)] != new Color(0,0,0,0))
                 return false;
    
     return true;
 }

 

The code initially gets the car's rotated texture and the scene's texture. It then uses a method to calculate the bounding rectangle of the rotated sprite's texture. Then, within this rectangle it tests for the sprite's pixels one by one. If it locates even one texture's point that is not on the selected color (OKColor) it returns false (the car is out of bounds).

The project that performs this kind of collision detection can be downloaded here.

kick it on DotNetKicks.com

Shout it

Tags:

XNA

XNA 2D Basic Collision Detection with Rotation (Sprite2Sprite)

by Ioannis 10. February 2010 07:26

(If you are in a hurry and do not want to know how it's done but just do it, dowload the project and extract the CollisionDetection2D.cs file to include in your project that contains the collision detection methods)

In my previous post ,we have seen several collision detection techniques that can be used in 2D games in XNA. In that post, we have left out the changes that affect our code when textures can also be rotated. In this post, I intend to fill this gap by picking up from exactly where we stopped and adding rotation to the equation.

Therefore, if you haven't read the previous post , I recommend that you go and give it a look. In this post we will cover what happens when we want to check for collision between two sprites that can be rotated.? In the next post we will work with the case where the collision needs to be checked with elements from the scene (such as a car in a road), and the sprite (eg the car) can be rotated.

Prior to considering the two aforementioned problems lets see how we can rotate a texture in XNA. Rotating is done by using a different overload of the _spriteBatch.Draw method. We want our sprite (DrawableGameComponent) to be decorated with two more properties:

private Vector2 _textureReferencePoint;
public Vector2 TextureReferencePoint
{
    get { return _textureReferencePoint; }
    set { _textureReferencePoint = value; }
}

private float _rotationAngle;
public float RotationAngle
{
    get { return _rotationAngle; }
    set { _rotationAngle = value; }
}

(...)

The first is the reference point of the texture and the second is the angle of the rotation. The reference  is the point that is used as reference when we want to put a texture to a specific position. It is expressed in texture coordinates which means that point (0,0) is the upper-left corner of the texture. In the basic draw method where we do not use the texture's reference point, XNA defaults to point (0,0). So when we say that our texture should be drawn at _position (a Vector2 object) the texture's reference point is moved to _position and the texture's pixels are drawn based on that point:

If we change the texture's reference point, then, the new reference point will be moved to _position, resulting in a different placement of the texture at _postion. So if reference is set to (texture.width/2,texture.height/2) then:

The texture's reference point plays also a central role in rotation since it is the center around which the rotation will be performed. The following figure shows two 90 degrees rotations around different reference points:

 The rotation angle is measured in rads. You can use degrees and convert them to rads using the following formula:

 

((2 * (float)Math.PI) / 360) * AngleInDegrees.

 

Having those two properties available, we can now use a different overload of the SpriteBatch.Draw method to rotate our texture as we draw it on screen (the rotation will be performed around the TextureReferencePoint and the texture's TextureReferencePoint point will be placed at _position):

 _spriteBatch.Draw(_texture, _position,null,Color.White,RotationAngle,TextureReferencePoint,1.0f,SpriteEffects.None,0f);

 

The 1.0f stands for scaling (play with it to make your texture smaller/bigger).

And now for the tricky part that concerns collisions:

 

Sprite/Sprite collision detection (extends the previous post)

The first thing to be done is to adjust our bouding shapes accordingly since now the texture's origin has changed and _position points at the central point of the texture and not its upper left corner. Then, we need to take into consideration that the texture is rotated so along with it we must rotate the bounding shape.

We use the following method that rotates a point (PointToRotate) around an arbitary origin (OriginOfRotation point):

private Vector2 _rotatePoint(Vector2 PointToRotate,Vector2 OriginOfRotation,float ThetaInRads)
{
    Vector2 RotationVector = PointToRotate - OriginOfRotation;
    Vector2 RotatedVector = new Vector2()
    {
        X = (float)(RotationVector.X * Math.Cos(ThetaInRads) - RotationVector.Y * Math.Sin(ThetaInRads)),
        Y = (float)(RotationVector.X * Math.Sin(ThetaInRads) + RotationVector.Y * Math.Cos(ThetaInRads))
    };

    return OriginOfRotation + RotatedVector;
}

Apparently in the case where we use bounding circles, we do not have to do anything apart from taking into consideration that the origin of the texture is not at (0,0) but at (texture.width/2, texture.height/2) and therefore the center of the bounding circle is just the _position vector as follows:

public Vector2 CircleCenter
{
    get { return new Vector2(_position.X,_position.Y); }
}
public int CircleRadius
{
    get { return Math.Max(_texture.Width / 2, _texture.Height / 2); }
}

But with rectangles and triangles there is the need of rotating the bounding shape along with the texture. We use the aforementioned rotation method as follows. For the triangle:

public Vector2 TrianglePoint1
{
    get { return _rotatePoint(_position - TextureReferencePoint, _position, RotationAngle); }
}
public Vector2 TriangleOffset1 { get; set; }
public Vector2 TrianglePoint2
{
    get { return _rotatePoint(_position - TextureReferencePoint + TriangleOffset1, 
                                                _position, RotationAngle); }
}
public Vector2 TriangleOffset2 { get; set; }
public Vector2 TrianglePoint3
{
    get { return _rotatePoint(_position - TextureReferencePoint + TriangleOffset2,
                                                _position, RotationAngle); }
}

And for the rectangle we keep the same properties upper left point, width and height and we also use the rotation angle and the rotation origin.

For the circle and the triangle the collision detection methods demonstrated at the previous post work perfectly. But the method used for the bounding rectangles fails since it does not support rotation. To solve this we divde the rectangle in two triangles and then we use the same method we use  for the triangles. Therefore we do not pass the upper left corners and the widths and heights but the individual points of the rectangle making a method that can detect collisions for any 4point shape. You can see this in action (see the methods _createTrianglesFromRectangle() and BoundingRectangle() from the project).

But how do we deal with Per-Pixel collision detection? First, we do a collision detection for the rotated bounding rectangles by using the previously mentioned "bounding rectangle" approach. If this detects a collision the we need to check a per-pixel collision. But to do this we need to know the overlapping area of two rotated bounding rectangles which is kinda difficult. Actually, even if we could find the overlapping area of the two rectangles it would be difficult to perform a per-pixel collision detection on this area which could be of any shape (not parallel to the axis).

So instead of that we use a bounding rectangle that encloses the rotated texture as shown in the figure (check the _getBoundingRectangleOfRotatedRectangle() method of the project):

All we want to do is get the pixels within this bounding rectagles and perform a per-pixel collision detection on them. Apparently those are not the pixels of the texture only. The plot thickens...

A solution is a follows:

  • Draw the texture on screen.
  • Get a snapshot of the screen at the bounding rectangles of the rotated textures.
  • Perform a PerPixel collision detection on those two snapshots.

But how can we take a snapshot that contains only the two textures and not the background? Well, we create an imaginary drawing surface and we draw on that surface the textures one by one. Upon drawing each one on the imaginary surface we take the desired snapshots. Then we have what we need to perform perpixel collision detection the "traditional" way (Check the methods _getSingleSpriteAloneFromScene() and PerPixelWR() from the project).

 

Shout it

kick it on DotNetKicks.com

Tags:

XNA

XNA 2D Basic Collision Detection

by Ioannis 24. January 2010 21:17

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.

 

Shout it

kick it on DotNetKicks.com

Tags:

XNA

Powered by BlogEngine.NET 1.5.0.7

Programming Blogs - BlogCatalog Blog Directory Add to Technorati Favorites

Ioannis Panagopoulos


NDepend is a tool that simplifies managing a complex .NET code base. Architects and developers can analyze code structure, specify design rules, plan massive refactoring, do effective code reviews and master evolution by comparing different versions of the code.

NDepend Tool



This blog is using BlogEngine.Net and is hosted in the hoster below. I have not experienced any problems installing BlogEngine.Net in the host and I am satisfied with the host's response times. Therefore I recommend it.


DiscountASP Add