Chapter     13

Moving a Character with Obstacles

If games, especially platform games, featured flat levels where the character simply ran from left to right unencumbered, they would not hold a player’s interest for very long.

Platform games such as Super Mario Brothers, LittleBigPlanet2, and countless others contain obstacles that the player must navigate around. Using obstacles in your games is a great way to add excitement and also brake up action. However, obstacles can also be an added level of complication when it comes to coding your game.

This chapter will cover a number of scenarios where you might have had problems working with obstacles in your games. The first scenario covers letting a character jump between platforms.

13.1 Jump Between Platforms

Problem

The game does not allow the player to jump between platforms on a game level.

Solution

Use a predetermined distance, and a mathematic formula to adjust the jumping animation.

How It Works

Everyone knows what a person looks like when they jump. There is a specific motion and smoothness to a jump that can be hard to replicate in a game. In this solution, we are going to modify some of the code from earlier solutions to create a jumping action for the playable character.

The first step is to create a control for the user to “jump.”  In Solution 5.4, I showed you how to create a gesture using the onFling() method of the SimpleOnGestureListener. Modify that code to set a common variable for indicating that the player wants to jump.

GestureDetector.SimpleOnGestureListener gestureListener = new
GestureDetector.SimpleOnGestureListener(){


@Override
publicbooleanonFling(MotionEvente1, MotionEvente2, float velocityX,
floatvelocityY) {

playeraction = PLAYER_JUMPING;
}
};

The playeraction and PLAYER_JUMPING variables are integers that are stored in a class that is accessible to the project.

The character we are going to make jump is the SuperBanditGuy. Earlier in this book, you created a SuperBanditGuy class that created the main character for the game and moved him around the screen. Modify the SuperBanditGuy class to add two floats (x and y) that will be used to track the x and y coordinates of the character as they progress through the jump.

public class SuperBanditGuy {

public float x = .75f;
public float y = .75f;
//I like to start characters a little higher than the bottom of the screen so
//that the player can see some ground under them
...

}

Now, add the following floats to the game loop,Renderer.

private float previousJumpPos = 0;
private float posJump = 0;

Again, in a previous solution, we created a case statement in the game loop that allows you to test the playeraction and move the character accordingly. Let's now modify that case statement to test for PLAYER_JUMPING and then launch into the math for calculating the jump. Listings 13-1 and 13-2  (OpenGL ES 1 and OpenGL ES 2/3, respectively) will let your character perform a basic jump.

Listing 13-1.  PLAYER_JUMPING (OpenGL ES 1)

switch(playeraction){
case PLAYER_MOVE_RIGHT:

...

break;
case PLAYER_JUMPING:
previousJumpPos = posJump;

posJump += (float)(((Math.PI / 2) / .5) * PLAYER_RUN_SPEED);
if (posJump<= Math.PI)
{
goodguy.y += 1.5 / .5 * .15 * PLAYER_RUN_SPEED;

}else{
goodguy.y -=(Math.sin((double)posJump) - Math.sin((double)previousJumpPos))* 1.5;
if (goodguy.y<= .75f){
playeraction = PLAYER_STAND;
goodguy.y = .75f;
}
}
goodguy.x += PLAYER_RUN_SPEED;

gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glScalef(.15f, .15f, 1f);
gl.glTranslatef(goodguy.x, goodguy.y, 0f);
gl.glPopMatrix();
gl.glLoadIdentity();

break;
...
}

Listing 13-2.  PLAYER_JUMPING (OpenGL ES 2/3)

switch(playeraction){
case PLAYER_MOVE_RIGHT:

...

break;
case PLAYER_JUMPING:
previousJumpPos = posJump;

posJump += (float)(((Math.PI / 2) / .5) * PLAYER_RUN_SPEED);
if (posJump<= Math.PI)
{
goodguy.y += 1.5 / .5 * .15 * PLAYER_RUN_SPEED;

}else{
goodguy.y -=(Math.sin((double)posJump) - Math.sin((double)previousJumpPos))* 1.5;
if (goodguy.y<= .75f){
playeraction = PLAYER_STAND;
goodguy.y = .75f;
}
}
goodguy.x += PLAYER_RUN_SPEED;

Matrix.translateM(RotationMatrix, 0, goodguy.x, goodguy.y, 0);

break;
...
}

The key to moving the character in a convincing jumping motion, regardless of the OpenGL ES version, is the following formula:

posJump += (float)(((Math.PI / 2) / .5) * PLAYER_RUN_SPEED);
if (posJump<= Math.PI)
{
goodguy.y += 1.5 / .5 * .15 * PLAYER_RUN_SPEED;
}else{
goodguy.y -=(Math.sin((double)posJump) - Math.sin((double)previousJumpPos))* 1.5;
if (goodguy.y<= .75f){
playeraction = PLAYER_STAND;
goodguy.y = .75f;
}
}

Note   There are a number of values in this formula that you need to tweak, based on your specific game. These values include the height and the length, in time, of the jump.

Notice that this formula only acts on the y axis of the character’s position. The x-axis position is going to continue to move left or right at the determined run speed of the character. Let’s examine each line of this formula.

previousJumpPos = posJump;

This first line sets the previousJumpPosfor use later in the formula.

posJump += (float)(((Math.PI / 2) / .5) * PLAYER_RUN_SPEED);

This line plots the position of the jump on a sine wave. This is not used to directly determine where on the screen the character is in the jump. Rather, it is used to determine, in memory, when the character has reached the peak on the jump.

The value of .5 is the length of time of the jump. While it doesn’t represent a specific unit of time, it can be increased or decreased to create longer or shorter lasting jumps. The Math.PI/2, or half PI, part of the line simply represents the fact that we are starting on a plane that is already halfway into the sine wave.

When you are dealing with sine waves, PI is the amount of time it takes to cycle the wave. Therefore, the timing of our wave is half PI to PI. While the current position on the sine wave is less than PI, we know the character is in the process of going up to the apex of the jump. Once the character’s position is greater than PI, we can begin to bring it back down to the ground. The purpose of the next line, the if statement, is to test for this condition.

if (posJump<= Math.PI)
{
goodguy.y += 1.5 / .5 * .15 * PLAYER_RUN_SPEED;
}else{
goodguy.y -=(Math.sin((double)posJump) - Math.sin((double)previousJumpPos))* 1.5;
...
}

Finally, within the if statement, the first condition moves the character up on the y axis, and the second condition moves it down.

goodguy.y += 1.5 / .5 * .15 * PLAYER_RUN_SPEED;

Notice that the character’s y-axis position is being increased by the formula. This will move the character up on the y axis. At the same time, the character’s x-axis position is being increased, as if the character were moving normally on the x axis in the direction of the jump.

The following line moves the character down.

goodguy.y -=(Math.sin((double)posJump) - Math.sin((double)previousJumpPos))* 1.5;

This formula decreases the value of the y-axis position to move the character back down to the ground level.

Note   The value of 1.5 in both of the moving statements represents the height of the jump. Again, you can adjust this value as needed for higher or shorter jumps.

When the jump is over, simply test that the character’s position is once again .75 on the y axis (the starting position as defined in the float added to the renderer) and exit from the jump, as follows.

if (goodguy.y<= .75f){
playeraction = PLAYER_STAND;
goodguy.y = .75f;
}

The best placement for this code is in the if statement where the y-axis position is being decreased. If you place this code outside of the if statement, you run the risk of letting the character fall below the ground level for a split second.

Finally, draw out the character, as shown in Listings 13-3 and 13-4.

Listing 13-3.  Draw the Character (OpenGL ES 1)

gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glScalef(.15f, .15f, 1f);
gl.glTranslatef(goodguy.x, goodguy.y, 0f);
gl.glMatrixMode(GL10.GL_TEXTURE);
gl.glLoadIdentity();

Listing 13-4.  Draw the Character (OpenGL ES 2/3)

Matrix.translateM(RotationMatrix, 0, goodguy.x, goodguy.y, 0);

This code can be easily added to your renderer to produce a jumping motion.

13.2 Move up Steps

Problem

The character needs to jump up or down steps, or other objects that are on uneven planes.

Solution

Use a modified version of the jumping solution to navigate steps.

How It Works

In Chapter 15, I will be presenting solutions for collision detection. While the solution in this chapter does broach the subject of collision detection, it is not as in depth as the solutions that will be presented later. Rather, this solution will specifically handle the modification of the jumping code from the last solution that will be needed to navigate steps.

If you are jumping up steps, you will start with the same code from Listings 13-1 and 13-2.

The modification that needs to be made is in the if statement that tests whether the character has descended far enough through the jump to reach the ground again. Replace the test value with the height of the step.

if (goodguy.y<=<height of step>){
playeraction = PLAYER_STAND;
goodguy.y =<height of step>;
}

By testing whether the character has descended to the height of the step, you can stop the movement of the character on the step. This solution requires that you either know, or test for, the height of the step.

This solution is good for levels where you can, in code, anticipate the layout of the level. For example, if you built your level using tiles, you can test the tile map to know where the step are and therefore know the height at which to stop the descent. This solution is not as good if the layout of the level can change dynamically.

If you have a level that contains debris or perhaps moving platforms and you cannot use this method, refer to Chapter 15, which has much more on collision detection.

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

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