Coding the game loop

We said that we will not be using a UI layout for our game screen, but instead a dynamically drawn view. This is where the view of our pattern comes in. Let's create a new class to represent our view, then we will put in the fundamental building blocks of our Tappy Defender game.

Building the view

We will leave our two activity classes alone for a while so that we can take a look at our class that will represent the view of our game. As we discussed at the start of this chapter, the view and the controller aspects will be part of the same class.

The Android API provides us with an ideal class for our requirements. The android.view.SurfaceView class not only provides us a view that is designed for drawing pixels, text, lines, and sprites onto, but also enables us to quickly handle player input as well.

As if this wasn't useful enough, we can also spawn a thread by implementing the runnable interface allowing our main game loop to get player input and other system essentials at the same time. We will deal with the general structure of your new SurfaceView implementation now, so we can fill in the details as we progress with the project.

Creating a new class for the view

Without further delay, we can create a new class which extends SurfaceView.

  1. Right-click the folder containing our .java files and select New | Java Class then click on OK.
  2. In the Create New Class dialog, name the new class TDView, (Tappy Defender view). Now, click on OK to have Android Studio autogenerate the class.
  3. The new class will open in the code editor. Amend the code to have it extend SurfaceView and implement Runnable as discussed in the previous section. Edit the highlighted parts of the code that follows:
    package com.gamecodeschool.c1tappydefender;
    
    import android.view.SurfaceView;
    
    public class TDView extends SurfaceView implements Runnable{
    
    }
  4. Use the Alt | Enter combination to import the missing classes.
  5. Note that we still have an error in our code. This is because we must provide a constructor for our SurfaceView implementation. Right-click just below the TDView class declaration and navigate to Generate | Constructor | SurfaceView(Context:context). Or you can just type this in as shown in the next block of code. Now click on OK.
What we did

We now have a new class called TDView, which extends SurfaceView for our drawing requirements and implements Runnable for our threading needs. We have also generated a constructor, which we will use soon to initialize our new class.

The Context parameter that is passed into our constructor is a reference to the current state of our application within the Android system that is held by our GameActivity class. This Context parameter is useful/essential for a number of things that we will be implementing throughout this project.

So far, our TDView class will look like this:

package com.gamecodeschool.c1tappydefender;

import android.content.Context;
import android.view.SurfaceView;

public class TDView extends SurfaceView implements Runnable{

    public TDView(Context context) {
        super(context);
    }
}
Structuring the class code

Now that we have our TDView class extended from the SurfaceView class, we can start coding it. To control the game, we need to be able to update all the game data/objects. This implies an update method. In addition, we are obviously going to want to draw all our game data once every frame after they have been updated. Let's keep all of our drawing code together in a method called draw. Furthermore, we need to control the frequency with which this happens. Therefore, a control method seems like it should be part of the class as well.

We also know that everything needs to happen in your thread; so to achieve this, we should wrap the code in the run method. Lastly, we need a way to control when the thread should and shouldn't do its work so we need an infinite loop controlled by a Boolean, perhaps, playing.

Copy the following code into the body of our TDView class to implement what we just discussed:

@Override
    public void run() {
        while (playing) {
            update();
            draw();
            control();
        }
    }

This is the bare-bones of our game. The run method will execute in a thread, but it will only execute the game loop while the Boolean playing instance is true. Then, it will update all the game data, draw the screen based on that game data, and control how long it is until the run method is called again.

Now, we can quickly build on this code. First of all, we can implement the three methods that we call from the run method. Type the following code in the body of our TDView class before the closing curly brace of the run method:

private void update(){
        
}
    
private void draw(){
        
}
       
private void control(){
        
}

We now need to declare our playing member variable. We can do this using the volatile keyword as it will be accessed from outside the thread and from within. Type this code just after the TDView class declaration:

volatile boolean playing;

Now, we know that we can control the execution of code within the run method with the infinite loop and the playing variable. We also need to start and stop the actual thread itself. Not just when we decide, but when the player unexpectedly quits the game. What if he gets a phone call or just taps the home button on his device.

To handle these events, we need the TDView class and GameActivity to work together. Now, in the TDView class, we can implement a pause method and a resume method. Within them, we put the code to stop and start our thread. Implement these two methods within the body of the TDView class:

// Clean up our thread if the game is interrupted or the player quits
public void pause() {
        playing = false;
        try {
            gameThread.join();
        } catch (InterruptedException e) {

        }
    }

    // Make a new thread and start it
    // Execution moves to our R
    public void resume() {
           playing = true;
           gameThread = new Thread(this);
           gameThread.start();
    }

Now, we need an instance of a Thread class called gameThread. We can declare it as a member variable of TDView just after the class declaration, right after our Boolean playing parameter. Like this:

volatile boolean playing;
Thread gameThread = null;

Note that the onPause and onResume methods are public. We can now add code to our GameActivity class to call these methods at the appropriate time. Remember that GameActivity extends Activity. Therefore, use the overridden Activity lifecycle methods.

By overriding the onPause method, whenever the activity is paused, we can shut down the thread. This avoids potentially embarrassing the player and having to explain to his caller, why they can hear sound FX in the background.

By overriding onResume(), we can have our thread start up in the last phase of the Android lifecycle before the app is actually running.

Note

Note the distinction between the pause and resume methods of the TDView class and the overridden onPause and onResume methods of the GameActivity class.

The game activity

Before you implement/override this method, note that all they will do is call the parent version of their respective methods followed by the public methods in the TDView class to which they correspond.

You might remember back to the section when we created our new GameActivity class, we deleted the entire code contents? With that in mind, here is the outline of the code we will need in GameActivity.java including the implementation of the overridden methods within the body of the GameActivity class that we discussed in the previous section. Type this code in GameActivity.java:

package com.gamecodeschool.c1tappydefender;

import android.app.Activity;
import android.os.Bundle;

public class GameActivity extends Activity {

    // This is where the "Play" button from HomeActivity sends us
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        
    }

    // If the Activity is paused make sure to pause our thread
    @Override
    protected void onPause() {
        super.onPause();
        gameView.pause();
    }

    // If the Activity is resumed make sure to resume our thread
    @Override
    protected void onResume() {
        super.onResume();
        gameView.resume();
    }

   
}

Finally, let's go ahead and declare an object of the TDView class. Do this just after the GameActivity class declaration:

// Our object to handle the View
private TDView gameView;

Now, within the onCreate method, we need to instantiate your object, keeping in mind that your constructor in TDView.java takes a Context object as an argument. Then, we use the newly instantiated object in a call to setContentView(). Remember when we built our home screen, we called setContentView() and passed in our UI design. This time, we are setting the player's view to be the object of our TDView class. Copy the following code into the onCreate method of the GameActivity class:

// Create an instance of our Tappy Defender View (TDView)
// Also passing in "this" which is the Context of our app
gameView = new TDView(this);

// Make our gameView the view for the Activity
setContentView(gameView);

At this point, we can actually run our game and click on the Play button to proceed to the GameView activity, which will use TDView as its view and start our thread. Obviously, there is nothing to see yet, so let's work on the model of our design pattern and build the basic outline of our first game object. At the end of the chapter, we will see how to run the game on an Android device.