# Chapter 3（3）：The Breakout Game

## The Breakout Game

Alright, this chapter talked a lot about helper classes and it is finally time to put them to some use. I will skip the concept phase here and basically just say that Breakout is an abbreviation of Pong for just one player to play against a wall of blocks. Breakout was initially created by Nolan Bushnell and Steve Wozniak and released by Atari in 1976. In this early version it was just a black and white game like Pong, but to make it more “exciting” transparent stripes were placed over the monitor to color the blocks (see Figure 3-13).

Figure 3-13

You will actually go a similar road by reusing some of the Pong components and using the helper classes you learned about in this chapter. Breakout is a more complex game than Pong; it can have many levels and can be improved quite a lot. For example, Arkanoid is a clone of Breakout and there were many games in the 1980s and 1990s that still used this basic game idea and added weapons, better graphic effects, and many levels with different block placements.

As you can see in Figure 3-14, the BreakoutGame class is structured in a similar way to the Pong class from the last chapter. The sprite handling is missing because it is done now with help of the SpriteHelper class. Some other internal methods and calls are also replaced by some of the helper classes. For example, StartLevel generates a random new level based on the level value, and to generate the random values you will use the RandomHelper class.

Figure 3-14

Please also note that many test methods are visible in the class. This will be improved similar to the helper classes in the next chapter, which introduces the BaseGame and TestGame classes that make handling the game class and especially unit testing a lot easier and more organized.

Take a look at Figure 3-15 for a quick overview of the Breakout game you are going to develop in the next few pages. It is quite a lot of fun and certainly has a greater replay value than Pong, which is only fun with two human players anyway. The Breakout game uses the same background texture and the two sound files from the Pong project, but you also add a new texture (BreakoutGame.png) for the paddle, ball, and blocks and you have new sounds for winning a level (BreakoutVictory.wav) and for destroying blocks (BreakoutBlockKill.wav).

Figure 3-15

### Unit Testing in Breakout

Here is a quick overview of the unit tests for the Breakout game; check the full source code for this chapter for more details. You don’t have the TestGame class yet, so you still use the same kind of unit testing you used in the last chapter. Check out the next chapter on a better way to do static unit tests. You only have three unit tests, but they were used and changed a lot as I implemented the game.

§  TestSounds - Just a quick test to check out all the new sounds for your project. Press space, Alt, Control, and Shift to play the sounds. I also added a little pause after playing the next sound to make it a little easier to hear the sounds. This test was used to check out the new XACT project I created for this game.

TestSounds——快速测试项目中的新声音文件。按下空格、AltControl和上档键来播放声音。我还在播放下一个声音之前添加了一点小停顿，更容易分辨每一个声音。这个测试用来检验为这个游戏建立的新的XACT项目。

§  TestGameSprites - This test was initially used to test the SpriteHelper class, but then all the code was moved to the Draw method of the game. The test was also used to initialize all the blocks in the game; the code was moved to the constructor, which is shown at the end of this chapter. This test shows you that it is not important to have a complicated test at the end because it is now only four lines of code, but the important part is to make your life easier while you are coding the game. Copy and paste useful parts of unit tests to your code as often as you need. Static unit tests also don’t have to be intact like dynamic unit tests for the helper classes because you only use them to build and test your game. When the game works you don’t need the static unit tests anymore except for testing parts of the game at a later point in time.

TestGameSprites——这个测试原先是用来测试SpriteHelper类的，但是后来所有代码都被移到了Draw方法中。这个测试同样用来初始化游戏中所有的砖块；代码移到了构造器里，这个将会在本章最后部分看到。这个测试要展示给你看的不是最后复杂的测试，因为现在只有短短的四行，但是重要的是让你在写代码的时候感到轻松。复制和粘贴单元测试中有用的部分到你的代码中去。静态单元测试也不需要像动态单元测试那样原封不动，因为你只是用它们来生成和测试你的游戏。当游戏运行的时候，你不再需要静态单元测试除非是测试游戏在稍后的一个测试点。

§  TestBallCollisions - Like in the previous chapter testing the ball collisions is the most useful unit test. Here you check if the collisions happen with the screen borders and paddle as expected. Only minor changes were required to get this to work. Then you can go to the more complicated block collision code, which is explained in more detail a bit later. You might even be able to think of more ways to test the collision and improve the game if you like. For example, it would make sense to trap the ball behind the wall of blocks and see if it destroys all the blocks correctly.

TestBallCollisions——就像前面那样，测试球的碰撞是最有用的单元测试。现在检验和屏幕边缘还有挡板的碰撞是否是期望的那样。只需要改动一点点代码。然后你就可以编写更复杂的与砖块的碰撞代码，这在稍后会详细解释。你可能已经想了很多方法去测试碰撞和按你所想来改进游戏。例如，将有放到砖块后面，然后看看是否能正确销毁所有砖块。

### Breakout Levels

Because you are using many of the existing Pong ideas, you can skip the code that is similar or identical. You should focus on the new variables for now:

/**//// <summary>/// How many block columns and rows are displayed?/// 要显示多少行和多少列的砖块/// </summary>const int NumOfColumns = 14,  NumOfRows = 12;/**//// <summary>/// Current paddle positions, 0 means left, 1 means right./// 当前挡板位置，0代表左，1代表右/// </summary>float paddlePosition = 0.5f;/**//// <summary>/// Level we are in and the current score./// 当前的游戏等级和分数/// </summary>int level = 0, score = -1;/**//// <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()./// 每一个砖块的位置，在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];
First you define how many columns and blocks you can have at maximum; in the first levels you will not fill all the lines and only use 10% of the blocks. The paddle position is also a little bit easier than in Pong because you have just one player. Then you store the current level and the score, which is new. In Pong each player just had three balls and the game was over if all balls were lost. In Breakout the player starts at level 1 and works his way up until he finally loses a ball. You don’t have a high score here or any game font, so the level and score data is just updated in the title of the window.

Then all the blocks are defined; the most important array is blocks, which just tells you which block is currently used. The blocks are initialized before each level starts, whereas the blockPositions and blockBoxes are initialized only once in the constructor of the game; blockPositions is used to determine the centered position of the block for rendering and blockBoxes defines the bounding box of the block for collision testing. It is important to note that none of these lists or position values use screen coordinates. All position data is stored in the 0-1 format: 0 is left or top, and 1 is right or bottom. This way the game stays resolution-independent and makes both rendering and collision checking easier.

The levels are generated in the StartLevel method, which is called at the beginning of the game and every time you advance one level:

StartLevel方法用来生成等级，这个方法在游戏开始的时候和每次晋级的时候被调用：

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
In the first for loop you just fill the whole block array with new values depending on the level. In level 1 the level value is 0 and you will only fill 10% of the blocks. RandomHelper.GetRandomInt(10) returns 0–9, which is smaller than 1 in only 10% of the cases. In level 2 this goes up to 20% until you reach level 10 or higher, where 100% of the level is filled. The game actually has no limit; you can play as long as you want.

RandomHelper.GetRandomInt(10)返回09的整数，在10%的时候值小于1。当到第二关的时候会涨到20%直到你到达第十关或更高，这时登记的100%会被填充。这个游戏没有限制，你想玩多久就可以玩多久。

Then you clear the lower three lines for the first levels to make the first levels easier. At level 3 only two lines are removed and at level 5 just one line is removed until you reach level 7 where all the lines are used.

Unlike Pong the ball speed vector is not immediately started for a new game. The ball stays on the paddle until the user presses space or A. Then the ball bounces off the paddle to a random location and the ball goes between the wall blocks, the screen borders, and the player paddle until either all blocks are removed to win a level or the player loses by not catching the ball.

Finally the window’s title is updated to show the current level number and the score the player has reached so far. In this very simple game the player only gets one point for every block he destroys; reaching a score of 100 is really good, but as I said before, there is no limit. Try to go higher and have fun with the game.

### The Game Loop

The game loop in Pong was quite easy and contained mostly input and collision code. Breakout is a little bit more complicated because you have to handle two states of the ball. It is either still on the paddle and awaits the user to press space or you are in the game and have to check for any collisions with the screen borders, the paddle, or any of the blocks in the game.

Pong游戏的的循环过程非常简单，并包含了大多数的输入和碰撞的代码。Breakout就要稍微复杂一些因为你需要控制球的两个状态。一个是挡板方面，等待用户按下空格，另一个是检查和屏幕边界、挡板或者砖块的碰撞。

Most of the Update method looks the same way as in the previous chapter; the second player was removed and a little bit of new code was added at the bottom:

// 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} // ifelse...{  // 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");    // Game over, reset to level 0    level = 0;    StartLevel();    // Show lost message    lostGame = 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
First you check if the ball was not started yet. If not update the ball position and put it on the center of the player’s paddle. Then check if space or A was pressed and start the ball then (just randomizes the ballSpeedVector for you and bounces the ball off to the wall of blocks).

The most important method is CheckBallCollisions, which you will check out in a second. Then the ball is updated like in the Pong game and you check if the ball is lost. If the player did not catch the ball, the game is over and the player can start over at level 1.

Finally you check if all blocks were removed and the level is complete. If all blocks are killed you can play the new victory sound and start the next level. The player sees a “You Won!” message on the screen (see Draw method) and can press space to start the next level.

### Drawing Breakout

Thanks to the SpriteHelper class the Draw method of the Breakout game is short and easy:

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)
You start by rendering the background; you don’t have to clear the background because the background texture fills the complete background. To make sure everything is rendered on top of the background, you draw it immediately before rendering the rest of the game sprites.

Next you draw the paddle and the ball, which is very easy to do because of the RenderCentered helpermethod in the SpriteHelper class, which works like this (the three overloads are just for a more convenient use of this method):

public void RenderCentered(float x, float y, float scale)...{  Render(new Rectangle(    (int)(x * 1024 - scale * gfxRect.Width/2),    (int)(y * 768 - scale * gfxRect.Height/2),    (int)(scale * gfxRect.Width),    (int)(scale * gfxRect.Height)));} // RenderCentered(x, y)public void RenderCentered(float x, float y)...{  RenderCentered(x, y, 1);} // RenderCentered(x, y)public void RenderCentered(Vector2 pos)...{  RenderCentered(pos.X, pos.Y);} // RenderCentered(pos)

RenderCentered takes a Vector2 or x and y float values and rescales the positions from 0 to 1 (the format you use in your game) to 1024×768. The Draw method of SpriteHelper then rescales everything to the current screen resolution from 1024×768. It may sound complicated, but it is really easy to use.

RenderCentered设置一个Vector2或者xy值，并从01（我们游戏中使用的格式）重新缩放位置到1024×768SpriteHelperDraw方法接着把所有东西重新缩放以使用当前1024×768的分辨率。这看起来有点儿复杂，但实际上使用起来是很简单的。

Then all the blocks of the current level are rendered and again that is very easy thanks to the position you have calculated in the constructor of your game. Take a look at the code on how to initialize all block positions at the upper part of the screen:

// 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/7680);
blockBoxes[x, y]
= new BoundingBox(
pos
- blockSize/2, pos + blockSize/2);
}
// for for

The blockBoxes bounding boxes are used for the collision testing discussed in a second. The calculation of the position is also no big deal; the x coordinate goes from 0.05 to 0.95 in as many steps as you have columns (14 if I remember correctly). You can try to change the NumOfColumns constant to 20 and the field will have many more blocks.

blockBoxes范围框用于后面要讨论的碰撞测试。位置的计算同样不是什么大问题；在许多步骤里x轴从0.05变到0.95。你可以试着把NumOfColumns恒定在20，然后区域里就会有更多的砖块。

Finally a small message is rendered on the screen with a scaling factor of two in case the player has won a level or lost the game. Then you just call the Draw method of SpriteHelper to render all game elements on the screen. Check out the unit tests in the game for how the rendering of the blocks, paddle, and game messages was developed. I started with the unit tests again and then wrote the implementation.

### Collision Testing

The collision testing for the Breakout game is a little bit more complicated than just checking the paddles and the screen border in Pong. The most complicated part is to correctly bounce off the blocks the ball hits. For the complete code check the source code for this chapter.

Breakout的单元测试比起只需要检测挡板和墙壁边界的Pong来说有点儿稍微复杂。最复杂的部分就是求碰撞后的正确反弹。可以查看本章的源代码。

Like the last game you have a ball with a bounding box, screen borders, and the paddle. The blocks are new and to check for any collisions you have check all of them each frame. See Figure 3-16 for an example collision happening with a block in the game.

Figure 3-16

Take a closer look at the basic collision code with the blocks. The collision with the screen border and paddle is pretty much the same as in the Pong game and is checked with the help of the TestBallCollisions unit test. To check for collisions with the blocks you iterate through all of them and check if the bounding box of the ball hits the bounding box of the blocks. The actual game code is a little bit more complicated to check which side of the bounding box you hit and in which direction you have to bounce off, but the rest of the code and the general idea is still the same.

 // 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    ballSpeedVector = -ballSpeedVector;      // Go outta here, only handle 1 block at a time      break;    } // if} // for for if
• 本文已收录于以下专栏：

## Chapter 3 Protecting the Data（3）：创建和使用数据库角色

• DBA_Huangzj
• 2014年09月28日 12:41
• 4528

## 第三方服务器用PHP验证GameCenter GKLocalPlayer返回的签名

• Gao_sun
• 2016年06月16日 21:27
• 1233

## （3）选择元素——（3）$()方法（The$() function）

• godha
• 2013年09月15日 01:05
• 1041

## CS106A Assignment3: Breakout——浅谈自上而下的设计体验

• WSY_Stat
• 2012年08月05日 15:20
• 1690

## Unity Official Tutorial --- CREATING A BREAKOUT GAME FOR BEGINNERS

CREATING A BREAKOUT GAME FOR BEGINNERSIn this live training session we look at creating a Breakout s...
• iFuMI
• 2016年05月01日 14:58
• 294

## How To Create A Breakout Game with Box2D and Cocos2D Tutorial: Part 2/2

How To Create A Breakout Game with Box2D and Cocos2D Tutorial: Part 2/2Like this post? Follow me on ...
• waleywen
• 2011年03月10日 13:15
• 1469

## MATLAB图像处理实例讲解配套(视频+PPT+源程序)--chapter1,3

• 2018年01月11日 12:16
• 95.11MB
• 下载

## 北大青鸟 ACCP5.0 S2 JavaScript chapter3(2)

• 2011年06月02日 11:24
• 6.49MB
• 下载

## 开发基于Struts Spring Hibernate Ajax的网上信息发布平台(Struts Hibernate Spring Ajax)--Chapter3

• 2009年09月07日 09:38
• 13.72MB
• 下载

## 北大青鸟 C# S2三层结构 .NET 课件 PPT(chapter3)

• 2010年09月16日 16:26
• 11.6MB
• 下载

举报原因： 您举报文章：Chapter 3（3）：The Breakout Game 色情 政治 抄袭 广告 招聘 骂人 其他 (最多只允许输入30个字)