This content has moved - please find it at https://devblog.cyotek.com.

Although these pages remain accessible, some content may not display correctly in future as the new blog evolves.

Visit https://devblog.cyotek.com.

Boulder Dash Part 2: Collision Detection

In our previous post we introduced the Firefly and Butterfly sprites and their movement rules around a random map. Now we're going to update the project to include collision detection and a new player sprite.

A sample project showing sprites colliding with each other and moving on. The blue sprite will be destroyed if it comes in contact with any other sprite.

Refactoring AiTest

To start with though, we're going to do a little refactoring. The ButterflySprite and FireflySprite classes share pretty much the same movement code and will share exactly the same collision code, so we'll merge the common behaviour into a new abstract EnemySprite class which these will inherit from. We'll also add a protected constructor which will allow us to specify the differences between the inherited sprites and their movement rules.

protected EnemySprite(Direction preferredDirection, Direction fallbackDirection)
{
  this.PreferredDirection = preferredDirection;
  this.FallbackDirection = fallbackDirection;
}

protected Direction FallbackDirection { get; set; }

protected Direction PreferredDirection { get; set; }

With that done, we'll change FireflySprite and ButterflySprite to inherit from EnemySprite instead of Sprite, and update the constructors of these classes to call the protected constructor to supply the movement rule differences.

Finally, we'll remove the Move methods from the two sprite classes and instead let EnemySprite implement the movement code.

public ButterflySprite()
  : base(Direction.Right, Direction.Left)

public FireflySprite()
  : base(Direction.Left, Direction.Right)

Although I won't go into this here, other refactoring we did was to move the Sprites collection from MainForm into Map. I also added an IsScenery method to the Map class which returns if a tile is considered scenery, for example a piece of solid earth, or a boulder which can't currently move.

A basic load map system was also added. You can still use the "Create Random Map" to generate a mostly empty canvas for the sprites to move around in (clicking with the left button will add a new firefly, with the right a butterfly) or you can load the predefined map.

Collision Detection

Now it's time to implement the actual collision detection. We'll do this by adding a new function to the base Sprite class that will check to see if a the location of any sprite matches a given location.

Note: This implementation assumes that only one sprite can occupy a tile at any one time, which is the case in Boulder Dash.

We're also using LINQ in this function for convenience. If you haven't yet upgraded to Visual Studio 2008/2010 you'll need to replace the call with a manual loop

public bool IsCollision(Point location, out Sprite sprite)
{
  sprite = this.Map.Sprites.SingleOrDefault(s => s.Location == location);

  return sprite != null;
}

I choose to implement the function as a bool to allow it to be easily used in an if statement, but providing an out parameter to return the matching sprite (or null otherwise).

With that done, it's time to update our movement code to also perform the collision detection. The two conditions in the Move method which check if a tile is part of the scenery will be modified to call out new method.

if (!this.Map.IsScenery(tile) && !this.IsCollision(tile.Location, out collision))

With this change, sprites on the map are now aware of each other and when they bump into each other they will automatically turn away.

Collision Actions

Our example project now has collision detection in place for the enemy sprites. Being enemies of the player nothing happens when they bump into each other. If they bump into the player on the other hand...

Time to add a new sprite. The PlayerSprite will be a non functioning sprite masquerading as a player character.

class PlayerSprite : Sprite
{
  public override void Move()
  {
    // Do nothing, this sprite doesn't automatically move
  }

  public override Color Color
  {
    get { return Color.Aquamarine; }
  }
}

In our previous modification the Move method of our EnemySprite implementations we grab the sprite that we are colliding with, but we don't do anything with it. Time to change that.

We'll add a basic enum that will control what happens when a sprite hits another. For this demo, that will either be nothing, or "explode" killing both sprites.

enum CollisionAction
{
  None,
  Explode
}

We're also going to modify the base Sprite class with a new method:

public abstract CollisionAction GetCollisionAction(Sprite collidedWith);

Sprite implementations will override this method and return a CollisionAction based on the sprite they collided with.

The implementation for our new Player class is quite straightforward:

public override CollisionAction GetCollisionAction(Sprite collidedWith)
{
  return CollisionAction.Explode; // Player dies if it touches any other sprites
}

And the one for EnemySprite is almost as easy:

public override CollisionAction GetCollisionAction(Sprite collidedWith)
{
  CollisionAction result;

   if (collidedWith is PlayerSprite)
    result = CollisionAction.Explode; // Kill player
  else
    result = CollisionAction.None; // Do nothing

  return result;
}

Now we have this, we'll update the Move method of our EnemySprite to take care of the action:

// if we collided with a sprite, lets execute the action
if (collision != null && this.GetCollisionAction(collision)== CollisionAction.Explode)
{
  // kill both this sprite and the one we collided with
  this.Map.Sprites.Remove(collision);
  this.Map.Sprites.Remove(this);
}

Note that if the Player could move as well then it too would need collision detection. However, as we only have one class capable of movement we'll add the code just to that for now.

Also note that we had to adjust the original NextMove method in MainForm otherwise it would crash when looping through the sprite list and a removal occurred.

for (int i = _map.Sprites.Count; i > 0; i--)
  _map.Sprites[i - 1].Move();

Sample Project

You can download an updated version of the sample project from the link below.

Update History

  • 2010-07-07 - First published
  • 2020-11-21 - Updated formatting

Downloads

Filename Description Version Release Date
aitest2.zip
  • md5: 054e65a63a4282e39625d1c0635f87e7

Sample project for implementing collision detection in the sprites of the Boulder Dash (Boulderdash) arcade game.

07/07/2010 Download

About The Author

Gravatar

The founder of Cyotek, Richard enjoys creating new blog content for the site. Much more though, he likes to develop programs, and can often found writing reams of code. A long term gamer, he has aspirations in one day creating an epic video game. Until that time, he is mostly content with adding new bugs to WebCopy and the other Cyotek products.

Leave a Comment

While we appreciate comments from our users, please follow our posting guidelines. Have you tried the Cyotek Forums for support from Cyotek and the community?

Styling with Markdown is supported