Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Windows-Phone-7

Use the Bullet and BulletManager Classes to Create Bullets and Attack Targets in XNA, WPXNA (15)

0.00/5 (No votes)
24 Jul 2013CPOL2 min read 8.7K  
Use the Bullet and BulletManager classes to create bullets and attack targets in XNA, WPXNA (15)

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.)

  • Bullet
  • BulletManager
  • Example

Bullet

Bullets are important in the game, almost all games have a bullet or something like bullets. Next, we'll introduce the Bullet class.

Bullet class derives from the Spirit class, here are the fields:

C#
internal readonly int Power;
private int life;

Field Power is power of bullet, field life is the bullet's HP. Every time the bullet hit its target will deduct HP, if the HP is less than or equal to 0, then the bullet will be destroyed. If the bullet initial HP is 0, it means infinite HP.

Parameters of the Bullet's constructor are similar to the Spirit class, field isMoving is set to true, which means that the bullet is in a move state from the beginning, but you also need to set the speed of the bullet.

C#
protected Bullet ( /* IPlayScene scene */ IScene scene, int type, Vector2 location, 
string movieName, float speed, int angle, HitArea hitArea, int width, int height, 
int power, int life, double destroySecond, bool isMovieRotable, bool isAreaLimited, 
bool isAreaEntered, double areaSecond )
 : base ( scene, type, location, movieName,
 null,
 speed, angle, hitArea, width, height, destroySecond, isMovieRotable, isAreaLimited, 
isAreaEntered, areaSecond )
{
 this.Power = power < 0 ? 0 : power;
 this.life = life;

 this.isMoving = true;
}

protected override void move ( )
{
 this.Location.X += this.xSpeed;
 this.Location.Y += this.ySpeed;
}

protected override Vector2 getMovieLocation ( )
{ return this.Location - this.halfSize; }

In the method move, we change the location of the bullet. In the method getMovieLocation, we set up the location of the movie.

C#
internal virtual void Attack ( IList<IAssailable> targets )
{

 if ( null == targets )
  return;

 foreach ( IAssailable target in targets )
 {
  target.Injure ( this.Power, this.type );

  if ( this.life > 0 )
  {
   this.life -= this.Power;

   if ( this.life <= 0 )
   {
    this.Destroy ( );
    break;
   }

  }

 }

}

Attack method lets the bullet attack on several targets, the target needs to implement the IAssailable interface. We will not deduct HP of the target, but their Injure method is called so that each target can complete their task.

BulletManager

BulletManager class is derived from the class SpiritManager<T>, the default order is 2000.

HitTesting event is used to let the outside world to test collision, by parameter BulletHitAreaEventArgs, BulletManager can know whether the bullet hit the target, if the bullet hits the target, the bullet's Attack method will be called.

We need to call the ToArray method of the field Spirits, because the Spirits may be changed in loop.

C#
internal event EventHandler<BulletHitAreaEventArgs> HitTesting;

internal BulletManager ( )
 : base ( 2000 )
{ }

internal override void Update ( GameTime time )
{

 if ( null == this.HitTesting )
  return;

 foreach ( Bullet bullet in this.Spirits.ToArray ( ) )
  if ( null != bullet.HitArea )
  {
   BulletHitAreaEventArgs hitAreaArg = new BulletHitAreaEventArgs ( bullet );
   this.HitTesting ( bullet, hitAreaArg );

   if ( hitAreaArg.IsHit )
    bullet.Attack ( hitAreaArg.Targets );

   hitAreaArg.Dispose ( );
  }

}

In this example, we didn't use the HitTest method which returns the bullets that hit the area parameter.

C#
internal List<Bullet> HitTest ( HitArea area, int mode )
{
 List<bullet> bullets = new List<Bullet> ( );

 foreach ( Bullet bullet in this.Spirits )
  if ( mode == 1 && bullet.Type < 100 && area.HitTest ( bullet.HitArea ) )
   bullets.Add ( bullet );
  else if ( mode == 2 && bullet.Type >= 100 && area.HitTest ( bullet.HitArea ) )
   bullets.Add ( bullet );

 return bullets;
}

Example

In the SceneT16 scene, you can click on the button to create a bullet and hit the bird.

Bird class had appeared in the previous example, we implement the IAssailable interface for it. In the Injure method, we deduct the bird's HP, if the HP is less than or equal to 0, then the field IsDied will be set to true.

C#
internal class Bird
 : Spirit, IAssailable
{
 private int life = 10;
 internal bool IsDied = false;

 internal Bird ( IScene scene, Vector2 location )
  : base ( scene, 0, location,
  "bird", null,
  4, 0,
  new SingleRectangleHitArea ( new Rectangle ( -40, -40, 80, 80 ) ),
  80,
  80,
  0,
  true,
  false,
  false,
  0
  )
 { }

 public bool Injure ( int life, int type )
 {
  this.life -= life;
  Debug.WriteLine ( "life={0}", this.life );

  if ( this.life <= 0 )
  {
   Debug.WriteLine ( "life<0" );
   this.IsDied = true;
   this.Destroy ( );
  }

  return true;
 }

 protected override Vector2 getMovieLocation ( )
 { return this.Location - this.halfSize; }

}

Class MyBullet is derived from the Bullet, we modified the updateSpeed method, which sets the xSpeed and ySpeed.

C#
internal class MyBullet
 : Bullet
{

 internal MyBullet ( IScene scene, Vector2 location, int angle )
  : base ( scene, 100, location, "mybutton", 5, angle,
  new SingleRectangleHitArea ( new Rectangle ( -5, -5, 10, 10 ) ),
  10, 10,
  2, 2,
  0,
  false,
  true,
  true,
  0
  )
 { }

 protected override void updateSpeed ( )
 {
  this.xSpeed = Calculator.Cos ( this.angle ) * this.speed;
  this.ySpeed = Calculator.Sin ( this.angle ) * this.speed;

  base.updateSpeed ( );
 }

}

In the Selected method of the button, we will call the Append method to create and add MyBullet, which has had a new bullet.

Method bulletHitTesting is used to determine whether the bullet hit a bird. Parameter e's HitArea field represents the bullet's HitArea, we set IsHit field to true, it means that the collision happened.

C#
internal sealed class SceneT16
 : CommandScene
{
 // ...

 private Bird bird;
 private BirdManager birdManager;
 private BulletManager bulletManager;
 private readonly Button goButton;

 internal SceneT16 ( )
  : base ( Vector2.Zero, GestureType.None, "background1",
  new Resource[] {
   new Resource ( "bird2.image", ResourceType.Image, @"image\bird2" ),
   new Resource ( "bullet.image", ResourceType.Image, @"image\bullet" ),
   new Resource ( "go.image", ResourceType.Image, @"image\button1" ),
  },
  new Making[] {
   new Movie ( "bird", "bird2.image", 80, 80, 5, "live",
    new MovieSequence ( "live", true, new Point ( 1, 1 ), new Point ( 2, 1 ) )
    ),
   new Movie ( "mybutton", "bullet.image", 10, 10, 0, "b",
    new MovieSequence ( "b", new Point ( 1, 1 ) )
    ),
   new Button ( "b.go", "go.image", "GO", 
   new Vector2 ( 10, 690 ), 100, 50, new Point ( 1, 1 ) ),
  }
  )
 {
  this.birdManager = new BirdManager ( );
  this.birdManager.Scene = this;

  this.bulletManager = new BulletManager ( );
  this.bulletManager.Scene = this;
  this.bulletManager.HitTesting += this.bulletHitTesting;

  this.goButton = this.makings[ "b.go" ] as Button;

  this.goButton.Selected += this.goButtonSelected;
 }

 private void bulletHitTesting ( object sender, BulletHitAreaEventArgs e )
 {

  if ( !this.bird.IsDied && e.HitArea.HitTest ( this.bird.HitArea ) )
  {
   e.IsHit = true;
   e.Targets = new IAssailable[] { this.bird };
  }

 }

 private void goButtonSelected ( object sender, ButtonEventArgs e )
 { this.bulletManager.Append ( new MyBullet ( this, new Vector2 ( 10, 10 ), 45 ) ); }

 protected override void updating ( GameTime time )
 {
  this.bulletManager.Update ( time );

  base.updating ( time );
 }

 public override void LoadContent ( )
 {
  base.LoadContent ( );

  this.bird = new Bird ( this, new Vector2 ( 200, 200 ) );
  this.birdManager.Append ( this.bird );
 }

 public override void UnloadContent ( )
 {
  this.birdManager.RemoveAll ( );
  this.bulletManager.RemoveAll ( );

  base.UnloadContent ( );
 }

 public override void Dispose ( )
 {
  this.birdManager.Dispose ( );

  this.bulletManager.HitTesting -= this.bulletHitTesting;
  this.bulletManager.Dispose ( );

  this.goButton.Selected -= this.goButtonSelected;

  base.Dispose ( );
 }

}

Get the code here. For more contents, please visit WPXNA.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)