Chapter    5

Creating the Player Character and Obstacles

In the previous chapter, you learned how to add a background image to your game, Prison Break. You created a class that, when instantiated, gave you all of the resources you needed to add your background.

In this chapter, you are going to take that knowledge and apply it to the game’s bricks, player paddle, and ball. This will give you all of the on-screen objects you need to make the game.

Before you begin working on the bricks, paddle, or ball, however, there are some things you need to do.

Before You Begin

You need to add a few things to your project to prepare for this chapter, the first being the images that you will use for the bricks, the paddle, and the ball. The image that I used for my bricks (there are a multitude of different bricks that could be used in the game) is a spritesheet, which allows you to use different images more easily by containing all of the images for the different bricks in one physical file.

If you have never worked with one, a spritesheet is a single-image file that contains within it all of the different images for a specific animation or set of characters. For example, if you were making a game where the main character could walk across the screen, the spritesheet for that character would contain all of the frames used to make the character animation.

Likewise, to give you—the game developer—some choice in how the game looks, the image for the ball is in a spritesheet as well. This spritesheet has two different ball images for you to choose from.

The images that I used in Prison Break for the brick spritesheet, the player paddle, and the ball spritesheet are shown in Figure 5-1, Figure 5-2, and Figure 5-3, respectively.

9781430245452_Fig05-01.jpg

Figure 5-1.  The brick spritesheet

9781430245452_Fig05-02.jpg

Figure 5-2.  The player paddle

9781430245452_Fig05-03.jpg

Figure 5-3.  The ball spritesheet

You may notice that the bricks and the player paddle are nearly square, rather than rectangular. This is good because it gives you a chance to stretch the image into a rectangle using OpenGL.

Next, you need to add some more variables to the PBGameVars file. Add the following lines to your PBGameVars. Don’t worry too much about the ones that you don’t understand right now; I will explain them when we use them.

public static float playerBankPosX = -.73f;

public static int playerAction = 0;

public static final int PLAYER_MOVE_LEFT_1 = 1;

public static final int PLAYER_RELEASE = 3;

public static final int PLAYER_MOVE_RIGHT_1 = 4;

public static final float PLAYER_MOVE_SPEED = .2f;

public static final int PADDLE = R.drawable.goldbrick;

public static final int BRICK_BLUE = 1;

public static final int BRICK_BROWN = 2;

public static final int BRICK_DARK_GRAY = 3;

public static final int BRICK_GREEN = 4;

public static final int BRICK_LITE_GRAY = 5;

public static final int BRICK_PURPLE = 6;

public static final int BRICK_RED = 7;

public static final int BRICK_SHEET = R.drawable.bricksheet;

public static final int BALL_SHEET = R.drawable.ballsheet;

public static final float BALL_SPEED = 0.01f;

public static float ballTargetY = 0.01f;

public static float ballTargetX = -1.125f;

With this little bit of housekeeping taken care of, it is time to jump into creating some items for the game. Let’s start with the player paddle.

Creating the Player Paddle Class

Add a new class called PBPlayer to your project. This class will look very much like the class that you created for the background, PBBackground. The class contains a constructor, a draw() method, and a loadTexture() method. The code for the PBPlayer class is shown in Listing 5-1.

Listing 5-1.   The PBPlayer Class

package com.jfdimarzio;

import java.io.IOException;

import java.io.InputStream;

import java.nio.ByteBuffer;

import java.nio.ByteOrder;

import java.nio.FloatBuffer;

import javax.microedition.khronos.opengles.GL10;

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.opengl.GLUtils;

public class PBPlayer {

        private FloatBuffer vertexBuffer;

        private FloatBuffer textureBuffer;

        private ByteBuffer indexBuffer;

        private int[] textures = new int[1];

        private float vertices[] = {

                0.0f, 0.0f, 0.0f,

                1.5f, 0.0f, 0.0f,

                1.5f, .25f, 0.0f,

                0.0f, .25f, 0.0f,

        };

        private float texture[] = {

                0.0f, 0.0f,

                1.0f, 0.0f,

                1.0f, 1.0f,

                0.0f, 1.0f,

        };

        private byte indices[] = {

                0,1,2,

                0,2,3,

        };

        public PBPlayer() {

                ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4);

                byteBuf.order(ByteOrder.nativeOrder());

                vertexBuffer = byteBuf.asFloatBuffer();

                vertexBuffer.put(vertices);

                vertexBuffer.position(0);

                byteBuf = ByteBuffer.allocateDirect(texture.length * 4);

                byteBuf.order(ByteOrder.nativeOrder());

                textureBuffer = byteBuf.asFloatBuffer();

                textureBuffer.put(texture);

                textureBuffer.position(0);

                indexBuffer = ByteBuffer.allocateDirect(indices.length);

                indexBuffer.put(indices);

                indexBuffer.position(0);

        }

        public void draw(GL10 gl) {

                gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

                gl.glFrontFace(GL10.GL_CCW);

                gl.glEnable(GL10.GL_CULL_FACE);

                gl.glCullFace(GL10.GL_BACK);

                gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

                gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

                gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);

                gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);

                gl.glDrawElements(GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_BYTE, indexBuffer);

                gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);

                gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

                gl.glDisable(GL10.GL_CULL_FACE);

        }

        public void loadTexture(GL10 gl,int texture, Context context) {

                InputStream imagestream =

context.getResources().openRawResource(texture);

                Bitmap bitmap = null;

                try {

                       bitmap = BitmapFactory.decodeStream(imagestream);

                }catch(Exception e){

                }finally {

                        try {

                              imagestream.close();

                              imagestream = null;

                        } catch (IOException e) {

                        }

                }

                gl.glGenTextures(1, textures, 0);

                gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

                gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);

                gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

                GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

                bitmap.recycle();

        }

}

In the next section, you will add the class for the bricks. Later in the chapter, you add the code that instantiates and calls all of the classes together.

Creating the Brick Class

Just like the player paddle and the background, you need a class that will represent your bricks. The class for the bricks is going to be a little different, however. Because you are going to use a spritesheet with the bricks class, you will not call loadTexture() the same way that you would with the background and the player paddle. Just to give you some perspective on how flexible the code can be, you are going to load all of the spritesheets into an array and pass them together. Therefore, we are going to remove the loadTexture() method and create a new class to handle spritesheet textures.

The code doesn’t have to be written this way; rather, because this book is a teaching tool, I am trying to show you different ways to do things. This is simply a good place to take a look at a different way of getting something done. Feel free, after you learn the differences, to use whichever method of texture loading you feel is best for your situation.

First, create a new class called PBBricks. The code for PBBricks is shown in Listing 5-2.

Listing 5-2.   PBBrick

PBBRick

package com.jfdimarzio;

import java.nio.ByteBuffer;

import java.nio.ByteOrder;

import java.nio.FloatBuffer;

import javax.microedition.khronos.opengles.GL10;

public class PBBrick {

        public float posY = 0f;

        public float posX = 0f;

        public float posT = 0f;

        public boolean isDestroyed = false;

        public int brickType = 0;

        private FloatBuffer vertexBuffer;

        private FloatBuffer textureBuffer;

        private ByteBuffer indexBuffer;

        private float vertices[] = {

                0.0f, 0.0f, 0.0f,

                1.0f, 0.0f, 0.0f,

                1.0f, .25f, 0.0f,

                0.0f, .25f, 0.0f,

        };

        private float texture[] = {

                0.0f, 0.0f,

                0.25f, 0.0f,

                0.25f, 0.25f,

                0.0f, 0.25f,

        };

        private byte indices[] = {

                0,1,2,

                0,2,3,

        };

        public PBBrick(int type) {

                brickType = type;

                ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4);

                byteBuf.order(ByteOrder.nativeOrder());

                vertexBuffer = byteBuf.asFloatBuffer();

                vertexBuffer.put(vertices);

                vertexBuffer.position(0);

                byteBuf = ByteBuffer.allocateDirect(texture.length * 4);

                byteBuf.order(ByteOrder.nativeOrder());

                textureBuffer = byteBuf.asFloatBuffer();

                textureBuffer.put(texture);

                textureBuffer.position(0);

                indexBuffer = ByteBuffer.allocateDirect(indices.length);

                indexBuffer.put(indices);

                indexBuffer.position(0);

        }

        public void draw(GL10 gl, int[] spriteSheet) {

                gl.glBindTexture(GL10.GL_TEXTURE_2D, spriteSheet[0]);

                gl.glFrontFace(GL10.GL_CCW);

                gl.glEnable(GL10.GL_CULL_FACE);

                gl.glCullFace(GL10.GL_BACK);

                gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

                gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

                gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);

                gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);

                gl.glDrawElements(GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_BYTE, indexBuffer);

                gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);

                gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

                gl.glDisable(GL10.GL_CULL_FACE);

        }

}

Caution   Pay close attention to the code in bold. It is different from the other draw() methods that you have created and it is important for loading the correct texture later in the chapter.

Since the PBBrick is using a spritesheet for its texture—as is the PBBall, which you will create later in the chapter—you need to make a new class that handles the texture loading for you.

Create a new class called PBTextures. The PBTextures class holds an array of textures, and serves up the correct one to the proper class that needs it. You should recognize the code in PBTextures as being from the loadTexture() method. The code for the PBTexture class is shown in Listing 5-3.

Listing 5-3.   PBTextures

package com.jfdimarzio;

import java.io.IOException;

import java.io.InputStream;

import javax.microedition.khronos.opengles.GL10;

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.opengl.GLUtils;

public class PBTextures {

        private int[] textures = new int[3];

        public PBTextures(GL10 gl){

                gl.glGenTextures(3, textures, 0);

        }

        public int[] loadTexture(GL10 gl, int texture, Context context,int textureNumber) {

                InputStream imagestream =

context.getResources().openRawResource(texture);

                Bitmap bitmap = null;

                try {

                        bitmap = BitmapFactory.decodeStream(imagestream);

                }catch(Exception e){

                }finally {

                        try {

                                imagestream.close();

                                imagestream = null;

                        } catch (IOException e) {

                        }

                }

                gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[textureNumber - 1]);

                gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);

                gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

                gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);

                gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);

                GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

                bitmap.recycle();

                return textures;

        }

}

With the PBTextures class out of the way, it is time to create the final object class, PBBall.

Create the PBBall Class

Create a new class called PBBall. This class, like the PBBrick, uses a spritesheet, so no loadTexture() is needed. The PBBall class is very much like the PBBrick. Watch for the code in bold, however; it contains an important change that is necessary for displaying the correct texture later.

The code for the PBBall is shown in Listing 5-4.

Listing 5-4.   PBBall

package com.jfdimarzio;

import java.nio.ByteBuffer;

import java.nio.ByteOrder;

import java.nio.FloatBuffer;

import java.util.Random;

import javax.microedition.khronos.opengles.GL10;

public class PBBall {

        public float posY = 0f;

        public float posX = 0f;

        public float posT = 0f;

        public int ballMode = 0;

        private Random randomPos = new Random();

        private int damage = 0;

        private FloatBuffer vertexBuffer;

        private FloatBuffer textureBuffer;

        private ByteBuffer indexBuffer;

        private float vertices[] = {

                0.0f, 0.0f, 0.0f,

                0.25f, 0.0f, 0.0f,

                0.25f, 0.25f, 0.0f,

                0.0f, 0.25f, 0.0f,

        };

        private float texture[] = {

                0.0f, 0.0f,

                0.50f, 0.0f,

                0.50f, 0.50f,

                0.0f, 0.50f,

        };

        private byte indices[] = {

                0,1,2,

                0,2,3,

        };

        public PBBall() {

                posY = (randomPos.nextFloat() + 1f) * (float)(-1.75 - -1.6);

                posX = randomPos.nextFloat() * .75f;

                ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4);

                byteBuf.order(ByteOrder.nativeOrder());

                vertexBuffer = byteBuf.asFloatBuffer();

                vertexBuffer.put(vertices);

                vertexBuffer.position(0);

                byteBuf = ByteBuffer.allocateDirect(texture.length * 4);

                byteBuf.order(ByteOrder.nativeOrder());

                textureBuffer = byteBuf.asFloatBuffer();

                textureBuffer.put(texture);

                textureBuffer.position(0);

                indexBuffer = ByteBuffer.allocateDirect(indices.length);

                indexBuffer.put(indices);

                indexBuffer.position(0);

        }

}

        public void draw(GL10 gl, int[] spriteSheet) {

                gl.glBindTexture(GL10.GL_TEXTURE_2D, spriteSheet[2]);

                gl.glFrontFace(GL10.GL_CCW);

                gl.glEnable(GL10.GL_CULL_FACE);

                gl.glCullFace(GL10.GL_BACK);

                gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

                gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

                gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);

                gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);

                gl.glDrawElements(GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_BYTE, indexBuffer);

                gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);

                gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

                gl.glDisable(GL10.GL_CULL_FACE);

        }

}

Now that you have all of your classes ready for your bricks, paddle, and ball, it is time to put them together and learn how to call them in the PBGameRenderer, right? Not quite. There are two smaller helper classes that you are going to need just to make life easier later on.

The PBRow and the PBWall

Undoubtedly, you have seen a Breakout-style game in the past. In these games, the bricks that you must break through are arranged in a brick wall pattern. This, too, is the case in Prison Break.

You are going to create two classes, PBRow and PBWall, to help make the task of instantiating multiple bricks and laying them out in a brick wall–style pattern. The PBWall class is going to be made up of a specified number of rows. These rows are separate instances of PBRow, which in turn is made of a predetermined number and layout of PBBrick. Therefore, when you initialize your game, you will only need to create an instance of PBWall and tell it how many rows you would like; PBWall will take care of the rest.

Create a new class called PBWall. The code for PBWall is shown in Listing 5-5-

Listing 5-5.   PBWall

package com.jfdimarzio;

public class PBWall {

        public PBRow[] rows;

        public PBWall(int numberOfRows){

                rows = new PBRow[numberOfRows];

                for(int x = 0; x < = numberOfRows - 1; x ++)

                {

                        rows[x] = new PBRow(x);

                }

        }

}

Next, create a new class called PBRow in your project. The code for PBRow is shown in Listing 5-6.

Listing 5-6.   PBRow

package com.jfdimarzio;

import java.util.Random;

public class PBRow {

        public PBBrick[] bricks;

        private Random brickType = new Random();

        private boolean isRowOdd = false;

        private int numberOfBricks = 0;

                public PBRow(int rowNumber){

                if(rowNumber        2 > 0)

                {

                        numberOfBricks = 4;

                        isRowOdd = true;

                }

                else

                {

                        numberOfBricks = 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 ;

                        }

                }

      }

}

I bolded one piece of code from PBRow to pay special attention to. This line creates a random number and assigns it to the brickType. Later in the PBGameRenderer, you will use this random brickType to determine the color (and texture) of the brick.

Now we put everything together in the PBGameRenderer.

Calling the Bricks, Paddle, and Ball in the PBGameRenderer

In this section of the chapter, I highlight the code that you need to add to the PBGameRenderer. After I highlight the code, I will give you the full code for the PBGameRenderer class so that you can see it in context.

The first thing you will need to do is create variables for your wall, paddle, ball, spritesheets, and so on. The following are the new variables you will need to add to the PBGameRenderer:

      private PBPlayer player1 = new PBPlayer();

      private PBBall ball = new PBBall();

      private PBTextures textureLoader;

      private int[] spriteSheets = new int[3];

      private int numberOfRows = 4;

      private PBWall wall;

With the variables in place, you need to add some texture loaders to the onSurfaceCreated() method. The code you need to add is bolded in the following snippet:

      @Override

      public void onSurfaceCreated(GL10 gl, EGLConfig arg1) {

             // TODO Auto-generated method stub

      initializeBricks();

      textureLoader = new PBTextures(gl);

      spriteSheets = textureLoader.loadTexture(gl, PBGameVars.BRICK_SHEET, PBGameVars.context, 1);

            gl.glEnable(GL10.GL_TEXTURE_2D);

            gl.glClearDepthf(1.0f);

            gl.glEnable(GL10.GL_DEPTH_TEST);

            gl.glDepthFunc(GL10.GL_LEQUAL);

            background.loadTexture(gl,PBGameVars.BACKGROUND, PBGameVars.context);

        player1.loadTexture(gl,PBGameVars.PADDLE, PBGameVars.context);

        }

Notice that the onSurfaceCreated() calls a new method, initializeBricks(). This new method creates your wall for you.

        private void initializeBricks(){

                wall = new PBWall(numberOfRows);

        }

Now you need a method that draws the bricks on each frame; something that can be called in the drawFrame() method, much like the drawBrackground() method. A drawBricks() method calls on every frame and serves a couple of functions. First, by iterating through the PBWall and reading the isDestroyed flag of each brick, it determines whether a brick has been knocked out of the game. If a brick has been destroyed, it is skipped in the drawing loop, thus preventing it from being rendered to the screen, and causing it to disappear from the game.

Second, the drawBricks() method uses a case statement built around the brickType of each brick to determine which of the images in the brick spritesheet to use as a texture for that specific brick. This is an important part of the code to pay attention to because it uses glTranslatef() to move the spritesheet to the correct texture on each brick.

Think of it like this: the brick that is drawn in the game is the size of one brick, but the spritesheet that holds all of the images is the size of seven bricks. Therefore, using glTranslatef(), you are going to move the spritesheet around on the brick until the correct brick image is mapped to the correct brick.

You see this in action in the code that is bolded, as follows:

        private void drawBricks(GL10 gl){

                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)

                                {

                                        switch (wall.rows[x].bricks[y].brickType){

                                                case PBGameVars.BRICK_BLUE:

                                                        gl.glMatrixMode(GL10.GL_MODELVIEW);

                                                        gl.glLoadIdentity();

                                                        gl.glPushMatrix();

                                                        gl.glScalef(.25f, .25f, 1f);

                                                        gl.glTranslatef(wall.rows[x].bricks[y].posX, wall.rows[x].bricks[y].posY, 0f);

        gl.glMatrixMode(GL10.GL_TEXTURE);

                                gl.glLoadIdentity();

                                gl.glTranslatef(0.50f, 0.25f , 0.0f);

                                wall.rows[x].bricks[y].draw(gl, spriteSheets);

                                gl.glPopMatrix();

                                gl.glLoadIdentity();

                                                        break;

                                                case PBGameVars.BRICK_BROWN:

                                                        gl.glMatrixMode(GL10.GL_MODELVIEW);

                                                        gl.glLoadIdentity();

                                                        gl.glPushMatrix();

                                                        gl.glScalef(.25f, .25f, 1f);

                                                        gl.glTranslatef(wall.rows[x].bricks[y].posX, wall.rows[x].bricks[y].posY, 0f);

                                                        gl.glMatrixMode(GL10.GL_TEXTURE);

                                                        gl.glLoadIdentity();

                                                        gl.glTranslatef(0.0f, 0.50f , 0.0f); wall.rows[x].bricks[y].draw(gl, spriteSheets);

                                                        gl.glPopMatrix();

                                                        gl.glLoadIdentity();

                                                        break;

                                                case PBGameVars.BRICK_DARK_GRAY:

                                                        gl.glMatrixMode(GL10.GL_MODELVIEW);

                                                        gl.glLoadIdentity();

                                                        gl.glPushMatrix();

                                                        gl.glScalef(.25f, .25f, 1f);

                                                        gl.glTranslatef(wall.rows[x].bricks[y].posX, wall.rows[x].bricks[y].posY, 0f);

                                                        gl.glMatrixMode(GL10.GL_TEXTURE);

                                                        gl.glLoadIdentity();

                                                        gl.glTranslatef(0.25f, 0.25f , 0.0f); wall.rows[x].bricks[y].draw(gl, spriteSheets);

                                                        gl.glPopMatrix();

                                                        gl.glLoadIdentity();

                                                        break;

                                                case PBGameVars.BRICK_GREEN:

                                                        gl.glMatrixMode(GL10.GL_MODELVIEW);

                                                        gl.glLoadIdentity();

                                                        gl.glPushMatrix();

                                                        gl.glScalef(.25f, .25f, 1f);

                                                        gl.glTranslatef(wall.rows[x].bricks[y].posX, wall.rows[x].bricks[y].posY, 0f);

                                                        gl.glMatrixMode(GL10.GL_TEXTURE);

                                                        gl.glLoadIdentity();

                                                        gl.glTranslatef(0.0f, 0.25f , 0.0f); wall.rows[x].bricks[y].draw(gl, spriteSheets);

                                                        gl.glPopMatrix();

                                                        gl.glLoadIdentity();

                                                        break;

                                                case PBGameVars.BRICK_LITE_GRAY:

                                                        gl.glMatrixMode(GL10.GL_MODELVIEW);

                                                        gl.glLoadIdentity();

                                                        gl.glPushMatrix();

                                                        gl.glScalef(.25f, .25f, 1f);

                                                        gl.glTranslatef(wall.rows[x].bricks[y].posX, wall.rows[x].bricks[y].posY, 0f);

                                                        gl.glMatrixMode(GL10.GL_TEXTURE);

                                                        gl.glLoadIdentity();

                                                        gl.glTranslatef(0.25f, 0.0f , 0.0f); wall.rows[x].bricks[y].draw(gl, spriteSheets);

                                                        gl.glPopMatrix();

                                                        gl.glLoadIdentity();

                                                        break;

                                                case PBGameVars.BRICK_PURPLE:

                                                        gl.glMatrixMode(GL10.GL_MODELVIEW);

                                                        gl.glLoadIdentity();

                                                        gl.glPushMatrix();

                                                        gl.glScalef(.25f, .25f, 1f);

                                                        gl.glTranslatef(wall.rows[x].bricks[y].posX, wall.rows[x].bricks[y].posY, 0f);

                                                        gl.glMatrixMode(GL10.GL_TEXTURE);

                                                        gl.glLoadIdentity();

                                                        gl.glTranslatef(0.50f, 0.0f , 0.0f); wall.rows[x].bricks[y].draw(gl, spriteSheets);

                                                        gl.glPopMatrix();

                                                        gl.glLoadIdentity();

                                                        break;

                                                case PBGameVars.BRICK_RED:

                                                        gl.glMatrixMode(GL10.GL_MODELVIEW);

                                                        gl.glLoadIdentity();

                                                        gl.glPushMatrix();

                                                        gl.glScalef(.25f, .25f, 1f);

                                                        gl.glTranslatef(wall.rows[x].bricks[y].posX, wall.rows[x].bricks[y].posY, 0f);

                                                        gl.glMatrixMode(GL10.GL_TEXTURE);

                                                        gl.glLoadIdentity();

                                                        gl.glTranslatef(0.0f, 0.0f , 0.0f); wall.rows[x].bricks[y].draw(gl, spriteSheets);

                                                        gl.glPopMatrix();

                                                        gl.glLoadIdentity();

                                                        break;

                                               default:

                                                        gl.glMatrixMode(GL10.GL_MODELVIEW);

                                                        gl.glLoadIdentity();

                                                        gl.glPushMatrix();

                                                        gl.glScalef(.25f, .25f, 1f);

                                                        gl.glTranslatef(wall.rows[x].bricks[y].posX, wall.rows[x].bricks[y].posY, 0f);

                                                        gl.glMatrixMode(GL10.GL_TEXTURE);

                                                        gl.glLoadIdentity();

                                                        gl.glTranslatef(0.0f, 0.0f , 0.0f); wall.rows[x].bricks[y].draw(gl, spriteSheets);

                                                        gl.glPopMatrix();

                                                        gl.glLoadIdentity();

                                                        break;

                                        }

                                }

                        }

                }

        }

Caution   Keep a close eye on each case in the switch statement. While they may look the same, there is an important difference. Notice the glTranslatef() call of each case in the Texture mode. Each one moves to a different set of coordinates, signifying the different brick images in the spritesheet.

Finally, you need two methods for moving the player paddle and the ball with each new frame. First, in the moveBall() method you will notice that there is some basic trajectory math being performed in the background, just to move it from its random starting point to a point off the screen. This math does not take into account any collision detection or angular deflections; that is covered in the next chapter.

        private void moveBall(GL10 gl){

                gl.glMatrixMode(GL10.GL_MODELVIEW);

                gl.glLoadIdentity();

                gl.glPushMatrix();

                gl.glScalef(.25f, .25f, 1f);

                ball.posX + = (float) ((PBGameVars.ballTargetX - ball.posX )/ (ball.posY / (PBGameVars.ballTargetY )));

                ball.posY - = PBGameVars.ballTargetY * 3;

                gl.glTranslatef(ball.posX, ball.posY, 0f);

                gl.glMatrixMode(GL10.GL_TEXTURE);

                gl.glLoadIdentity();

                gl.glTranslatef(0.0f,0.0f, 0.0f);

                ball.draw(gl,spriteSheets);

                gl.glPopMatrix();

                gl.glLoadIdentity();

        }

The move paddle is similar; however, it uses some variables from the PBGameVars to determine where to move the player.

        private void movePlayer1(GL10 gl){

                gl.glMatrixMode(GL10.GL_MODELVIEW);

                gl.glLoadIdentity();

                gl.glPushMatrix();

                gl.glScalef(.25f, .25f, 1f);

                 

                if (PBGameVars.playerAction == PBGameVars.PLAYER_MOVE_LEFT_1 && PBGameVars.playerBankPosX > 0)

                {

                        PBGameVars.playerBankPosX = PBGameVars.playerBankPosX - PBGameVars.PLAYER_MOVE_SPEED;

                }

                else if(PBGameVars.playerAction == PBGameVars.PLAYER_MOVE_RIGHT_1 && PBGameVars.playerBankPosX < 2.5)

                {

                        PBGameVars.playerBankPosX = PBGameVars.playerBankPosX + PBGameVars.PLAYER_MOVE_SPEED;

                }

                gl.glTranslatef(PBGameVars.playerBankPosX, .5f, 0f);

                gl.glMatrixMode(GL10.GL_TEXTURE);

                gl.glLoadIdentity();

                gl.glTranslatef(0.0f,0.0f, 0.0f);

                player1.draw(gl);

                gl.glPopMatrix();

                gl.glLoadIdentity();

        }

The playerAction variable in the previous code sample determines if the player wants to move the paddle to the left or to the right. In the real world, the player is touching either to the left or to the right side of the device’s screen to move the paddle. To detect the screen touches and set the appropriate variable, add the following bolded code to the PBGame file:

package com.jfdimarzio;

import android.app.Activity;

import android.os.Bundle;

import android.view.MotionEvent;

public class PBGame extends Activity {

        final PBGameVars gameEngine = new PBGameVars();

        private PBGameView gameView;

        @Override

        public void onCreate(Bundle savedInstanceState) {

                super.onCreate(savedInstanceState);

                gameView = new PBGameView(this);

                setContentView(gameView);

        }

        @Override

        protected void onResume() {

                super.onResume();

                gameView.onResume();

        }

        @Override

        protected void onPause() {

                super.onPause();

                gameView.onPause();

        }

        @Override

        public boolean onTouchEvent(MotionEvent event) {

                float x = event.getX();

                float y = event.getY();

                int height = PBGameVars.display.getHeight() / 4;

                int playableArea = PBGameVars.display.getHeight() - height;

                if (y > playableArea){

                      switch (event.getAction()){

                         case MotionEvent.ACTION_DOWN:

                             if(x < PBGameVars.display.getWidth() / 2){

                                    PBGameVars.playerAction = PBGameVars.PLAYER_MOVE_LEFT_1;

                             }else{

                                    PBGameVars.playerAction = PBGameVars.PLAYER_MOVE_RIGHT_1;

                             }

                             break;

                         case MotionEvent.ACTION_UP:

                              PBGameVars.playerAction = PBGameVars.PLAYER_RELEASE;

                              break;

                        }

                    }

                    return false;

    }

}

The complete PBGameRenderer (as of this chapter) should appear as shown in Listing 5-7. The code that you added in this chapter is in bold.

Listing 5-7.   PBGameRenderer

package com.jfdimarzio;

import javax.microedition.khronos.egl.EGLConfig;

import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLSurfaceView.Renderer;

public class PBGameRenderer implements Renderer{

        private PBBackground background = new PBBackground();

   private PBPlayer player1 = new PBPlayer();

   private PBBall ball = new PBBall();

   private PBTextures textureLoader;

   private int[] spriteSheets = new int[3];

   private int numberOfRows = 4;

   private PBWall wall;

      private long loopStart = 0;

      private long loopEnd = 0;

      private long loopRunTime = 0 ;

      private float bgScroll1;

      @Override

      public void onDrawFrame(GL10 gl) {

              // TODO Auto-generated method stub

              loopStart = System.currentTimeMillis();

              // TODO Auto-generated method stub

              try {

                      if (loopRunTime < PBGameVars.GAME_THREAD_FPS_SLEEP){

                              Thread.sleep(PBGameVars.GAME_THREAD_FPS_SLEEP - loopRunTime);

                      }

              } catch (InterruptedException e) {

                      // TODO Auto-generated catch block

                      e.printStackTrace();

              }

              gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

              drawBackground1(gl);

   movePlayer1(gl);

   drawBricks(gl);

   moveBall(gl);

              loopEnd = System.currentTimeMillis();

              loopRunTime = ((loopEnd - loopStart));

       }

       @Override

       public void onSurfaceChanged(GL10 gl, int width, int height) {

              // TODO Auto-generated method stub

              gl.glViewport(0, 0, width,height);

              gl.glMatrixMode(GL10.GL_PROJECTION);

              gl.glLoadIdentity();

              gl.glOrthof(0f, 1f, 0f, 1f, -1f, 1f);

       }

       @Override

       public void onSurfaceCreated(GL10 gl, EGLConfig arg1) {

              // TODO Auto-generated method stub

       initializeBricks();

       textureLoader = new PBTextures(gl);

       spriteSheets = textureLoader.loadTexture(gl, PBGameVars.BRICK_SHEET,PBGameVars.context, 1);

              gl.glEnable(GL10.GL_TEXTURE_2D);

              gl.glClearDepthf(1.0f);

              gl.glEnable(GL10.GL_DEPTH_TEST);

              gl.glDepthFunc(GL10.GL_LEQUAL);

              background.loadTexture(gl,PBGameVars.BACKGROUND, PBGameVars.context);

              player1.loadTexture(gl,PBGameVars.PADDLE, PBGameVars.context);

       }

       private void drawBackground1(GL10 gl){

              gl.glMatrixMode(GL10.GL_MODELVIEW);

              gl.glLoadIdentity();

              gl.glPushMatrix();

              gl.glScalef(1f, 1f, 1f);

              background.draw(gl);

              gl.glPopMatrix();

              gl.glLoadIdentity();

    }

       private void initializeBricks(){

            wall = new PBWall(numberOfRows);

   }

       private void drawBricks(GL10 gl){

           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)

                    {

                        switch (wall.rows[x].bricks[y].brickType){

                               case PBGameVars.BRICK_BLUE:

                                    gl.glMatrixMode(GL10.GL_MODELVIEW);

                                    gl.glLoadIdentity();

                                    gl.glPushMatrix();

                                    gl.glScalef(.25f, .25f, 1f);

                                    gl.glTranslatef(wall.rows[x].bricks[y].posX, wall.rows[x].bricks[y].posY, 0f);

                                    gl.glMatrixMode(GL10.GL_TEXTURE);

                                    gl.glLoadIdentity();

                                    gl.glTranslatef(0.50f, 0.25f, 0.0f); wall.rows[x].bricks[y].draw(gl, spriteSheets);

                                    gl.glPopMatrix();

                                    gl.glLoadIdentity();

                                    break;

                               case PBGameVars.BRICK_BROWN:

                                    gl.glMatrixMode(GL10.GL_MODELVIEW);

                                    gl.glLoadIdentity();

                                    gl.glPushMatrix();

                                    gl.glScalef(.25f, .25f, 1f);

                                    gl.glTranslatef(wall.rows[x].bricks[y].posX, wall.rows[x].bricks[y].posY, 0f);

                                    gl.glMatrixMode(GL10.GL_TEXTURE);

                                    gl.glLoadIdentity();

                                    gl.glTranslatef(0.0f, 0.50f, 0.0f);

                                    wall.rows[x].bricks[y].draw(gl, spriteSheets);

                                    gl.glPopMatrix();

                                    gl.glLoadIdentity();

                                    break;

                               case PBGameVars.BRICK_DARK_GRAY:

                                    gl.glMatrixMode(GL10.GL_MODELVIEW);

                                    gl.glLoadIdentity();

                                    gl.glPushMatrix();

                                    gl.glScalef(.25f, .25f, 1f);

                                    gl.glTranslatef(wall.rows[x].bricks[y].posX, wall.rows[x].bricks[y].posY, 0f);

                                    gl.glMatrixMode(GL10.GL_TEXTURE);

                                    gl.glLoadIdentity();

                                    gl.glTranslatef(0.25f, 0.25f, 0.0f);

                                    wall.rows[x].bricks[y].draw(gl, spriteSheets);

                                    gl.glPopMatrix();

                                    gl.glLoadIdentity();

                                    break;

                               case PBGameVars.BRICK_GREEN:

                                    gl.glMatrixMode(GL10.GL_MODELVIEW);

                                    gl.glLoadIdentity();

                                    gl.glPushMatrix();

                                    gl.glScalef(.25f, .25f, 1f);

                                    gl.glTranslatef(wall.rows[x].bricks[y].posX, wall.rows[x].bricks[y].posY, 0f);

                                    gl.glMatrixMode(GL10.GL_TEXTURE);

                                    gl.glLoadIdentity();

                                    gl.glTranslatef(0.0f, 0.25f, 0.0f); wall.rows[x].bricks[y].draw(gl, spriteSheets);

                                    gl.glPopMatrix();

                                    gl.glLoadIdentity();

                                    break;

                               case PBGameVars.BRICK_LITE_GRAY:

                                    gl.glMatrixMode(GL10.GL_MODELVIEW);

                                    gl.glLoadIdentity();

                                    gl.glPushMatrix();

                                    gl.glScalef(.25f, .25f, 1f);

                                    gl.glTranslatef(wall.rows[x].bricks[y].posX, wall.rows[x].bricks[y].posY, 0f);

                                    gl.glMatrixMode(GL10.GL_TEXTURE);

                                    gl.glLoadIdentity();

                                    gl.glTranslatef(0.25f, 0.0f, 0.0f); wall.rows[x].bricks[y].draw(gl, spriteSheets);

                                    gl.glPopMatrix();

                                    gl.glLoadIdentity();

                                    break;

                               case PBGameVars.BRICK_PURPLE:

                                    gl.glMatrixMode(GL10.GL_MODELVIEW);

                                    gl.glLoadIdentity();

                                    gl.glPushMatrix();

                                    gl.glScalef(.25f, .25f, 1f);

                                    gl.glTranslatef(wall.rows[x].bricks[y].posX, wall.rows[x].bricks[y].posY, 0f);

                                    gl.glMatrixMode(GL10.GL_TEXTURE);

                                    gl.glLoadIdentity();

                                    gl.glTranslatef(0.50f, 0.0f, 0.0f); wall.rows[x].bricks[y].draw(gl, spriteSheets);

                                    gl.glPopMatrix();

                                    gl.glLoadIdentity();

                                    break;

                               case PBGameVars.BRICK_RED:

                                    gl.glMatrixMode(GL10.GL_MODELVIEW);

                                    gl.glLoadIdentity();

                                    gl.glPushMatrix();

                                    gl.glScalef(.25f, .25f, 1f);

                                    gl.glTranslatef(wall.rows[x].bricks[y].posX, wall.rows[x].bricks[y].posY, 0f);

                                    gl.glMatrixMode(GL10.GL_TEXTURE);

                                    gl.glLoadIdentity();

                                    gl.glTranslatef(0.0f, 0.0f, 0.0f); wall.rows[x].bricks[y].draw(gl, spriteSheets);

                                    gl.glPopMatrix();

                                    gl.glLoadIdentity();

                                    break;

                        default:

                                    gl.glMatrixMode(GL10.GL_MODELVIEW);

                                    gl.glLoadIdentity();

                                    gl.glPushMatrix();

                                    gl.glScalef(.25f, .25f, 1f);

                                    gl.glTranslatef(wall.rows[x].bricks[y].posX, wall.rows[x].bricks[y].posY, 0f);

                                    gl.glMatrixMode(GL10.GL_TEXTURE);

                                    gl.glLoadIdentity();

                                    gl.glTranslatef(0.0f, 0.0f, 0.0f); wall.rows[x].bricks[y].draw(gl, spriteSheets);

                                    gl.glPopMatrix();

                                    gl.glLoadIdentity();

                                    break;

                     }

                 }

             }

          }

      }

       private void moveBall(GL10 gl){

           gl.glMatrixMode(GL10.GL_MODELVIEW);

           gl.glLoadIdentity();

           gl.glPushMatrix();

           gl.glScalef(.25f, .25f, 1f);

           ball.posX += (float) ((PBGameVars.ballTargetX - ball.posX )/ (ball.posY /(PBGameVars.ballTargetY )));

           ball.posY -= PBGameVars.ballTargetY * 3;

           gl.glTranslatef(ball.posX, ball.posY, 0f);

           gl.glMatrixMode(GL10.GL_TEXTURE);

           gl.glLoadIdentity();

           gl.glTranslatef(0.0f,0.0f, 0.0f);

           ball.draw(gl,spriteSheets);

           gl.glPopMatrix();

           gl.glLoadIdentity();

      }

       private void movePlayer1(GL10 gl){

           gl.glMatrixMode(GL10.GL_MODELVIEW);

           gl.glLoadIdentity();

           gl.glPushMatrix();

           gl.glScalef(.25f, .25f, 1f);

           if (PBGameVars.playerAction == PBGameVars.PLAYER_MOVE_LEFT_1 &&PBGameVars.playerBankPosX > 0)

       {

             PBGameVars.playerBankPosX = PBGameVars.playerBankPosX - PBGameVars.PLAYER_MOVE_SPEED;

       }

           else if(PBGameVars.playerAction == PBGameVars.PLAYER_MOVE_RIGHT_1 &&PBGameVars.playerBankPosX < 2.5)

       {

             PBGameVars.playerBankPosX = PBGameVars.playerBankPosX + PBGameVars.PLAYER_MOVE_SPEED;

       }

           gl.glTranslatef(PBGameVars.playerBankPosX, .5f, 0f);

           gl.glMatrixMode(GL10.GL_TEXTURE);

           gl.glLoadIdentity();

           gl.glTranslatef(0.0f,0.0f, 0.0f);

           player1.draw(gl);

           gl.glPopMatrix();

           gl.glLoadIdentity();

       }

    }

Compile and run your game in the emulator or on your device. You will now see a wall of bricks to destroy, a responsive player paddle, and a ball that quickly moves off the screen.

Summary

In this chapter, you created a wall of randomly colored bricks, a movable player paddle, and a ball. This is a lot of code to add to the game, and it brings you very close to completing the Prison Break game. You also learned how to use a spritesheet, and you created a great helper class that builds a wall of bricks with a specified number of rows.

The ability to use helper classes to do some of the more tedious setup work in a game is a valuable skill that you find yourself using more and more. Try to look for places where you can use helper classes to handle character or object setup in your games.

In the next chapter, you will create the collision detection logic that finishes the playable physics of the game.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
98.82.120.188