Chicken Pong – Part 2

August 30, 2011

In part 1, I animated a chicken sprite and got it bouncing around the screen. Once I had that for a basic demo, I decided to extend the project into a game of Pong.

The first change I had to make was to remove the bouncing behaviour from the left and right sides of the screen and replace it with a scoring action. Then I needed to display the score and get the chicken back into play after a point is scored.

Adding scoring was easy, I created left and right score properties and updated them instead of reversing the speed.

int leftScore = 0;
int rightScore = 0;

Then I rendered the score in Draw() using SpriteBatch.DrawString().

SpriteFont scoreFont = Content.Load<SpriteFont>("Score");

string scoreText = leftScore + " - " + rightScore;
Vector2 scorePosition = new Vector2(graphics.GraphicsDevice.Viewport.Width / 2, 50);
Vector2 fontOrigin = scoreFont.MeasureString(scoreText) / 2;   // Need to offset the score so it's properly centred
SpriteBatch.DrawString(scoreFont, scoreText, scorePosition, Color.White, 0, fontOrigin, 1.0f, SpriteEffects.None, 0.5f);

To reset the chicken’s position, I refactored the initialisation of the chicken out into it’s own method, randomising the x and y direction each time, and called that when a point was scored.

void ResetChicken()
{
   int xDirection = 1;
   if (rnd.NextDouble() <= 0.5) xDirection = -1;
   int yDirection = 1;
   if (rnd.NextDouble() <= 0.5) yDirection = -1;
   ChickenSpeed = new Vector2(speedConstantX * xDirection, speedConstantY * yDirection);
   ChickenPosition = new Vector2((graphics.GraphicsDevice.Viewport.Width / 2) - (ChickenTexture.Width / 2), (graphics.GraphicsDevice.Viewport.Height / 2) - (ChickenTexture.Height / 2));
}

The next step was to add paddles (chicken drumsticks naturally), allow the user to control them and detect collisions between a paddle and the chicken.

The paddles got position properties but didn’t need speed properties since position is directly controlled by the user. I initialised the paddle positions to 10 pixels away from the left to right edge of the screen. I had to wait till LoadContent() to initialise these positions since I needed to know the texture size to calculate the starting position.

protected override void LoadContent()
{
   LeftDrumstickTexture = Content.Load<Texture2D>("Drumstick");
   LeftDrumstickPosition = new Vector2(10, (graphics.GraphicsDevice.Viewport.Height / 2) - (LeftDrumstickTexture.Height / 2));

   RightDrumstickTexture = Content.Load<Texture2D>("Drumstick");
   RightDrumstickPosition = new Vector2(graphics.GraphicsDevice.Viewport.Width - RightDrumstickTexture.Width - 10, (graphics.GraphicsDevice.Viewport.Height / 2) - (RightDrumstickTexture.Height / 2));
}

Then every game cycle, I detected presses on the keys I’d defined for controls and added the paddle speed constant to the y component of that paddle’s position.

private void UpdateInput()
{
   KeyboardState kbState = Keyboard.GetState();

   if (kbState.IsKeyDown(Keys.W) && LeftDrumstickPosition.Y > 0)
      LeftDrumstickPosition.Y -= 8;
   else if (kbState.IsKeyDown(Keys.S) && LeftDrumstickPosition.Y < graphics.GraphicsDevice.Viewport.Height - LeftDrumstickTexture.Height)
      LeftDrumstickPosition.Y += 8;
   if (kbState.IsKeyDown(Keys.Up) && RightDrumstickPosition.Y > 0)
      RightDrumstickPosition.Y -= 8;
   else if (kbState.IsKeyDown(Keys.Down) && RightDrumstickPosition.Y < graphics.GraphicsDevice.Viewport.Height - RightDrumstickTexture.Height)
      RightDrumstickPosition.Y += 8;
}

To detect collisions between the chicken and paddles, I decided to use the simple xna implementation of box-bounding. Every update I drew a box around the chicken and the paddle and checked whether any of the boxes intersected. If they did, I reversed the chicken’s x direction and pushed it away from the paddle by a few pixels so a collision wouldn’t be detected again next update.

void UpdateChickenSprite(GameTime gameTime)
{
   float minX = ChickenPosition.X;
   float maxX = ChickenPosition.X + ChickenTexture.Width;
   float minY = ChickenPosition.Y;
   float maxY = ChickenPosition.Y + ChickenTexture.Height;
   BoundingBox chickenBox = new BoundingBox(new Vector3(minX, minY, 0), new Vector3(maxX, maxY, 0));

   minX = LeftDrumstickPosition.X + LeftDrumstickTexture.Width - 5;
   maxX = LeftDrumstickPosition.X + LeftDrumstickTexture.Width;
   minY = LeftDrumstickPosition.Y;
   maxY = LeftDrumstickPosition.Y + LeftDrumstickTexture.Height;
   BoundingBox leftDrumstickBox = new BoundingBox(new Vector3(minX, minY, 0), new Vector3(maxX, maxY, 0));

   minX = RightDrumstickPosition.X + RightDrumstickTexture.Width - 5;
   maxX = RightDrumstickPosition.X + RightDrumstickTexture.Width;
   minY = RightDrumstickPosition.Y;
   maxY = RightDrumstickPosition.Y + RightDrumstickTexture.Height;
   BoundingBox rightDrumstickBox = new BoundingBox(new Vector3(minX, minY, 0), new Vector3(maxX, maxY, 0));

   if (chickenBox.Intersects(leftDrumstickBox))
   {
      ChickenSpeed.X *= -1;
      ChickenPosition.X += 10;
   }
   else if (chickenBox.Intersects(rightDrumstickBox))
   {
      ChickenSpeed.X *= -1;
      ChickenPosition.X -= 10;
   }
   ...
}

After I’d finished, I felt the final movement of the chicken was too predictable so I added a chance to reverse the y direction of the chicken to mix things up a bit.

if (chickenBox.Intersects(leftDrumstickBox))
{
   ...
   if (rnd.NextDouble() <= 0.5) ChickenSpeed.Y *= -1;
}
else if (chickenBox.Intersects(rightDrumstickBox))
{
   ...
   if (rnd.NextDouble() <= 0.5) ChickenSpeed.Y *= -1;
}

The work with collision detection made me eager to move on to playing with the Farseer physics engine so I decided to leave Chicken Pong there even though there was plenty of tweaking left to do to improve it’s fun factor.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s