RecentComments

Comment RSS

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

XNA Game Development (Scenes and Game Services)

by Ioannis 15. January 2010 07:30

In the previous post, we have talked about decentralizing our XNA application by taking advantage of the GameComponent and DrawableGameComponent classes. In this post, we will explore two things:

 

  • The way to decouple the GameComponents' interfaces from the main game logic by using "Game Services".
  • The way to create "Scenes". Scenes in games are those displays that occur before the game starts with th game menu, the help screen, etc etc.

 

Let's start with Game Services. In the project of the previous post (also available here) in the CrazyIvanSprite class in the constructor we have a call to the game.Services.GetService method as follows:

public CrazyIvanSprite(Game game)
    : base(game)
{
    _ivanPosition = new Vector2(400, 300);
    _game = game;
    _ivan = _game.Content.Load<Texture2D>("Ivan");
    _spriteBatch = (SpriteBatch)game.Services.GetService(typeof(SpriteBatch));
}

Note that this call retrieves the SpriteBatch object in order to use it later when rendering the sprite. Where does it find it? Well in the main game loop (GameMainLoop class), in the LoadContent method, where all the initializations take place, we find a call to Services.AddService method

protected override void LoadContent(){
    spriteBatch = new SpriteBatch(GraphicsDevice);
    Services.AddService(typeof(SpriteBatch), spriteBatch);
    ...
}

In other words, the main game loop has placed an object in the game's Services and this object was retrieved by its type by the sprite that needed it. You may be wondering at this point why is this different from passing the desired object as a method parameter or via a public accessor property to the sprite. Well, technically there is no difference. 

The thing is that by using game services the main game loop does not have to know the interfacing details of each sprite. On the contrary it places the objects that may be needed to the services pool and announces that they are there and can be retrieved by anybody that wants to use them. Moreover,any sprite that wants to make something available to anybody can add an object to the services pool.

Now you may be wondering why this functionality holds the name of "Services" when it seems that it is merely a way to make objects available to game components. This is because the explanation so far has used GameServices as a mean to pass objects between sprites. The ideal design and implementation of a game specifies that the Game Services mechanism should be used as follows:

 

  • Design your game and decide which functionality will be needed to be provided by the main game loop to your sprites.
  • Write an Interface defining the methods for this functionality (for example IGraphicsServices).
  • Write a class implementing this interface (for example myGraphicsService).

 

Having those at some point during initialization of the game instanstiate the class and provide it as a service:

protected override void LoadContent(){
    myGraphicsService GService = new myGraphicsService ();
    Services.AddService(typeof(IGraphicsServices), GService );
    ...
}

Now the term "Services" makes more sense since the game announces that there is a service in the game that provides the methods of the IGraphicsService interface. The consumer sprites of the service can now retrieve it as follows:

public ASprite(Game game)
    : base(game)
{
    ...
    IGraphicsService GService = (IGraphicsService )game.Services.GetService(typeof(IGraphicsService ));
}

Lets move now to the way we implement "Scenes" in our game. As previously mentioned, scenes are the game screens that appear during th lifecycle of the game (eg the main menu scene, the main game scene, the game over scene, the help scene etc). Also note that in XNA we do not have the GUI goodies that we are used to in WinForms or WPF meaning that we have to implement everything from scratch.

As a reminder, from the previous post we have seen that everything in the main game scene is a DrawableGameComponent. In fact, the same holds for every other scene in the game. For example a flashing text string in a typical game menu saying "Press any key to begin" is a DrawbleGameComponent having a timer that in specific intervals changes the color of the text. Also, clicking on a menu selection is not caught by any mouse handler but on the contrary the mouse position is polled during the Update phase of the main game loop and the programmer has to check to see whether the mouse pointer falls into the area where the specific menu item is.

So other scenes just like the main game scene consist also of a set of sprites that interact to each other and to the user's input. Since we can control when a DrawableComponent will be shown on screen by its Visible property, we can make the DrawableComponents that compose the game scene all visible and updatable when we are at the game scene and make all other compoents not visible and vice versa when we are at another scene.

This is a solution but there is a better one. We will follow the Main game loop's architecture by creating a class Scene as follows:

public class Scene:DrawableGameComponent
{
    public List<GameComponent> SceneComponents { get; set; }
    public Scene(Game game) : base(game){
        SceneComponents = new List<GameComponent>();
        Visible = false;
        Enabled = false;
    }
    public void Show(){
        Enabled = true;
        Visible = true;
    }
    public void Hide(){
        Enabled = false;
        Visible = false;
    }
    public void Pause(){
        Enabled = false;
        Visible = true;
    }
    public override void Update(GameTime gameTime){
        foreach (GameComponent Component in SceneComponents)
        {
            if (Component.Enabled) Component.Update(gameTime);
        }
        base.Update(gameTime);
    }
    public override void Draw(GameTime gameTime){
        foreach (GameComponent Component in SceneComponents)
        {
            if (Component is DrawableGameComponent)
            {
                if ((Component as DrawableGameComponent).Visible)
                (Component as DrawableGameComponent).Draw(gameTime);
            }
        }
        base.Draw(gameTime);
    }
}

As you can see your scene has a list of DrawableGameComponent objects and is a DrawableGameComponent itself. In its Update method the scene calls the Update methods of all the DrawableGameComponent objects and the same for its Draw method. This means that now to create a scene we just create a class inheriting from the Scene class and insert its sprites and its logic in this class. In the previous post's example everything that was included in the main game loop, can be transferred to a scene named GameScene that inherits from Scene.

In the main game loop now we do not manage the sprites but we manage which scene will be enabled and visible.

In other words Game Services along with Scenes are two additional mechanisms that allow us to further harness the complexity of a game application.

The full application of the previous post with basic scene management can be downloaded here.

Shout it

kick it on DotNetKicks.com

Tags:

XNA

XNA Game Development (Decentralize)

by Ioannis 23. December 2009 07:31

In the previous post, we have talked about drawing and moving 2D textures on the XNA window. In this post, we will explore the basic architecture that XNA uses in order to separate the game logic from the sprite logic. So prior to reading this post, it is suggested to go through the previous one.

The main code smell there, was the fact that we had to put all the game logic for moving the Ivan sprite to the Update and Draw methods of the game loop. Imagine how messed up those too methods would look if we placed more sprites on the game scene. Therefore our first tendency is to take all this logic that moves and draws the Ivan sprite and draws the background and place it in on two separate classes.

Here is the code for the CrazyIvanSprite.cs file that has the CrazyIvanSprite class.

public class CrazyIvanSprite
{
    private Game _game;
    private Texture2D _ivan;
    private Vector2 _ivanPosition;

    public CrazyIvanSprite(Game game){
        _ivanPosition = new Vector2(400, 300);
        _game = game;
    }
    public void LoadContent(){
        _ivan = _game.Content.Load<Texture2D>("Ivan");
    }
    public void UnloadContent(){
        _ivan.Dispose();
    }
    public void Update(Vector2 MouseVector){
        Vector2 IvanToMouseVector = -(_ivanPosition - MouseVector);
        IvanToMouseVector.Normalize();
        _ivanPosition += IvanToMouseVector;
    }
    public void Draw(SpriteBatch spriteBatch){
        spriteBatch.Draw(_ivan, _ivanPosition, Color.White);
    }
}

 

In other words we have recreated in the CrazyIvanSprite class the same methods that the Game loop had and placed in there the logic that moves Ivan. The same is done for the background image. The main game loop in the main class is transformed as follows:

private CrazyIvanSprite _ivan;
private BackgroundSprite _background;

protected override void LoadContent(){
    spriteBatch = new SpriteBatch(GraphicsDevice);
    _ivan = new CrazyIvanSprite(this);
    _background = new BackgroundSprite(this);
    _ivan.LoadContent();
    _background.LoadContent();
}
protected override void UnloadContent(){
    _ivan.UnloadContent();
    _background.UnloadContent();
}
protected override void Update(GameTime gameTime){
    _ivan.Update();
    base.Update(gameTime);
}
protected override void Draw(GameTime gameTime){
    GraphicsDevice.Clear(Color.CornflowerBlue);
    spriteBatch.Begin();
    _background.Draw(spriteBatch);
    _ivan.Draw(spriteBatch);
    spriteBatch.End();
    base.Draw(gameTime);
}
 

Note that the game's methods just call methods with the same same in the two classes we have just created (the project that follows this approach can be downloaded here). Now the beautiful thing is the fact that XNA has thought of this approach before we did and actually has taken it one step furher. The approach is as follows:

"If we create our sprite classes to inherit from DrawableGameComponent or GameComponent and add them in the main game loop to the Components collection of the Game class, then XNA will execute automatically our sprite's Update and Draw methods within its own Update and Draw methods"

Let's see how this works for the CrazyIvan sprite. The new sprite's class is as follows:

public class CrazyIvanSprite:DrawableGameComponent
{
    private Game _game;
    private Texture2D _ivan;
    private Vector2 _ivanPosition;
    private SpriteBatch _spriteBatch;

    public CrazyIvanSprite(Game game):base(game){
        _ivanPosition = new Vector2(400, 300);
        _game = game;
        _ivan = _game.Content.Load<Texture2D>("Ivan");
        _spriteBatch = (SpriteBatch)game.Services.GetService(typeof(SpriteBatch));
    }
    protected override void Dispose(bool disposing){
        _ivan.Dispose();
        base.Dispose(disposing);
    }
    public override void Update(GameTime gameTime){
        MouseState MState=Mouse.GetState();
        Vector2 MouseVector = new Vector2(MState.X, MState.Y);
        Vector2 IvanToMouseVector = -(_ivanPosition - MouseVector);
        IvanToMouseVector.Normalize();
        _ivanPosition += IvanToMouseVector;
        base.Update(gameTime);
    }
    public override void Draw(GameTime gameTime){
        _spriteBatch.Draw(_ivan, _ivanPosition, Color.White);
        base.Draw(gameTime);
    }
}
 

As you can see, the updated CrazyIvanSprite class now overrides the typical Update , Dispose and Draw methods of the DrawableGameComponent class. The real magic happens in the main game loop's methods:

private BackGround _back;
private CrazyIvanSprite _ivan;

protected override void LoadContent(){
    spriteBatch = new SpriteBatch(GraphicsDevice);
    Services.AddService(typeof(SpriteBatch), spriteBatch);
    _back = new BackGround(this);
    _ivan = new CrazyIvanSprite(this);
    this.Components.Add(_back);
    this.Components.Add(_ivan);
}
protected override void UnloadContent()
{}
protected override void Update(GameTime gameTime){
    base.Update(gameTime);
}
protected override void Draw(GameTime gameTime){
    GraphicsDevice.Clear(Color.CornflowerBlue);

    spriteBatch.Begin();
    base.Draw(gameTime);
    spriteBatch.End();
}

Now that the sprites inherit from DrawableGameComponent, all we have to do is just create them and add them in the Components collection of the Game class. The rest is taken care by XNA and that is why Update and Draw methods of the sprite are not explicitely called in the corresponding Game loop's methods. Those will be executed when base.Update() and base.Draw() will be called.

Therefore by inheriting from DrawableGameComponent and GameComponent classes we manage to separate the sprites' logic from the game logic allowing sprite reuse between game and the distribution of the implementation of the sprites among many developers in a team.

Two things to note here:

  • The main difference between DrawableGameComponent and GameComponent is that the first implements a Draw() and Update() method while the second only implements the Update() method for logic that does not need drawing to the window.
  • If in a specific point we want to tell our game not to call the Update() method of a sprite we can set its Enabled property to false.
  • If in a specific point we want to tell our game not to call the Draw() methods of a sprite we can set its Visible property to true

This is it for now. In the next post we will talk about Game Services and Scene management. You can download the project of the final code here. Happy coding.

 

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