Drawing the scene

As we will see, drawing a bitmap is really trivial. But the coordinate system that we use to draw our graphics onto needs a brief explanation.

Plotting and drawing

When we draw a Bitmap object to the screen, we pass in the coordinates we want to draw the object at. The available coordinates of a given Android device depend on the resolution of its screen.

For example, the Samsung Galaxy S4 phone has a screen resolution of 1920 pixels (across) by 1080 pixels (down) when held in a landscape view.

The numbering system of these coordinates starts in the top-left hand corner at 0,0 and proceeds down and to the right until the bottom right corner is pixel 1919, 1079. The apparent 1 pixel disparity between 1920/ 1919 and 1080/ 1079 is because the numbering starts at 0.

Therefore, when we draw a bitmap or any other drawable to the screen, we must specify x, y coordinates.

Furthermore, a bitmap is, of course, comprised of many pixels. So which pixel of a given bitmap is drawn at the x, y screen coordinate that we will be specifying?

The answer is the top-left pixel of the Bitmap object. Take a look at the next image, which should clarify the screen coordinates using the Samsung Galaxy S4 as an example.

For now, when drawing just a single ship at an arbitrary location, this information is of little consequence. It will become more important in the next chapter, when we start constraining our graphics to the visible screen and respawning them when they disappear.

So let's just bare this in mind and get on with drawing our ship to the screen.

Drawing PlayerShip

Now that we know all this, we can add some code to our TDView class, so we can see our PlayerShip class in action. First, we need a new PlayerShip object with class scope. The following code is the TDView class declaration:

//Game objects
private PlayerShip player;

We also need a bunch of objects that we haven't seen yet to help us actually do some drawing. We need a canvas and some paint.

The Canvas and Paint objects

The aptly named Canvas class provides just what you will expect—a virtual canvas to draw our graphics upon.

We can make a virtual canvas using the Canvas class and project it onto our SurfaceView object which is the view of your GameActivity class. We can actually add Bitmap objects and even manipulate inpidual pixels on our Canvas object using methods from our Paint object. In addition, we also need an object of the SurfaceHolder class. This allows us to lock your Canvas object while we are manipulating it and unlock it when we are ready to draw the frame.

We will see in more detail how these classes work as we proceed. Type this code immediately following the previous line of code we typed:

// For drawing
private Paint paint;
private Canvas canvas;
private SurfaceHolder ourHolder;

As usual, we need to use the Alt | Enter keyboard combination to import some new classes for the two lines of code that follow. From this point on, we will save digital link and just assume that you know to do this each time you add a new class.

Next, we need to set up to prepare for drawing. The best place to do this is in the TDView(), constructor. Type the following code to prepare our Paint and SurfaceHolder objects for action:

// Initialize our drawing objects
ourHolder = getHolder();
paint = new Paint();

Immediately after the previous line of code, we can at last call new() to initialize our PlayerShip object:

// Initialize our player ship
player = new PlayerShip(context);

Now, we can jump to our TDView class's update method and do this:

// Update the player
player.update();

That's it. The PlayerShip class (part of the model) knows what to do, and we can add all kinds of artificial intelligence into our PlayerShip class. The TDView class (the controller) just says when it is time to update. You can easily imagine that all we need to do is create lots of different game objects with different properties and behaviors and call their update methods once per frame.

Now, jump to the TDView class's draw method. Let's draw our player object by performing the following:

  1. Check that our SurfaceHolder class is valid.
  2. Lock the Canvas object.
  3. Clear the screen with a call to drawColor().
  4. Splash some virtual paint on it by calling drawBitmap() and passing in the PlayerShip bitmap and an x, y coordinate.
  5. Finally, unlock the Canvas object and draw the scene.

To achieve these things, type this code in the draw method:

if (ourHolder.getSurface().isValid()) {

  //First we lock the area of memory we will be drawing to
  canvas = ourHolder.lockCanvas();

  // Rub out the last frame
  canvas.drawColor(Color.argb(255, 0, 0, 0));

  // Draw the player
  canvas.drawBitmap(
    player.getBitmap(), 
    player.getX(), 
    player.getY(), 
    paint);

  // Unlock and draw the scene
  ourHolder.unlockCanvasAndPost(canvas);
}

At this point, we can actually run the game. If our eyesight is fast enough or our Android device slow enough, we will just about see our player spaceship fly across the screen with immense speed.

There is just one more thing to do before we deploy our game so far.

Controlling the frame rate

The reason we can barely see anything is that even though we only move our ship at one pixel per frame along the x axis (in the PlayerShip class's update method), our thread is calling the run method in an unrestricted manner. This is probably happening hundreds of times per second. What we need to do is control this rate.

Sixty frames per second (FPS) is a reasonable goal. This goal implies the need for timing. The Android system measures time in milliseconds (thousandths of a second). Therefore, we can add the following code to the control method:

try {
    gameThread.sleep(17);
    } catch (InterruptedException e) {

    }

In the preceding code, we paused the thread for 17 milliseconds (1000(milliseconds)/60(FPS)) by calling gameThread.sleep with 17 as the argument to the method. We wrap the code within a try/catch block.