Android FastRender View Loop Strech Loop Stretch

The name of this class should already give away what lies ahead. In the last chapter we discussed using a SurfaceView to perform continuous rendering in a separate thread that could also house our game's main loop. We developed a very simple class called FastRenderView, which derived from the SurfaceView class, we made sure we play nice with the activity life cycle, and we set up a thread in which we constantly rendered to the SurfaceView via a Canvas.

We'll reuse this FastRenderView class and augment it to do a few more things:

It will keep a reference to a Game instance from which it can get the active Screen. We will constantly call the Screen.update() and Screen.present() methods from within the FastRenderView thread.

It will keep track of the delta time between frames that gets passed to the active Screen.

It will take the artificial framebuffer that the AndroidGraphics instance draws to and draw it to the SurfaceView, scaled if necessary.

Listing 5-13 shows the implementation of the AndroidFastRenderView class.

Listing 5-13. AndroidFastRendeiView.java, a Threaded SurfaceView Executing Our Game Code package com.badlogic.androidgames.framework.impl;

import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; import android.view.SurfaceHolder; import android.view.SurfaceView;

public class AndroidFastRenderView extends SurfaceView implements Runnable { AndroidGame game; Bitmap framebuffer; Thread renderThread = null; SurfaceHolder holder; volatile boolean running = false;

This should look very familiar. We just need to add two more members: an AndroidGame instance and a Bitmap instance representing our artificial framebuffer. The other members are the same as in our FastRenderView from Chapter 3.

public AndroidFastRenderView(AndroidGame game, Bitmap framebuffer) { super(game); this.game = game; this.framebuffer = framebuffer; this.holder = getHolder();

In the constructor we simply call the base class's constructor with the AndroidGame parameter (which is an Activity; more on that in a bit) and store the parameters in the respective members. We also get a SurfaceHolder again, as we did previously.

public void resume() { running = true;

renderThread = new Thread(this); renderThread.start();

The resume() method is an exact copy of the FastRenderView.resume() method, so we don't need to go over that again. It just makes sure that our thread plays nice with the activity life cycle.

long startTime = System.nanoTime();

while(running) {

if(!holder.getSurface().isValid()) continue;

float deltaTime = (System.naiio7ime()-startTime) / 1000000000.0f;

startTime = System.nanoTime();

game.getCurrentScreen().update(deltaTime); game.getCurrentScreen().present(deltaTime);

Canvas canvas = holder.lockCanvas(); canvas.getClipBounds(dstRect);

canvas.drawBitmap(framebuffer, null, dstRect, null); holder.unlockCanvasAndPost(canvas);

The run() method has a few more bells and whistles. The first addition is the tracking of the delta time between each frame. We use System.nanoTime() for this, which returns the current time in nanoseconds as a long.

NOTE: A nanosecond is one-billionth of a second.

In each loop iteration, we start off by taking the difference between the last loop iteration's start time and the current time. To make working with that delta time easier, we convert it to seconds. Next we save the current time stamp, which we'll use in the next loop iteration to calculate the next delta time. With the delta time at hand, we call the current Screen's update() and present() methods, which will update the game logic and render things to the artificial framebuffer. Finally we get ahold of the Canvas for the SurfaceView and draw the artificial framebuffer. The scaling is performed automatically in case the destination rectangle we pass to the Canvas.drawBitmap() method is smaller or bigger than the framebuffer.

Note that we've used a shortcut here to get a destination rectangle that stretches over the whole SurfaceView via the Canvas.getClipBounds() method. It will set the top and left members of dstRect to 0 and 0, and the bottom and right members to the actual screen dimensions (480! 800 in portrait mode on a Nexus One). The rest of the method is exactly the same as what we had in our FastRenderView test. It just makes sure that the thread stops when the activity is paused or destroyed.

public void pause() {

renderThread.join(); break;

} catch (InterruptedException e) { // retry

The last method of this class, pause(), is again exactly the same as the FastRenderView.pause() method. It simply terminates the rendering/main loop thread and waits for it to completely die before returning.

We are nearly done with our framework. The last piece of the puzzle is the implementation of the Game interface.

0 0

Post a comment