Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using HitArea Class for Hit Testing in XNA, WPXNA (6)

0.00/5 (No votes)
4 Jun 2013 1  
How to use HitArea class for hit testing in XNA, WPXNA

The original post can be found here.

Introduction/Catalog

I have developed some games on Windows Phone. Here, I'll share my experiences and gradually upload some classes, no good name, I just call it WPXNA. (Some example code may not be stringent enough.)

  • Hit Testing
  • Single Rectangle Testing
  • Multiple Rectangle Testing
  • An Example

Hit Testing

In the game, hit testing is very important, and this may affect the efficiency of the game, of course, some games may not need hit testing. And I created a simple class HitArea.

HitArea contains three main methods:

protected abstract bool containTesting ( int x, int y );

internal bool ContainTest ( Vector2 location )
{
 return this.ContainTest ( ( int ) location.X, ( int ) location.Y );
}
internal bool ContainTest ( float x, float y )
{ return this.ContainTest ( ( int ) x, ( int ) y ); }
internal bool ContainTest ( int x, int y )
{

 if ( !this.IsEnabled )
  return false;

 return this.containTesting ( x, y );
}

protected abstract bool hitTesting ( Rectangle rectangle );

internal bool HitTest ( Rectangle rectangle )
{

 if ( !this.IsEnabled )
  return false;

 return this.hitTesting ( rectangle );
}

protected abstract bool hitTesting ( HitArea area );

internal bool HitTest ( HitArea area )
{

 if ( !this.IsEnabled )
  return false;

 return this.hitTesting ( area );
}

Method containTesting is used to test whether the point is contained in the area, the x and y of the point must be int. Method hitTesting is used to test whether the area is hit with a rectangle or another area. HitArea class does not implement these methods, but they are rather implemented by the derived class.

In the process of testing, we judged field IsEnabled of HitArea class. In the default case, IsEnabled is true.

internal bool IsEnabled;

protected HitArea ( )
 : this (
 true
 )
{ }
protected HitArea ( bool isEnabled )
{ this.IsEnabled = isEnabled; }

All classes that derive from HitArea also need to implement the Locate method, which is used to move the area.

internal abstract void Locate ( Point position );

Single Rectangle Testing

I defined the SingleRectangleHitArea class to do the testing of a single rectangle.

protected readonly Point location;
protected Rectangle rectangle;

internal Rectangle Rectangle
{
 get { return this.rectangle; }
}

internal SingleRectangleHitArea ( Rectangle rectangle )
 : this ( rectangle,
 true
 )
{ }
internal SingleRectangleHitArea ( Rectangle rectangle, bool isEnabled )
 : base ( isEnabled )
{
 this.rectangle = rectangle;
 this.location = rectangle.Location;
}

Field location is the original registered location of the area, property Rectangle represents the rectangle to test.

By implementing the containTesting method of SingleRectangleHitArea class, you can test the area contains a point. And the method hitTesting, SingleRectangleHitArea class allows you to test whether an area hit a rectangle or another area.

protected override bool containTesting ( int x, int y )
{ return this.rectangle.Contains ( x, y ); }

protected override bool hitTesting ( Rectangle rectangle )
{ return this.rectangle.Intersects ( rectangle ); }

protected override bool hitTesting ( HitArea area )
{ return area.HitTest ( this.rectangle ); }

internal override void Locate ( Point location )
{ this.rectangle.Location = new Point 
( this.location.X + location.X, this.location.Y + location.Y ); }

Multiple Rectangle Testing

Single rectangles can be used for smaller spirits, you may need to use multiple rectangles in order to test large spirits.

private readonly List<Point> subLocations = new List<Point> ( );
private readonly List<Rectangle> subRectangles = new List<Rectangle> ( );

internal MultiRectangleHitArea ( Rectangle rectangle, params Rectangle[] subRectangles )
 : this ( rectangle,
 true,
 subRectangles
 )
{ }
internal MultiRectangleHitArea 
( Rectangle rectangle, bool isEnabled, params Rectangle[] subRectangles )
 : base ( rectangle, isEnabled )
{
 this.subRectangles.AddRange ( subRectangles );

 foreach ( Rectangle subRectangle in subRectangles )
  this.subLocations.Add ( subRectangle.Location );

}

MultiRectangleHitArea class inherits from the SingleRectangleHitArea class, except the rectangle in SingleRectangleHitArea also includes some small rectangles that are included in the rectangle of SingleRectangleHitArea.

protected override bool containTesting ( int x, int y )
{

 if ( !base.containTesting ( x, y ) )
  return false;

 foreach ( Rectangle subRectangle in this.subRectangles )
  if ( subRectangle.Contains ( x, y ) )
   return true;

 return false;
}

protected override bool hitTesting ( Rectangle rectangle )
{

 if ( !base.hitTesting ( rectangle ) )
  return false;

 foreach ( Rectangle subRectangle in this.subRectangles )
  if ( subRectangle.Intersects ( rectangle ) )
   return true;

 return false;
}

protected override bool hitTesting ( HitArea area )
{
 
 if ( !base.hitTesting ( area ) )
  return false;

 foreach ( Rectangle subRectangle in this.subRectangles )
  if ( area.HitTest ( subRectangle ) )
   return true;

 return false;
}

An Example

First, we defined two SingleRectangleHitArea and initialized them in the constructor.

private readonly SingleRectangleHitArea hitArea1;
private readonly SingleRectangleHitArea hitArea2;
private int step = 1;

public World ( Color backgroundColor )
 : base ( )
{
 // ...
 
 this.hitArea1 = new SingleRectangleHitArea ( new Rectangle ( 0, 0, 100, 100 ) );
 this.hitArea2 = new SingleRectangleHitArea ( new Rectangle ( 200, 200, 100, 100 ) );
}

Position of the field hitArea1 is (0, 0), size of 100. Position of the field hitArea2 is (200, 200), size of 100. So they don't collide.

private void OnUpdate ( object sender, GameTimerEventArgs e )
{

 this.step++;

 if ( this.step == 10 )
  Debug.WriteLine ( "Hit? {0}", this.hitArea1.HitTest ( this.hitArea2 ) );
 else if ( this.step == 20 )
 {
  this.hitArea1.Locate ( new Point ( 150, 150 ) );
  Debug.WriteLine ( "Hit? {0}", this.hitArea1.HitTest ( this.hitArea2 ) );
 }
 else if ( this.step == 30 )
  Debug.WriteLine ( "Hit? {0}", this.hitArea1.ContainTest ( 200, 200 ) );
  
}

In the above code, we moved the location of hitArea1, then hitArea1 and hitArea2 will collide.

Get the code from here, for more contents, please visit WPXNA.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here