Chapter 9. The Game Engine, Threads, and The Game Loop

During this chapter we will see the game engine come together. By the end of the chapter, we will have an exciting blank screen that draws debugging text at 60 frames per second on a real device, although, probably less on an emulator. While doing so we will learn about programming threads, try-catch blocks, the Runnable interface, Android Activity lifecycle and the concept of a game loop.

My expression of excitement for a blank screen might seem sarcastic but, once this chapter is done we will be able to code and add game objects, almost at will. We will see how much we have achieved in this chapter when we add the moving ball and controllable bat in the next. Furthermore, this game engine code will be used as an approximate template (we will improve it each project) for future projects making the realization of future games faster and easier.

In this chapter, we will be:

  • Coding the PongGame class
  • Coding the draw method and a few more besides
  • Learning about threads and try-catch blocks
  • Implementing the game loop

Let's get started.

Coding the PongActivity class

In this project, as discussed previously, we will have multiple classes. Four to be exact. The Activity class provided by the Android API is the class that interacts with the operating system. We have already seen how the OS interacts with onCreate when the player clicks the app icon to start an app (or our game). Furthermore, we have seen how the operating system calls the onTouchEvent method when the user interacts with the screen, giving us the opportunity to make our game respond appropriately.

As this game is more complicated and needs to respond in real-time it is necessary to use a slightly more in-depth structure. At first this seems like a complication but in the long run, it makes our code more simple and easy to understand.

Rather than have a class called Pong (analogous to SubHunter) that does everything, we will have a class which just handles start-up and shutdown of our game as well as help a bit with initialization by getting the screen resolution. It makes sense that this class will be of type Activity.

However, as you will soon see, we will delegate interacting with touches to another class, the same class that will also handle almost every aspect of the game- kind of like a game engine. This will introduce us to some interesting concepts that will be new to us.

Let's get started with coding the Activity-based class. We called this class PongActivity and it was auto-generated for us when we created the project in the previous chapter.

Add the first part of the code for the PongActivity class.

import android.app.Activity;
import android.graphics.Point;
import android.os.Bundle;
import android.view.Display;

public class PongActivity extends Activity {

    private PongGame mPongGame;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Display display = getWindowManager().getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);

        mPongGame = new PongGame(this, size.x, size.y);
        setContentView(mPongGame);
    }
}

Notice it all looks very familiar. In fact, except for the mPongGame object of type PongGame all the code in the previous block, we saw the same in the Sub' Hunter project. I won't go through again how we get the screen resolution with display, size, and getSize. I will go through in full detail what we are doing with this new object. It might look quite strange at first.

The first new thing is that we are declaring an instance of our PongGame class. Currently, this is an empty class.

private PongGame mPongGame;

In the previous code, we begin using the m prefix for member variables. This is a code formatting convention to avoid getting confused about the scope of our variables. Also notice it is declared as a member should be, outside of any methods.

Tip

Now that we will be creating lots of classes, including with constructors and methods with parameters, prefixing m to the start of all member variables will avoid getting confused about which variables belong to the instance of the class and which only have scope within a method.

In onCreate after we have done our usual thing with display etc, we initialize mPongGame like this

mPongGame = new PongGame(this, size.x, size.y);

What we are doing is passing three arguments to the PongGame constructor. We have obviously not coded a constructor and as we know the default constructor takes zero arguments. Therefore, this line will cause an error until we fix this soon.

The arguments passed in are interesting. First, this, which is a reference to PongActivity. The PongGame class will need to perform actions (use methods) that it needs this reference for.

The second and third arguments are the horizontal and vertical screen resolution. It makes sense that our game engine (PongGame) will need these to perform tasks like detecting the edge of the screen and scaling the other game objects to an appropriate size. We will discuss these arguments further when we get to coding the PongGame constructor.

Next look at the even stranger line that follows

setContentView(mPongGame);

This is where in the SubHunter class we set the ImageView as the content for the app. Remember that the Activity class' setContentView method must take a View object and ImageView is a View. This previous line of code seems to be suggesting that we will use our PongGame class as the visible content for the game? But PongGame isn't a View. Not yet anyway.

We will fix the constructor and the not-a-View problem after we add a few more lines of code to PongActivity.

Note

Reader challenge

Can you guess which OOP concept the solution might be?

Add these two overridden methods and then we will talk about them. Add them below the closing curly brace of onCreate but before the closing curly brace of PongActivity.

@Override
protected void onResume() {
   super.onResume();

   // More code here later in the chapter
}

@Override
protected void onPause() {
   super.onPause();

   // More code here later in the chapter
}

What we have done is to override two more of the methods of the Activity class. We will see why we need to do this, what we will do inside these methods and just as important, when Android will call these methods, later in this chapter. The point to note here is that by adding these overridden methods we are giving the OS the opportunity to notify us of the player's intentions in two more situations. What exactly resuming and stopping entails will be fully explained later in this chapter.

It makes sense at this point to move on to the PongGame class, the main class of this game. We will come back to PongActivity near the end of the chapter.

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

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