In the last chapter, you learned how to implement a basic collision detection system in your Prison Break game. This collision detection system allowed you to test for any collisions between the game ball and the bricks or the paddle. It is now time to add the finishing touch to the game.
Most arcade-style games feature a scoring component. Whether it is a direct score or a ranking that correlates to how a level was finished, the score is what lets the player know how he played compared to other players.
In this chapter, you take a look at two possible ways to keep score in Prison Break. The first method adds a specific number of points to the player’s score for each brick broken. The second method awards the player a number of points for each complete row of bricks eliminated.
To keep score, you first need to add a new method to the PBGameRenderer() that will track the player’s score. This method advances the score as you call it.
How will the score be written out to the screen? You create three small vertices following the methods outlined in Chapter 4 and Chapter 5 for making the background, bricks, ball, and paddle. Because we have gone through this procedure four times already, it will not be repeated again here.
Create a new class called PBScoreTile and place instances of it in the upper right-hand corner of your game screen. Next, add to your project a new spritesheet that contains all of the numbers needed to build a score. This spritesheet is shown in Figure 7-1.
Figure 7-1. ScoreNumbers Spritesheet
Each tile should default to the 0 when drawn. This is accomplished by performing a glTranslatef() to the coordinates of 0,0,0 in the texture matrix.
gl.glMatrixMode(GL10.GL_TEXTURE);
gl.glLoadIdentity();
gl.glTranslatef(0.0f, 0.0f, 0.0f);
For each point scored, use glTranslatef() to move the spritesheet to the corresponding number. This is best achieved in a loop that continues to add 1 to the current score, move a 9 back to a 0 when needed, and advance to the next scoring tile. Therefore, advance your score to the next number each time the method is called by using glTranslatef() to move to the next 0.25 increment in the spritesheet.
The following is an example in pseudocode:
private void advanceScore(){
...
//advance glTranslatef() to the next image in the sprite sheet
gl.glMatrixMode(GL10.GL_TEXTURE);
gl.glLoadIdentity();
gl.glTranslatef(0.0f, 0.0f, 0.0f);
...
}
When you have your scoring method finished, it is time to call it.
In the next section, you learn how to call it per brick.
It is very simple to allow your player to score per brick. All you need to do is modify the detectCollisions() method to advance the score when a brick is taken out of play. Modify your detectCollisions() method (as per the following bolded code) to advance the score each time the player destroys a brick.
private void detectCollisions(){
if(ball.posY <= 0){
//GameOver
}
for (int x = 0; x < wall.rows.length; x++)
{
for(int y = 0; y < wall.rows[x].bricks.length; y++)
{
if(!wall.rows[x].bricks[y].isDestroyed)
{
if (((ball.posY > wall.rows[x].bricks[y].posY - .25f)
&& (ball.posY < wall.rows[x].bricks[y].posY)
&& (ball.posX + .25f > wall.rows[x].bricks[y].posX)
&& (ball.posX < wall.rows[x].bricks[y].posX + 1.50f)))
{
wall.rows[x].bricks[y].isDestroyed = true;
advanceScore();
PBGameVars.ballTargetY = PBGameVars. ballTargetY * -1f;
if(PBGameVars.ballTargetX == -2f){
PBGameVars.ballTargetX = 5f;
}else{
PBGameVars.ballTargetX = -2f;
}
}
}
}
}
...
}
To add a bit of variety to the scoring, you can also implement a way to give each brick a different point value. First, modify your advanceScore() method to accept an int value representing the number of points you want to advance the score counter. Then, you can simply pass the brickType of the destroyed brick as the number of points that it is worth, as shown in the following bolded code:
private void detectCollisions(){
if(ball.posY <= 0){
//GameOver
}
for (int x = 0; x < wall.rows.length; x++)
{
for(int y = 0; y < wall.rows[x].bricks.length; y++)
{
if(!wall.rows[x].bricks[y].isDestroyed)
{
if (((ball.posY > wall.rows[x].bricks[y].posY - .25f)
&& (ball.posY < wall.rows[x].bricks[y].posY)
&& (ball.posX + .25f > wall.rows[x].bricks[y].posX)
&& (ball.posX < wall.rows[x].bricks[y].posX + 1.50f)))
{
wall.rows[x].bricks[y].isDestroyed = true;
advanceScore(wall.rows[x].bricks[y]. brickType);
PBGameVars.ballTargetY = PBGameVars. ballTargetY * -1f;
if(PBGameVars.ballTargetX == -2f){
PBGameVars.ballTargetX = 5f;
}else{
PBGameVars.ballTargetX = -2f;
}
}
}
}
}
...
}
If you don’t want to score your players per brick broken, you can also increment the score per row.
When you created the PBWall class, one of the features that you built into it was the ability to specify the number of brick rows you want the user to have to break through. You could, theoretically, instantiate this to hundreds of rows and turn the game into more of an endurance game. In doing so, you could track the number of rows the player has successfully cleared.
Note While it is outside the scope of this chapter, if you wanted to create a new game that contained hundreds of brick rows, you would have to create a method that scrolled the screen when the ball moved above the view port. In pseudocode, this would mean that if ball.posY > 4, then glTranslatef(ball.posY - 2, 0.0f,0.0f) in the projection matrix. The -2 keeps the ball in the center of the screen as it scrolls.
Add a new property named numberOfBricksRemaining to your PBRow. This property will track the number of bricks in the current row. When the counter reaches zero, you can advance the score, as in the following example:
public class PBRow {
public PBBrick[] bricks;
public int numberOfBricksRemaining = 0;
public boolean rowIsScored = false;
private Random brickType = new Random();
private boolean isRowOdd = false;
private int numberOfBricks = 0;
public PBRow(int rowNumber){
if(rowNumber 2 > 0)
{
numberOfBricks = 4;
numberOfBricksRemaining = 4;
isRowOdd = true;
}
else
{
numberOfBricks = 5;
numberOfBricksRemaining = 5;
isRowOdd = false;
}
bricks = new PBBrick[numberOfBricks];
for(int x = 0; x < numberOfBricks ; x++)
{
bricks[x] = new PBBrick((int) (brickType.nextFloat() * 7));
if(isRowOdd)
{
bricks[x].posX = x - 2f ;
bricks[x].posY = (rowNumber * .25f) + 1 ;
}
else
{
bricks[x].posX = x - 2.5f;
bricks[x].posY = (rowNumber * .25f) + 1 ;
}
}
}
}
TIP The rowIsScored property allows you to track whether or not you have already awarded a score to the cleared row.
This will initially set the number of bricks remaining in the row to the total number of bricks in the row. Then, as you iterate through your collision detection method, you subtract 1 from this property for every destroyed brick. Once the property reaches 0, you call your scoring method.
private void detectCollisions(){
if(ball.posY <= 0){
//GameOver
}
for (int x = 0; x < wall.rows.length; x++)
{
for(int y = 0; y < wall.rows[x].bricks.length; y++)
{
if(!wall.rows[x].bricks[y].isDestroyed)
{
if (((ball.posY > wall.rows[x].bricks[y].posY- .25f)
&& (ball.posY < wall.rows[x].bricks[y].posY)
&& (ball.posX + .25f > wall.rows[x].bricks[y].posX)
&& (ball.posX < wall.rows[x].bricks[y].posX + 1.50f)))
{
wall.rows[x].bricks[y].isDestroyed = true;
wall.rows[x].numberOfBricksRemaining -=;
PBGameVars.ballTargetY = PBGameVars. ballTargetY * -1f;
if(PBGameVars.ballTargetX == -2f){
PBGameVars.ballTargetX = 5f;
}else{
PBGameVars.ballTargetX = -2f;
}
}
}
}
if(wall.rows[x]. numberOfBricksRemaining = 0 && wall. rows[x].rowIsScored == false){
advanceScore();
wall.rows[x].rowIsScored = true;
}
}
...
}
This is all that is required to create a few different score-taking mechanisms. I am sure that, with a little time, you can implement one that is even more creative.
Summary
In this chapter, you learned how to create three different ways to let your player track and compare his score with that of other players. This is vital to arcade games because it gives your player a greater sense of accomplishment.
In the next (and final) chapter of this book, you will learn how to add new levels to Prison Break.
98.82.120.188