Let’s face it, most games involve some kind of shooting. Whether they be in first person like Call of Duty, or third person like Angry Birds, projectiles are in a tons of different games. In this post, I will show you how to do that, as well as manage it properly. In one of my games, Cyber Attack, this technique is displayed. If you want to make you own game like that, by all means, feel free to use me as a guide. Let’s get started.

After you make your XNA project, the first thing you need to do is create a bullet class. This class will hold variables like the movement speed, the velocity, and the position of the projectile.

using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace MissileGame { class Bullet { private Texture2D bulletTexture; private Vector2 bulletTarget; public Vector2 bulletPosition; private Vector2 bulletVelocity; public bool isActive; private float moveSpeed; public Rectangle bulletRectangle; public Bullet() { isActive = false; } }

So far, so good. After that is done, in the same class, create the ActivateBullet() method. This method will be called when you want to use a projectile. It will tell it the position, texture, and velocity that you want to use.

public void ActivateBullet(Vector2 point, Vector2 center, Texture2D texture) { bulletTarget = point; bulletPosition = center; bulletTexture = texture; moveSpeed = 200; isActive = true; SetVelocity(); } private void SetVelocity() { bulletVelocity = -(bulletPosition - bulletTarget); bulletVelocity.Normalize(); }

In Cyber Attack, the target is wherever you touch on the screen, and the center position is the gun at the bottom of the screen. I also keep my SetVelocity() method separate, as I use it for my homing bullets. You don’t have to do that. Onto the Update() method.

public void Update(GameTime gameTime) { float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds; if (bulletPosition.Y < -30) isActive = false; if (bulletPosition.X < -30 || bulletPosition.X > 600) isActive = false; bulletPosition += (bulletVelocity * moveSpeed * elapsedTime); bulletRectangle = new Rectangle((int)bulletPosition.X, (int)bulletPosition.Y, bulletTexture.Width, bulletTexture.Height); HandleCollisions(); }

What exactly did we do here? First, we set the elapsed time. We use this time, multiplied by the move speed, to move the bullets smoothly across the screen. It also updates the rectangle, so you always have an accurate shot. As you can see, we also terminate bullets as they fly off screen. This ensures they are usable for the array we are going to make later. The next two methods are self explanatory.

public void Draw(GameTime gameTime, SpriteBatch spriteBatch) { spriteBatch.Draw(bulletTexture, bulletPosition, null, color, rotation, new Vector2(bulletTexture.Width / 2, bulletTexture.Height / 2), 1.0f, SpriteEffects.None, 0f); } public void Kill() { isActive = false; }

Great, now we are done with the bullet class. This class has a parent class, however. In Cyber Attack, it was within a level class that also had a parent gameplay class, but we aren’t doing that here. We are still making a parent class, but it will be a generic one. In this parent class, you will need a few things. The array for the bullets needs to be defined, as well as a method to spawn one.

public List bullets = new List(); protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); gunTexture = Content.Load<Texture2D>("playerTowerGun"); bulletTexture = Content.Load<Texture2D>("bulletBlaster"); gunPosition = new Vector2((480 / 2) - (gunTexture.Width / 2), 700); for (int i = 0; i < 20; i++) { bullets.Add(new Bullet()); } }

Again, this isn’t the complete class. What we are doing here is defining the array of bullets, as well as filling that array with empty bullets.

If you are developing for Windows or the Xbox, your input class will look slightly different than this. On Windows Phone, this is what it looks like.

protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); for (int i = 0; i < bullets.Count; ++i) if (bullets[i].isActive) bullets[i].Update(gameTime); HandleInput(); base.Update(gameTime); } public void HandleInput() { int touches = 0; foreach (TouchLocation location in TouchPanel.GetState()) { if (touches == 0) { touches++; if (location.Position.Y <= 600) { if (touches == 1) { Vector2 point = new Vector2(location.Position.X, location.Position.Y); FireBullet(point, new Vector2(gunPosition.X + gunTexture.Width / 2, gunPosition.Y + gunTexture.Height / 2)); } } } } }

In that block, we first define a variable to handle the touches. This ensures they won’t be able to hold all their fingers on the screen and fire bullets simultaneously. We then find where the touch location is and create a Vector2 for it. That variable is passed on to the FireBullet() method below. One thing you might have an issue with is the gunPosition and the gunTexture variables. If you don’t plan on having a gun, just pass a Vector2 that you want the bullet to start from.

Adding a bullet is rather simple. All we do is go through the array for the first bullet that !isActive and activate it.

public void FireBullet(Vector2 point, Vector2 center) { for (int i = 0; i < bullets.Count; i++) { if (!bullets[i].isActive) { bullets[i].ActivateBullet(point, center, bulletTexture); return; } } }

The return; after we activate the bullet kills the loop. It will only hit after you find an inactive bullet.

The next block of code is the UpdateBullets() and Draw() loops for your parent class.

private void UpdateBullets(GameTime gameTime) { for (int i = 0; i < bullets.Count; ++i) if (bullets[i].isActive) bullets[i].Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(); foreach (Bullet bullet in bullets) if (bullet.isActive) bullet.Draw(gameTime, spriteBatch); spriteBatch.Draw(gunTexture, gunPosition, Color.White); spriteBatch.End(); base.Draw(gameTime); }

That about sums up this tutorial. I’ve tried to keep it short and to the point with only the bare minimum. If you need further assistance, feel free to send me a message or leave a comment. You can download the source code here, otherwise you can download Cyber Attack on the Windows Phone Store to see it in action.

Logan Nowak