An First Example Using Translation

What can we use this for? Say we want to render 100 Bobs at different positions in our world. Additionally we want them to move around on the screen and change direction each time they hit an edge of the screen (or rather a plane of our parallel projection view frustum, which coincides with the extents of our screen). We could do this by having one large Vertices instance that holds the vertices of the 100 rectangles—one for each Bob—and recalculate the vertex positions each frame. The easier method is to have one small Vertices instance that only holds a single rectangle (the model of Bob) and reuse it by translating it with the model-view matrix on the fly. Let's define our Bob model:

Vertices bobModel = new Vertices(glGraphics, 4, 12, false, true);

bobModel.setIndices(new short[] {0, 1, 2, 2, 3, 0}, 0, 6);

So, each Bob is 32x32 units in size. We also texture map him (we'll use bobrgb888.png to see the extents of each Bob).

Bob Becomes a Class

Let's define a simple Bob class. It will be responsible for holding a Bob's position and advancing his position in his current direction based on the delta time, just like we advanced Mr. Nom (with the difference that we don't move in a grid anymore). The update() method will also make sure that Bob doesn't escape our view volume bounds. Listing 7-12 shows the Bob class.

Listing 7-12. package com.badlogic.androidgames.glbasics;

import java.util.Random;

class Bob {

static final Random rand = new Random(); public float x, y; float dirX, dirY;

x = rand.nextFloat() * 320; y = rand.nextFloat() * 480; dirX = 50; dirY = 50;

public void update(float deltaTime) {

x = x + dirX * deltaTime; y = y + dirY * deltaTime;

Every Bob will place himself at a random location in the world when we construct him. All the Bobs will initially move in the same direction: 50 units to the right and 50 units upward per second (as we multiply by the deltaTime). In the update() method we simply advance Bob in his current direction in a time-based manner, and then check if he left the view frustum bounds. If that's the case we invert his direction and make sure he's still in the view frustum.

Now let's assume we are instantiating 100 Bobs, like this:

Bob[] bobs = new Bob[100]; for(int i = 0; i < 100; i++) { bobs[i] = new Bob();

To render each of these Bobs, we'd do something like this (assuming we've already cleared the screen, set the projection matrix, and bound the texture):

gl.glMatrixMode(GL10.GL_M0DELVIEW); for(int i = 0; i < 100; i++) { bob.update(deltaTime); gl.glLoadIdentity();

gl.glTranslatef(bobs[i].x, bobs[i].y, 0); bobModel.render(GL10.GL_TRIANGLES, 0, 6);

That is pretty sweet, isn't it? For each Bob, we call his update() method, which will advance his position and make sure he stays within the bounds of our little world. Next we load an identity matrix into the model-view matrix of OpenGL ES so we have a clean slate. We then use the current Bob's x- and y-coordinates in a call to glTransltef(). When we then render the Bob model in the next call, all the vertices will be offset by the current Bob's position—exactly what we wanted.

Putting It Together

Let's make this a full-blown example. Listing 7-13 shows the code. Listing 7-13.; 100 Moving Bobs! package com.badlogic.androidgames.glbasics;

import javax.microedition.khronos.opengles.GL10;

import com.badlogic.androidgames.framework.Game; import com.badlogic.androidgames.framework.Screen; import; import; import; import com.badlogic.androidgames.framework.impl.GLGame; import com.badlogic.androidgames.framework.impl.GLGraphics;

public class BobTest extends GLGame {

@0verride public Screen getStartScreen() { return new BobScreen(this);

class BobScreen extends Screen {

static final int NUM_BOBS = 100; GLGraphics glGraphics; Texture bobTexture; Vertices bobModel; Bob[] bobs;

Our BobScreen class holds a Texture (loaded from bobrbg888.png), a Vertices instance holding the model of Bob (a simple textured rectangle), and an array of Bob instances. We also define a little constant named NUM_B0BS so we can modify the number of Bobs we want to have on the screen.

public BobScreen(Game game) { super(game);

glGraphics = ((GLGame)game).getGLGraphics();

bobTexture = new Texture((GLGame)game, "bobrgb888.png");

bobModel = new Vertices(glGraphics, 4, 12, false, true); bobModel.setVertices(new float[] { -16, -16, 0, 1,

16, -16, 1, 1, 16, 16, 1, 0, -16, 16, 0, 0, }, 0, 16); bobModel.setIndices(new short[] {0, 1, 2, 2, 3, 0}, 0, 6);

bobs = new Bob[100]; for(int i = 0; i < 100; i++) { bobs[i] = new Bob();

The constructor just loads the texture, creates the model, and instantiates instances.

^Override public void update(float deltaTime) { game.getInput().getTouchEvents(); game.getInput().getKeyEvents();


for(int i = 0; i < NUM_BOBS; i++) { bobs[i].update(deltaTime);

The update() method is where we let our Bobs update themselves. We also make sure our input event buffers are emptied.

^Override public void present(float deltaTime) { GL10 gl = glGraphics.getGL(); gl.glClearColor(1,0,0,1); gl.glClear(GL10. GL_COLOR_BUFFER_BIT); gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); gl.gl0rthof(0, 320, 0, 480, 1, -1);

gl.glEnable(GL10. GL_TEXTURE_2D); bobTexture.bind();

gl.glMatrixMode(GL10.GL_MODELVIEW); for(int i = 0; i < NUM_BOBS; i++) { gl.glLoadIdentity();

gl.glTranslatef(bobs[i].x, bobs[i].y, 0); gl.glRotatef(45, 0, 0, 1); gl.glScalef(2, 0.5f, 0); bobModel.draw(GL10.GL_TRIANGLES, 0, 6);

In the render() method we clear the screen, set the projection matrix, enable texturing, and bind the texture of Bob. The last couple of lines are responsible for actually rendering each Bob instance. Since OpenGL ES remembers its states, we have to set the active matrix only once (in this case we are going to modify the model-view matrix in the rest of the code). We then loop through all the Bobs, set the model-view matrix to a translation matrix based on the position of the current Bob, and render the model, which will be translated by the model view-matrix automatically.

@0verride public void resume() { }

@0verride public void dispose() { }

That's it. Best of all, we employed the MVC pattern we used in Mr. Nom again. It really lends itself well to game programming. The logical side of Bob is completely decoupled from his appearance, which is nice, as we can easily replace his appearance with something more complex. Figure 7-20 shows the output of our little program after running for a few seconds.

Extends Glgame
Figure 7-20. That's a lot of Bobs.

That's not the end of all our fun with transformations yet. If you remember what I said a couple of pages ago, you'll know what's coming: rotations and scaling.

0 0

Post a comment