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:
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.
protected Bullet ( 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.
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.
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.
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
.
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
.
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.
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.