topical media & game development

talk show tell print

game-xna-intro-XnaBreakout-BreakoutGame.cs / cs



  // Project: XnaBreakout, File: BreakoutGame.cs
  // Namespace: XnaBreakout, Class: BreakoutGame
  // Path: C:\code\XnaBook\XnaBreakout, Author: Abi
  // Code lines: 16, Size of file: 298 Bytes
  // Creation date: 21.11.2006 03:56
  // Last modified: 26.11.2006 13:21
  // Generated with Commenter by abi.exDream.com
  
  #region Using directives
  using System;
  using System.Collections.Generic;
  using System.IO;
  using Microsoft.Xna.Framework;
  using Microsoft.Xna.Framework.Audio;
  using Microsoft.Xna.Framework.Graphics;
  using Microsoft.Xna.Framework.Input;
  using Microsoft.Xna.Framework.Storage;
  using Microsoft.Xna.Framework.Content;
  using XnaBreakout.Helpers;
  #endregion
  
  namespace XnaBreakout
  {
  
<summary> This is the main type for your game </summary> partial class BreakoutGame : Microsoft.Xna.Framework.Game { #region Constants <summary> Rectangles for our graphics, tested with the unit tests below. </summary> static readonly Rectangle GamePaddleRect = new Rectangle(39, 1, 93, 23), GameBallRect = new Rectangle(1, 1, 36, 36), GameBlockRect = new Rectangle(136, 1, 62, 27), GameYouWonRect = new Rectangle(2, 39, 92, 21), GameYouLostRect = new Rectangle(105, 39, 94, 21);

  
<summary> Ball speed multiplicator, this is how much screen space the ball will travel each second. </summary> const float BallSpeedMultiplicator = 0.85f;//1;//0.75f;//0.5f;1

  
<summary> How many block columns and rows are displayed? </summary> const int NumOfColumns = 14, NumOfRows = 12; #endregion

                  #region Variables
                  GraphicsDeviceManager graphics;
                  ContentManager content;
                  Texture2D backgroundTexture, gameTexture;
                  AudioEngine audioEngine;
                  WaveBank waveBank;
                  SoundBank soundBank;
  
  
<summary> Resolution of our game. </summary> int width, height;

  
<summary> Current paddle positions, 0 means left, 1 means right. </summary> float paddlePosition = 0.5f;

  
<summary> Current ball position. </summary> Vector2 ballPosition = new Vector2(0.5f, 0.95f-0.035f); <summary> Ball speed vector. </summary> Vector2 ballSpeedVector = new Vector2(0, 0);

  
<summary> Paddle, ball, block, etc. as sprite helpers. </summary> SpriteHelper paddle, ball, block, youWon, youLost, background;

  
<summary> Level we are in and the current score. Just updated in the windows title because we don't have font support yet. </summary> int level = 0, score = -1; <summary> Wait until user presses space or A to start a level. </summary> bool pressSpaceToStart = true, lostGame = false;

  
<summary> All blocks of the current play field. If they are all cleared, we advance to the next level. </summary> bool[,] blocks = new bool[NumOfColumns, NumOfRows];

  
<summary> Block positions for each block we have, initialized in Initialize(). </summary> Vector2[,] blockPositions = new Vector2[NumOfColumns, NumOfRows];

  
<summary> Bounding boxes for each of the blocks, also precalculated and checked each frame if the ball collides with one of the blocks. </summary> BoundingBox[,] blockBoxes = new BoundingBox[NumOfColumns, NumOfRows]; #endregion

                  #region Constructor
  
<summary> Create the breakout game </summary> public BreakoutGame() { graphics = new GraphicsDeviceManager(this); content = new ContentManager(Services); } // BreakoutGame()

  
<summary> Initialize our game and load all graphics. </summary> protected override void Initialize() { // Remember resolution width = graphics.GraphicsDevice.Viewport.Width; height = graphics.GraphicsDevice.Viewport.Height;

                          // Init all blocks, set positions and bounding boxes
                          for (int y = 0; y < NumOfRows; y++)
                                  for (int x = 0; x < NumOfColumns; x++)
                                  {
                                          blockPositions[x, y] = new Vector2(
                                                  0.05f + 0.9f * x / (float)(NumOfColumns - 1),
                                                  0.066f + 0.5f * y / (float)(NumOfRows - 1));
                                          Vector3 pos = new Vector3(blockPositions[x, y], 0);
                                          Vector3 blockSize = new Vector3(
                                                  GameBlockRect.X/1024.0f, GameBlockRect.Y/768, 0);
                                          blockBoxes[x, y] = new BoundingBox(
                                                  pos - blockSize/2,
                                                  pos + blockSize/2);
                                  } // for for
  
                          // Start with level 1
                          StartLevel();
  
                          base.Initialize();
                  } // Initialize()
  
  
<summary> Load all graphics content (just our background texture). Use this method to make sure a device reset event is handled correctly. </summary> <param name="loadAllContent">Load everything?</param> protected override void LoadGraphicsContent(bool loadAllContent) { if (loadAllContent) { // Load all our content backgroundTexture = content.Load<Texture2D>("SpaceBackground"); gameTexture = content.Load<Texture2D>("BreakoutGame"); audioEngine = new AudioEngine("BreakoutSound.xgs"); waveBank = new WaveBank(audioEngine, "Wave Bank.xwb"); if (waveBank != null) soundBank = new SoundBank(audioEngine, "Sound Bank.xsb");

                                  // Create all sprites
                                  paddle = new SpriteHelper(gameTexture, GamePaddleRect);
                                  ball = new SpriteHelper(gameTexture, GameBallRect);
                                  block = new SpriteHelper(gameTexture, GameBlockRect);
                                  youWon = new SpriteHelper(gameTexture, GameYouWonRect);
                                  youLost = new SpriteHelper(gameTexture, GameYouLostRect);
                                  background = new SpriteHelper(backgroundTexture, null);
                          } // if
                          base.LoadGraphicsContent(loadAllContent);
                  } // LoadGraphicsContent(loadAllContent)
  
  
<summary> Unload graphic content if the device gets lost. </summary> <param name="unloadAllContent">Unload everything?</param> protected override void UnloadGraphicsContent(bool unloadAllContent) { if (unloadAllContent == true) { content.Unload(); SpriteHelper.Dispose(); } // if

                          base.UnloadGraphicsContent(unloadAllContent);
                  } // UnloadGraphicsContent(loadAllContent)
                  #endregion
  
                  #region Start level
                  void StartLevel()
                  {
                          // Randomize levels, but make it more harder each level
                          for (int y = 0; y < NumOfRows; y++)
                                  for (int x = 0; x < NumOfColumns; x++)
                                          blocks[x, y] = RandomHelper.GetRandomInt(10) < level+1;
                          // Use the lower blocks only for later levels
                          if (level < 6)
                                  for (int x = 0; x < NumOfColumns; x++)
                                          blocks[x, NumOfRows - 1] = false;
                          if (level < 4)
                                  for (int x = 0; x < NumOfColumns; x++)
                                          blocks[x, NumOfRows - 2] = false;
                          if (level < 2)
                                  for (int x = 0; x < NumOfColumns; x++)
                                          blocks[x, NumOfRows - 3] = false;
  
                          // Halt game
                          ballSpeedVector = Vector2.Zero;
  
                          // Wait until user presses space or A to start a level.
                          pressSpaceToStart = true;
  
                          // Update title
                          Window.Title =
                                  "XnaBreakout - Level " + (level+1) + " - Score " + Math.Max(0, score);
                  } // StartLevel
                  #endregion
  
                  #region Start and stop ball
  
<summary> Start new ball at the beginning of each game and when a ball is lost. </summary> public void StartNewBall() { // Randomize direction, but always go up ballSpeedVector = new Vector2(RandomHelper.GetRandomFloat(-1, +1), -1); ballSpeedVector.Normalize(); // Make sure game is started now if (score < 0) score = 0; // If we lost the game and restarted, reset score! if (lostGame) { // Game over, reset to level 0 level = 0; score = 0; lostGame = false; StartLevel(); } // if // Clear message pressSpaceToStart = false; } // StartNewBall()

  
<summary> Stop ball for menu and when game is over. </summary> public void StopBall() { ballSpeedVector = new Vector2(0, 0); pressSpaceToStart = true; } // StopBall #endregion

                  #region Update
                  GamePadState gamePad;
                  KeyboardState keyboard;
  
<summary> Update game input. </summary> protected override void Update(GameTime gameTime) { // The time since Update was called last float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

                          // Get keyboard and gamepad states
                          keyboard = Keyboard.GetState();
                          gamePad = GamePad.GetState(PlayerIndex.One);
  
                          // Escape and back exit the game
                          if (keyboard.IsKeyDown(Keys.Escape) ||
                                  gamePad.Buttons.Back == ButtonState.Pressed)
                                  this.Exit();
  
                          // Move half way across the screen each second
                          float moveFactorPerSecond = 0.75f *
                                  (float)gameTime.ElapsedRealTime.TotalMilliseconds / 1000.0f;
  
                          // Move left and right if we press the cursor or gamepad keys.
                          if (gamePad.DPad.Right == ButtonState.Pressed ||
                                  gamePad.ThumbSticks.Left.X > 0.5f ||
                                  keyboard.IsKeyDown(Keys.Right))
                                  paddlePosition += moveFactorPerSecond;
                          if (gamePad.DPad.Left == ButtonState.Pressed ||
                                  gamePad.ThumbSticks.Left.X < -0.5f ||
                                  keyboard.IsKeyDown(Keys.Left))
                                  paddlePosition -= moveFactorPerSecond;
  
                          // Make sure paddle stay between 0 and 1 (offset 0.05f for paddle width)
                          if (paddlePosition < 0.05f)
                                  paddlePosition = 0.05f;
                          if (paddlePosition > 1 - 0.05f)
                                  paddlePosition = 1 - 0.05f;
  
                          // Game not started yet? Then put ball on paddle.
                          if (pressSpaceToStart)
                          {
                                  ballPosition = new Vector2(paddlePosition, 0.95f - 0.035f);
  
                                  // Handle space
                                  if (keyboard.IsKeyDown(Keys.Space) ||
                                          gamePad.Buttons.A == ButtonState.Pressed)
                                  {
                                          StartNewBall();
                                  } // if
                          } // if
                          else
                          {
                                  // Check collisions
                                  CheckBallCollisions(moveFactorPerSecond);
  
                                  // Update ball position and bounce off the borders
                                  ballPosition += ballSpeedVector *
                                          moveFactorPerSecond * BallSpeedMultiplicator;
                                  
                                  // Ball lost?
                                  if (ballPosition.Y > 0.985f)
                                  {
                                          // Play sound
                                          soundBank.PlayCue("PongBallLost");
                                          // Show lost message, reset is done above in StartNewBall!
                                          lostGame = true;
                                          pressSpaceToStart = true;
                                  } // if
  
                                  // Check if all blocks are killed and if we won this level
                                  bool allBlocksKilled = true;
                                  for (int y = 0; y < NumOfRows; y++)
                                          for (int x = 0; x < NumOfColumns; x++)
                                                  if (blocks[x, y])
                                                  {
                                                          allBlocksKilled = false;
                                                          break;
                                                  } // for for if
  
                                  // We won, start next level
                                  if (allBlocksKilled == true)
                                  {
                                          // Play sound
                                          soundBank.PlayCue("BreakoutVictory");
                                          lostGame = false;
                                          level++;
                                          StartLevel();
                                  } // if
                          } // else
  
                          base.Update(gameTime);
                  } // Update(gameTime)
                  #endregion
  
                  #region Check ball collisions
  
<summary> Check ball collisions </summary> void CheckBallCollisions(float moveFactorPerSecond) { // Check top, left and right screen borders float MinYPos = 0.0235f; if (ballPosition.Y < MinYPos) { ballSpeedVector.Y = -ballSpeedVector.Y; // Move ball back into screen space if (ballPosition.X < MinYPos) ballPosition.X = MinYPos; // Play hit sound soundBank.PlayCue("PongBallHit"); } // if float MinXPos = 0.018f; if (ballPosition.X < MinXPos || ballPosition.X > 1 - MinXPos) { ballSpeedVector.X = -ballSpeedVector.X; // Move ball back into screen space if (ballPosition.X < MinXPos) ballPosition.X = MinXPos; if (ballPosition.X > 1 - MinXPos) ballPosition.X = 1 - MinXPos; // Play hit sound soundBank.PlayCue("PongBallHit"); } // if

                          // Check for collisions with the paddles
                          // Construct bounding boxes to use the intersection helper method.
                          Vector2 ballSize = new Vector2(24 / 1024.0f, 24 / 768.0f);
                          BoundingBox ballBox = new BoundingBox(
                                  new Vector3(ballPosition.X - ballSize.X / 2, ballPosition.Y - ballSize.Y / 2, 0),
                                  new Vector3(ballPosition.X + ballSize.X / 2, ballPosition.Y + ballSize.Y / 2, 0));
                          Vector2 paddleSize = new Vector2(
                                  GamePaddleRect.Width / 1024.0f, GamePaddleRect.Height / 768.0f);
                          BoundingBox paddleBox = new BoundingBox(
                                  new Vector3(paddlePosition - paddleSize.X / 2, 0.95f-paddleSize.Y * 0.7f, 0),
                                  new Vector3(paddlePosition + paddleSize.X / 2, 0.95f, 0));
  
                          // Ball hit paddle?
                          if (ballBox.Intersects(paddleBox))
                          {
                                  // Bounce off in the direction vector from the paddle
                                  ballSpeedVector.X += (ballPosition.X - paddlePosition) / (MinXPos * 3);
                                  // Max to -1 and +1
                                  if (ballSpeedVector.X < -1)
                                          ballSpeedVector.X = -1;
                                  if (ballSpeedVector.X > 1)
                                          ballSpeedVector.X = 1;
                                  // Bounce of the paddle
                                  ballSpeedVector.Y = -1;// -ballSpeedVector.Y;
                                  // Move away from the paddle
                                  ballPosition.Y -= moveFactorPerSecond * BallSpeedMultiplicator;
                                  // Normalize vector
                                  ballSpeedVector.Normalize();
                                  // Play sound
                                  soundBank.PlayCue("PongBallHit");
                          } // if
  
                          // Ball hits any block?
                          for (int y = 0; y < NumOfRows; y++)
                                  for (int x = 0; x < NumOfColumns; x++)
                                          if (blocks[x, y])
                                          {
                                                  // Collision check
                                                  if (ballBox.Intersects(blockBoxes[x, y]))
                                                  {
                                                          // Kill block
                                                          blocks[x, y] = false;
                                                          // Add score
                                                          score++;
                                                          // Update title
                                                          Window.Title =
                                                                  "XnaBreakout - Level " + (level + 1) + " - Score " + score;
                                                          // Play sound
                                                          soundBank.PlayCue("BreakoutBlockKill");
                                                          // Bounce ball back, but first find out which side we hit.
                                                          // Start with left/right borders.
                                                          if (Math.Abs(blockBoxes[x, y].Max.X - ballBox.Min.X) <
                                                                  moveFactorPerSecond)
                                                          {
                                                                  ballSpeedVector.X = Math.Abs(ballSpeedVector.X);
                                                                  // Also move back a little
                                                                  ballPosition.X += (ballSpeedVector.X < 0 ? -1 : 1) *
                                                                          moveFactorPerSecond;
                                                          } // else
                                                          else if (Math.Abs(blockBoxes[x, y].Min.X - ballBox.Max.X) <
                                                                  moveFactorPerSecond)
                                                          {
                                                                  ballSpeedVector.X = -Math.Abs(ballSpeedVector.X);
                                                                  // Also move back a little
                                                                  ballPosition.X += (ballSpeedVector.X < 0 ? -1 : 1) *
                                                                          moveFactorPerSecond;
                                                          } // else
                                                          // Now check top/bottom borders
                                                          else if (Math.Abs(blockBoxes[x, y].Max.Y - ballBox.Min.Y) <
                                                                  moveFactorPerSecond)
                                                          {
                                                                  ballSpeedVector.Y = Math.Abs(ballSpeedVector.Y);
                                                                  // Also move back a little
                                                                  ballPosition.Y += (ballSpeedVector.Y < 0 ? -1 : 1) *
                                                                          moveFactorPerSecond;
                                                          } // if
                                                          else if (Math.Abs(blockBoxes[x, y].Min.Y - ballBox.Max.Y) <
                                                                  moveFactorPerSecond)
                                                          {
                                                                  ballSpeedVector.Y = -Math.Abs(ballSpeedVector.Y);
                                                                  // Also move back a little
                                                                  ballPosition.Y += (ballSpeedVector.Y < 0 ? -1 : 1) *
                                                                          moveFactorPerSecond;
                                                          } // else
                                                          else
                                                                  ballSpeedVector *= -1;
  
                                                          // Go outa here, only handle 1 block at a time
                                                          break;
                                                  } // if
                                          } // for for if
                  } // CheckBallCollisions()
                  #endregion
  
                  #region Draw
  
<summary> Draws all sprites and game components. </summary> protected override void Draw(GameTime gameTime) { // Render background background.Render(); SpriteHelper.DrawSprites(width, height);

                          // Render all game graphics                        
                          paddle.RenderCentered(paddlePosition, 0.95f);
                          ball.RenderCentered(ballPosition);
                          // Render all blocks
                          for (int y = 0; y < NumOfRows; y++)
                                  for (int x = 0; x < NumOfColumns; x++)
                                          if (blocks[x, y])
                                                  block.RenderCentered(blockPositions[x, y]);
  
                          if (pressSpaceToStart &&
                                  score >= 0)
                          {
                                  if (lostGame)
                                          youLost.RenderCentered(0.5f, 0.65f, 2);
                                  else
                                          youWon.RenderCentered(0.5f, 0.65f, 2);
                          } // if
  
                          // Draw all sprites on the screen
                          SpriteHelper.DrawSprites(width, height);
  
                          base.Draw(gameTime);
                  } // Draw(gameTime)
                  #endregion
  
                  #region Start game
                  public static void StartGame()
                  {
                          using (BreakoutGame game = new BreakoutGame())
                          {
                                  game.Run();
                          } // using
                  } // StartGame()
                  #endregion
  
                  #region Unit tests
  if DEBUG
                  #region Test delegate
  
<summary> Test delegate helper </summary> delegate void TestDelegate(); #endregion

                  #region TestPongGame class
  
<summary> Helper class to test the BreakoutGame </summary> class TestBreakoutGame : BreakoutGame { TestDelegate testLoop; public TestBreakoutGame(TestDelegate setTestLoop) { testLoop = setTestLoop; } // TestPongGame(setTestLoop)

                          protected override void Draw(GameTime gameTime)
                          {
                                  base.Draw(gameTime);
                                  testLoop();
                          } // Draw(gameTime)
                  } // class TestBreakoutGame
                  #endregion
  
                  #region StartTest
                  static TestBreakoutGame testGame;
                  static void StartTest(TestDelegate testLoop)
                  {
                          testGame = new TestBreakoutGame(testLoop);
                          testGame.Run();
                          testGame.Dispose();
                  } // RunTest(testLoop)
                  #endregion
  
                  #region TestSounds
                  public static void TestSounds()
                  {
                          StartTest(
                                  delegate
                                  {
                                          string soundToPlay = "";
                                          if (testGame.keyboard.IsKeyDown(Keys.Space))
                                                  soundToPlay = "PongBallHit";
                                          if (testGame.keyboard.IsKeyDown(Keys.LeftControl))
                                                  soundToPlay = "PongBallLost";
                                          if (testGame.keyboard.IsKeyDown(Keys.LeftAlt))
                                                  soundToPlay = "BreakoutVictory";
                                          if (testGame.keyboard.IsKeyDown(Keys.LeftShift))
                                                  soundToPlay = "BreakoutBlockKill";
  
                                          if (String.IsNullOrEmpty(soundToPlay) == false)
                                          {
                                                  testGame.soundBank.PlayCue(soundToPlay);
                                                  System.Threading.Thread.Sleep(500);
                                          } // if
                                  });
                  } // TestSounds()
                  #endregion
  
                  #region TestGameSprites
                  public static void TestGameSprites()
                  {
                          StartTest(
                                  delegate
                                  {
                                          if (testGame.keyboard.IsKeyDown(Keys.LeftControl))
                                                  testGame.youLost.RenderCentered(0.5f, 0.65f, 2);
                                          else
                                                  testGame.youWon.RenderCentered(0.5f, 0.65f, 2);
                                  });
                  } // TestGameSprites()
                  #endregion
  
                  #region TestBallCollisions
                  public static void TestBallCollisions()
                  {
                          StartTest(
                                  delegate
                                  {
                                          testGame.Window.Title = "XnaBreakout - Press 1-5 to start collision tests";
  
                                          // Start specific collision scene based on the user input.
                                          if (testGame.keyboard.IsKeyDown(Keys.D1))
                                          {
                                                  // First test, just collide with screen border
                                                  testGame.ballPosition = new Vector2(0.9f, 0.5f);
                                                  testGame.ballSpeedVector = new Vector2(1, 1);
                                                  testGame.ballSpeedVector.Normalize();
                                                  testGame.pressSpaceToStart = false;
                                          } // if
                                          else if (testGame.keyboard.IsKeyDown(Keys.D2))
                                          {
                                                  // Second test, straight on collision with paddle
                                                  testGame.ballPosition = new Vector2(0.6f, 0.8f);
                                                  testGame.ballSpeedVector = new Vector2(1, 1);
                                                  testGame.ballSpeedVector.Normalize();
                                                  testGame.paddlePosition = 0.7f;
                                                  testGame.pressSpaceToStart = false;
                                          } // if
                                          else if (testGame.keyboard.IsKeyDown(Keys.D3))
                                          {
                                                  // Advanced test to check if we hit the edge of paddle
                                                  testGame.ballPosition = new Vector2(0.9f, 0.4f);
                                                  testGame.ballSpeedVector = new Vector2(1, -0.5f);
                                                  testGame.ballSpeedVector.Normalize();
                                                  testGame.paddlePosition = 0.29f;
                                                  testGame.pressSpaceToStart = false;
                                          } // if
                                          else if (testGame.keyboard.IsKeyDown(Keys.D4))
                                          {
                                                  // Advanced test to check if we hit the edge of paddle
                                                  testGame.ballPosition = new Vector2(0.9f, 0.4f);
                                                  testGame.ballSpeedVector = new Vector2(1, -0.5f);
                                                  testGame.ballSpeedVector.Normalize();
                                                  testGame.paddlePosition = 0.42f;
                                                  testGame.pressSpaceToStart = false;
                                          } // if
                                          else if (testGame.keyboard.IsKeyDown(Keys.D4))
                                          {
                                                  // And finally test collision with blocks of current level
                                                  testGame.StartLevel();
                                                  testGame.pressSpaceToStart = false;
                                                  testGame.ballPosition = new Vector2(0.9f, 0.4f);
                                                  testGame.ballSpeedVector = new Vector2(1, -0.5f);
                                                  testGame.ballSpeedVector.Normalize();
                                          } // if
                                  });
                  } // TestBallCollisions()
                  #endregion
  endif
                  #endregion
          } // class BreakoutGame
  } // namespace XnaBreakout
  


(C) Æliens 20/2/2008

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.