Chapter     11

Moving a Character

Moving a character around the screen—whether it’s a person, animal, robot, or vehicle—is one of the more crucial parts of a compelling game. Chances are, if you have tried to create a character that moves freely in a game, you have run into some problems.

This chapter will present solutions to help you move your character around. Solutions in this chapter include making a character run, and changing the character animation when the character is moving.

The first solution helps you move your character in four directions on the screen. The remaining solutions help you move your character at different speeds and animate your character as it moves.

11.1 Move a Character in Four Directions

Problem

The character on the screen will not move.

Solution

Use the game loop to control the movement of the character.

How It Works

This solution requires you to track where the player wants the character to move, then translate that intent to the x or y axis of the model matrix. In other words, once you have captured where the player wants to move, you can use a switch...case statement to determine which axis to translate in the model matrix, thus moving the character on the screen accordingly.

You complete this solution in three steps. You need to determine which direction the player wants to move in, then create a flag that holds this value, and finally use that value to move the character on the screen. The first step is to capture which direction the player wants to move. We will accomplish this by using the SimpleOnGestureListener().

The player will swipe left, right, up, or down to indicate which direction the character should run (think something similar to a Temple Run–style input system).In the game's main intent, instantiate a new SimpleOnGestureListener(), as shown in Listing 11-1.

Listing 11-1.  SimpleOnGestureListener()

GestureDetector.SimpleOnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener(){
@Override
public boolean onDown(MotionEventarg0) {
//TODO Auto-generated method stub
return false;
}

@Override
public boolean onFling(MotionEvente1, MotionEvente2, float velocityX,
float velocityY) {

float leftMotion = e1.getX() - e2.getX();
float upMotion = e1.getY() - e2.getY();

float rightMotion = e2.getX() - e1.getX();
float downMotion = e2.getY() - e1.getY();

if((leftMotion == Math.max(leftMotion, rightMotion))&&

(leftMotion>Math.max(downMotion, upMotion)) )
{
}

if((rightMotion == Math.max(leftMotion, rightMotion))&&
(rightMotion>Math.max(downMotion, upMotion) )
{
}
if((upMotion == Math.max(upMotion, downMotion))&&
(upMotion>Math.max(leftMotion, rightMotion)) )
{
}

if((downMotion == Math.max(upMotion, downMotion))&&
(downMotion>Math.max(leftMotion, rightMotion)) )
{
}
return false;
}
@Override
public void onLongPress(MotionEvent e) {
//TODO Auto-generated method stub

}
@Override
public boolean onScroll(MotionEvente1, MotionEvente2, float distanceX,
float distanceY) {
//TODO Auto-generated method stub
return false;
}
@Override
public void onShowPress(MotionEvent e) {
//TODO Auto-generated method stub

}
@Override
public boolean onSingleTapUp(MotionEvent e) {
//TODO Auto-generated method stub
return false;
}

};

Notice the four if statements within this instantiation. They represent the left, right, up, and down actions. Now create an int that can be accessed from both the main intent and the game loop. Set the int according to which direction the SimpleOnGestureListener() has detected (see Listing 11-2).

Listing 11-2.  SimpleOnGestureListener()

public static int playeraction = 0;
public static final int PLAYER_MOVE_LEFT = 1;
public static final int PLAYER_MOVE_RIGHT = 2;
public static final int PLAYER_MOVE_UP = 3;
public static final int PLAYER_MOVE_DOWN = 4;

...

if((leftMotion == Math.max(leftMotion, rightMotion)) &&
(leftMotion>Math.max(downMotion, upMotion)) )
{
playeraction = PLAYER_MOVE_LEFT;
}

if((rightMotion == Math.max(leftMotion, rightMotion)) &&
(rightMotion>Math.max(downMotion, upMotion) )
{
playeraction = PLAYER_MOVE_RIGHT;
}

if((upMotion == Math.max(upMotion, downMotion)) &&
(upMotion>Math.max(leftMotion, rightMotion)) )
{
playeraction = PLAYER_MOVE_UP;
}

if((downMotion == Math.max(upMotion, downMotion)) &&
(downMotion>Math.max(leftMotion, rightMotion)) )
{
playeraction = PLAYER_MOVE_DOWN;
}

...

Finally, in the Renderer, create a method that reads the value of the int you just set and translates the model matrix of the character accordingly, as shown in Listings 11-3 and 11-4.

Listing 11-3.  movePlayer()(OpenGL ES 1)

private void movePlayer(GL10gl){
switch(playeraction){
case PLAYER_MOVE_RIGHT:

gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(0f, .75f, 0f);
character.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();

break;
case PLAYER_MOVE_LEFT:

gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(0f, -.75f, 0f);
character.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();
break;

case PLAYER_MOVE_UP:

gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(.75f, 0f, 0f);
character.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();

break;
case PLAYER_MOVE_DOWN:

gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(-.75f, 0f, 0f);
character.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();
break;

}
}

Listing 11-4.  movePlayer()(OpenGL ES 2/3)

private void movePlayer(GL10gl){
switch(playeraction){
case PLAYER_MOVE_RIGHT:

Matrix.translateM(mTMatrix, 0, 0, .75f, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix);

break;
case PLAYER_MOVE_LEFT:

Matrix.translateM(mTMatrix, 0, 0, -.75f, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix);
break;

case PLAYER_MOVE_UP:

Matrix.translateM(mTMatrix, 0, .75f, 0, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix);
break;
case PLAYER_MOVE_DOWN:

Matrix.translateM(mTMatrix, 0, -.75f, 0, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix);
break;

}
}

The calls to glTranslatef() have been highlighted in bold in Listing 11-3 (for the OpenGL ES 1code) because you should translate your model matrix by whatever values work best in your specific game.

11.2 Move a Character at Different Speeds

Problem

The game character needs to walk and run at different speeds.

Solution

Use a count of game loops to determine when the character should change speeds.

How It Works

In this solution, you count the number of game loops that have been executed and use this count to determine when the character's speed should change. For example, your game is built in such a way that the character will move right when the player touches the right side of the screen, the character will move left when the player touches the left side of the screen, and the character stands still when the player is not touching the screen. You can use this architecture to let the character walk if the player just touches the screen for a short amount of time, and then run if the player touches the screen longer.

Tip   Chapter 5 outlines solutions for setting up a game with touch-based controls.

The first step is to create two variables that are scoped to be read from any class in your game. The first variable is to track the number of game loops that have been executed, and the second variable tracks the current speed of the character.

public static final float PLAYER_RUN_SPEED = .15f;
public static int totalGameLoops = 0;

Next, create a movePlayer() method in the Renderer class. This method has been used in multiple solutions thus far in this book. If you need a base explanation of how this method works, please see Chapter 6.

The movePlayer() method contains a switch...case statement that reads the actions of the player and moves the character accordingly. Modify this method to test for the number of executed loops and change the speed of the character based on this (see Listings 11-5 and 11-6).

Listing 11-5.  Varying the Speed of Movement (OpenGL ES 1)

private void movePlayer(GL10gl){
if (totalGameLoops> 15)
{
PLAYER_RUN_SPEED += .5f;
}

switch(playeraction){
case PLAYER_MOVE_RIGHT:

playercurrentlocation += PLAYER_RUN_SPEED;
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(playercurrentlocation, 0f, 0f);
goodguy.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();

break;
case PLAYER_MOVE_LEFT:

playercurrentlocation -= PLAYER_RUN_SPEED;
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(playercurrentlocation, 0f, 0f);
goodguy.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();

break;

case PLAYER_STAND:

PLAYER_RUN_SPEED = .15f;
totalGameLoops = 0;

break;
}
}

Listing 11-6.  Varying the Speed of Movement (OpenGL ES 2/3)

private void movePlayer(GL10gl){
if (totalGameLoops> 15)
{
PLAYER_RUN_SPEED += .5f;
}
switch(playeraction){
case PLAYER_MOVE_RIGHT:
playercurrentlocation += PLAYER_RUN_SPEED;
Matrix.translateM(mTMatrix, 0, 0,playercurrentlocation, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix);

break;
case PLAYER_MOVE_LEFT:
playercurrentlocation -= PLAYER_RUN_SPEED;
Matrix.translateM(mTMatrix, 0, 0,playercurrentlocation, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix);
break;

case PLAYER_STAND:

PLAYER_RUN_SPEED = .15f;
totalGameLoops = 0;

break;
}

}

Finally, in the onDrawFrame() method of the Renderer, increment the totalGameLoops int with each execution (see Listing 11-7).

Listing 11-7.  totalGameLoops

public void onDrawFrame(GL10gl) {

...

totalGameLoops +=1;
movePlayer(gl);

...
}

11.3 Animate a Character When It Moves

Problem

The game character does not appear to be walking when it moves.

Solution

Use spritesheet animation to make the character appear to walk when it moves.

How It Works

This solution will involve making a modification to the movePlayer() method that you have been working with quite extensively. After the model matrix is translated, translate the texture matrix to present the next frame in the spritesheet.

Note   For solutions on working with spritesheets, see Chapter 6.

First create a scoped variable, visible from all classes, that will be used to track the current frame of spritesheet animation.

public static float currentrunaniframe = 0f;

Next, make the bolded changes to the movePlayer() method (see Listings 11-8 and 11-9).

Listing 11-8.  Animating the Character (OpenGL ES 1)

private void movePlayer(GL10gl){
if (totalGameLoops> 15)
{
PLAYER_RUN_SPEED += .5f;
}

currentrunaniframe += .25f;
if (currentrunaniframe> .75f)
{
currentrunaniframe = .0f;
}

 
switch(playeraction){
case PLAYER_MOVE_RIGHT:

playercurrentlocation += PLAYER_RUN_SPEED;
scrollBackground1(gl, playeraction);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(playercurrentlocation, 0f, 0f);
gl.glMatrixMode(GL10.GL_TEXTURE);
gl.glLoadIdentity();
gl.glTranslatef(currentrunaniframe,.50f, 0.0f);
goodguy.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();

break;
case PLAYER_MOVE_LEFT:

playercurrentlocation -= PLAYER_RUN_SPEED;
scrollBackground1(gl, playeraction);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(playercurrentlocation, 0f, 0f);
gl.glMatrixMode(GL10.GL_TEXTURE);
gl.glLoadIdentity();
gl.glTranslatef(currentrunaniframe,.75f, 0.0f);
goodguy.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();

break;

case PLAYER_STAND:

PLAYER_RUN_SPEED = .15f;
totalGameLoops = 0;

break;
}
}

Listing 11-9.  Animating the Character (OpenGL ES 2/3)

private void movePlayer(GL10gl){
if (totalGameLoops> 15)
{
PLAYER_RUN_SPEED += .5f;
}
currentrunaniframe += .25f;
if (currentrunaniframe> .75f)
{
currentrunaniframe = .0f;
}

switch(playeraction){
case PLAYER_MOVE_RIGHT:
playercurrentlocation += PLAYER_RUN_SPEED;
Matrix.translateM(mTMatrix, 0, 0, playercurrentlocation, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix,currentrunaniframe, .50f );

break;
case PLAYER_MOVE_LEFT:
playercurrentlocation -= PLAYER_RUN_SPEED;
Matrix.translateM(mTMatrix, 0, 0, playercurrentlocation, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix,currentrunaniframe, .75f );
break;

case PLAYER_STAND:

PLAYER_RUN_SPEED = .15f;
totalGameLoops = 0;

break;
}

}

The bolded if statement in Listing 11-8 works based on four frames of animation. If there are four frames of character animation in the sprite sheet, after four loops the animation needs to be reset to the first frame. The if statement tests the current frame, and resets the animation if it has reached the fourth frame.

The key to animating the character (if you are working in OpenGL ES 2/3) is to modify the draw() method of your character class to pass in the x and y locations of the sprite sheet image that you want to display (see Chapter 6 for a detailed solution to accomplish this).

Finally, modify the PLAYER_STAND case to change the animation from to a static “standing” image. Keep in mind that depending on the setup of your spritesheet, the coordinates presented in this solution might need to be altered (see Listings 11-10 and 11-11).

Listing 11-10.  PLAYER_STAND (OpenGL ES 1)

case PLAYER_STAND:

PLAYER_RUN_SPEED = .15f;
totalGameLoops = 0;
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(playercurrentlocation, 0f, 0f);
gl.glMatrixMode(GL10.GL_TEXTURE);
gl.glLoadIdentity();
gl.glTranslatef(.25f,.25f, 0.0f);
goodguy.draw(gl);
gl.glPopMatrix();
gl.glLoadIdentity();

break;

Listing 11-11.  PLAYER_STAND (OpenGL ES 2/3)

case PLAYER_STAND:

PLAYER_RUN_SPEED = .15f;
totalGameLoops = 0;
playercurrentlocation -= PLAYER_RUN_SPEED;
Matrix.translateM(mTMatrix, 0, 0, playercurrentlocation, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTMatrix, 0, mMVPMatrix, 0)
character.draw(mMVPMatrix,.25f, .25f );

break;
..................Content has been hidden....................

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