Coding the Hud

The HUD in this game is no more complex than the previous game. We will define some Rect instances to draw the controls on the screen, we will rely on GameState to provide the time and fastest times for each level and we will make the button Rect ArrayList available so that GameEngine can pass then them to our two classes that require them to handle the player's input.

Get started by adding a new class called HUD and add the following members and constructor method.

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;

import java.util.ArrayList;

class HUD {
    private Bitmap mMenuBitmap;

    private int mTextFormatting;
    private int mScreenHeight;
    private int mScreenWidth;
    final float ONE_THIRD = .33f;
    final float TWO_THIRDS = .66f;

    private ArrayList<Rect> mControls;

    static int LEFT = 0;
    static int RIGHT = 1;
    static int JUMP = 2;

    HUD(Context context, Point size){
        mScreenHeight = size.y;
        mScreenWidth = size.x;
        mTextFormatting = size.x / 25;

        prepareControls();

        // Create and scale the bitmaps
        mMenuBitmap = BitmapFactory
                .decodeResource(context.getResources(),
                        R.drawable.menu);

        mMenuBitmap = Bitmap
                .createScaledBitmap(mMenuBitmap,
                        size.x, size.y, false);

    }
}

The class starts off with five members that we will use to control position and formatting of the parts of the HUD. Next there is an ArrayList for our Rect buttons and next there is the static variables LEFT, RIGHT and JUMP which the UIController (that we code next) and PlayerInputComponent (that we code next chapter) can use to identify what the player is trying to do.

In the constructor we initialize some of our formatting variables using the passed in screen resolution, call the prepareControls method and load and scale the Bitmap that is used for the menu background.

Now we can code the prepareControls method that we just called.

private void prepareControls(){
   int buttonWidth = mScreenWidth / 14;
   int buttonHeight = mScreenHeight / 12;
   int buttonPadding = mScreenWidth / 90;


   Rect left = new Rect(
               buttonPadding,
               mScreenHeight - buttonHeight - buttonPadding,
               buttonWidth + buttonPadding,
               mScreenHeight - buttonPadding);

   Rect right = new Rect(
                (buttonPadding * 2) + buttonWidth,
                mScreenHeight - buttonHeight - buttonPadding,
                (buttonPadding * 2) + (buttonWidth * 2),
                mScreenHeight - buttonPadding);

   Rect jump = new Rect(mScreenWidth - buttonPadding 
               - buttonWidth,
               mScreenHeight - buttonHeight - buttonPadding,
               mScreenWidth - buttonPadding,
               mScreenHeight - buttonPadding);


   mControls = new ArrayList<>();
   mControls.add(LEFT,left);
   mControls.add(RIGHT,right);
   mControls.add(JUMP, jump);
}

In the previous code we initialize our remaining formatting members relative to the screen resolution in pixels. We then use them to position our three Rect objects that represent the buttons and then add them to the mControls ArrayList.

Next add the draw method which just like the HUD in the previous project will be called each frame of the game to draw the HUD. Notice the usual suspects are passed in as parameters to enable the method to do its job.

void draw(Canvas c, Paint p, GameState gs){

   if(gs.getGameOver()){

         // Draw the mMenuBitmap screen
         c.drawBitmap(mMenuBitmap, 0,0, p);

         // draw a rectangle to highlight the text
         p.setColor(Color.argb (100, 26, 128, 182));
         c.drawRect(0,0, mScreenWidth, 
                     mTextFormatting * 4, p);

         // Draw the level names
         p.setColor(Color.argb(255, 255, 255, 255));
         p.setTextSize(mTextFormatting);
         c.drawText("Underground", 
                     mTextFormatting, 
                     mTextFormatting * 2, 
                     p);
      
         c.drawText("Mountains", 
                     mScreenWidth * ONE_THIRD 
                     + (mTextFormatting), 
                     mTextFormatting * 2, 
                     p);
      
         c.drawText("City", 
                     mScreenWidth * TWO_THIRDS 
                     + (mTextFormatting), 
                     mTextFormatting * 2, 
                     p);

         // Draw the fastest times
         p.setTextSize(mTextFormatting/1.8f);
      
         c.drawText("BEST:" + gs.getFastestUnderground() 
                     +" seconds", 
                     mTextFormatting, 
                     mTextFormatting*3, 
                     p);
      
      c.drawText("BEST:" + gs.getFastestMountains() 
                  +" seconds", mScreenWidth * ONE_THIRD 
                  + mTextFormatting, 
                  mTextFormatting * 3, 
                  p);
      
      c.drawText("BEST:" + gs.getFastestCity() 
                  + " seconds", 
                  mScreenWidth * TWO_THIRDS + mTextFormatting, 
                  mTextFormatting * 3, 
                  p);

      // draw a rectangle to highlight the large text
      p.setColor(Color.argb (100, 26, 128, 182));
      c.drawRect(0,mScreenHeight - mTextFormatting * 2, 
                   mScreenWidth, 
                   mScreenHeight, 
                   p);

      p.setColor(Color.argb(255, 255, 255, 255));
      p.setTextSize(mTextFormatting * 1.5f);
      c.drawText("DOUBLE TAP A LEVEL TO PLAY",
                  ONE_THIRD + mTextFormatting * 2, 
                  mScreenHeight - mTextFormatting/2, 
                  p);
   }
   // else block follows next

}

The method is long and might seem complicated at first glance but there is nothing we haven't seen before. There is one thing to note, however. All the code is wrapped in an if block with a condition of gs.getGameOver. So, all the code we just added runs when the game is over. We will add the else block which follows this if block in a moment.

The code inside the if block draws the background, level names and fastest times as well as the message to tell the player how to start the game. Clearly, we don't want these things on the screen while the game is being played.

Still inside the draw method add the else block that follows the if block which will execute while the game is being played.

// else block follows next
else {
   // draw a rectangle to highlight the text
   p.setColor(Color.argb (100, 0, 0, 0));
   c.drawRect(0,0, mScreenWidth, 
                mTextFormatting, 
                p);

   // Draw the HUD text
   p.setTextSize(mTextFormatting/1.5f);
   p.setColor(Color.argb(255, 255, 255, 255));
   c.drawText("Time:" + gs.getCurrentTime() 
               + "+" + gs.getCoinsRemaining() * 10, 
               mTextFormatting / 4, 
               mTextFormatting / 1.5f, 
               p);


   drawControls(c, p);
}

In the else block we draw a transparent rectangle across the top of the screen which has the effect of highlighting the text that is drawn on top of it. Then we draw the current time. The slightly convoluted formula (gs.getCoinsRemaining() * 10) has the effect of calculating (and displaying) the time penalty based on how many coins the player still needs to collect. The final line of code in the draw method (but still inside the else block) calls the drawControls method. This is separated out purely to stop the method getting any longer than it already is.

Here are the final two methods of the HUD class. Add the drawControls and getControls methods.

private void drawControls(Canvas c, Paint p){
   p.setColor(Color.argb(100,255,255,255));

   for(Rect r : mControls){
         c.drawRect(r.left, r.top, r.right, r.bottom, p);
   }       

   // Set the colors back
   p.setColor(Color.argb(255,255,255,255));
}


ArrayList<Rect> getControls(){
   return mControls;
}

The drawControls method loops through the mControls ArrayList and draws each button as a transparent rectangle. The getControls method simply returns a reference to mControls. GameEngine will use this method to pass mControls to the other classes that need it.

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

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