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.