- Android Game Programming by Example
- John Horton
- 1023字
- 2021-07-16 13:50:02
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:
- Check that our
SurfaceHolder
class is valid. - Lock the
Canvas
object. - Clear the screen with a call to
drawColor()
. - Splash some virtual paint on it by calling
drawBitmap()
and passing in thePlayerShip
bitmap and an x, y coordinate. - 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.