Book Image

Mastering Android Game Development

By : Raul Portales
Book Image

Mastering Android Game Development

By: Raul Portales

Overview of this book

Table of Contents (18 chapters)
Mastering Android Game Development
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
API Levels for Android Versions
Index

Putting everything together


Let's pick up our stub project, add all the classes we need to have a working game engine, and then modify the code so it allows us to start, stop, pause, and resume the game engine and display the number of milliseconds since the game was started.

We will put our current implementation of GameEngine, UpdateThread, DrawThread, and GameObject inside the com.example.yass.engine package.

Next, we will create another package named com.example.yass.counter, which we will use for the code of this example.

Inside YassActivity, we have an inner class named PlaceholderFragment. We are going to rename it to GameFragment, refactor it to a separate file, and put it under the com.example.yass.counter package.

We are going to add a TextView that will show the number of milliseconds and two buttons: one to start and stop the game engine and another one to pause and resume it.

We are going to add them to the layout of fragment_yass_main.xml, which will look like this:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical"
  android:padding="@dimen/activity_horizontal_margin"
  android:paddingLeft="@dimen/activity_horizontal_margin"
  tools:context="com.example.yass.counter.PlaceholderFragment">

  <TextView
    android:id="@+id/txt_score"
   android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/hello_world" />

  <Button
    android:id="@+id/btn_start_stop"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/start" />

  <Button
    android:id="@+id/btn_play_pause"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/pause" />
</LinearLayout>

For the game fragment, we need to add the following code inside onViewCreated:

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
  super.onViewCreated(view, savedInstanceState);
  mGameEngine = new GameEngine(getActivity());
  mGameEngine.addGameObject(
    new ScoreGameObject(view, R.id.txt_score));
  view.findViewById(R.id.btn_start_stop)
    .setOnClickListener(this);
  view.findViewById(R.id.btn_play_pause)
    .setOnClickListener(this);
}

Once the view is created, we create the game engine and add a new ScoreGameObject to it. Then we set the current fragment as the listener for the two buttons we have added.

The code for onClick is very simple; just decide which method to call for each button:

@Override
public void onClick(View v) {
  if (v.getId() == R.id.btn_play_pause) {
    playOrPause();
  }
  if (v.getId() == R.id.btn_start_stop) {
    startOrStop();
  }
}

Deciding whether the game should be paused or resumed is as simple as this:

private void playOrPause() {
  Button button = (Button)
  getView().findViewById(R.id.btn_play_pause);
  if (mGameEngine.isPaused()) {
    mGameEngine.resumeGame();
    button.setText(R.string.pause);
  }
  else {
    mGameEngine.pauseGame();
    button.setText(R.string.resume);
  }
}

We also handle a name change on the button to make sure the UI is consistent. In the code, we are making use of the isPaused method from GameEngine. This method just returns the status of the UpdateThread object as long as it is not null:

public boolean isPaused() {
  return mUpdateThread != null && mUpdateThread.isGamePaused();
}

Similarly, to play/pause the game and keep the state of the buttons, we will add this method:

private void startOrStop() {
  Button button = (Button)
    getView().findViewById(R.id.btn_start_stop);
  Button playPauseButton = (Button)
    getView().findViewById(R.id.btn_play_pause);
  if (mGameEngine.isRunning()) {
    mGameEngine.stopGame();
    button.setText(R.string.start);
    playPauseButton.setEnabled(false);
  }
  else {
    mGameEngine.startGame();
    button.setText(R.string.stop);
    playPauseButton.setEnabled(true);
    playPauseButton.setText(R.string.pause);
  }
}

Once again, we need a method in the GameEngine to know whether it is running or not. As we did for the previous one, we just mirror the status of UpdateThread:

public boolean isRunning() {
  return mUpdateThread != null && mUpdateThread.isGameRunning();
}

Once the basic connections are done, we can move to the really interesting bit: the game object we are creating. This object illustrates the use of each method from the GameObject class that we have been talking about:

public class ScoreGameObject extends GameObject {

  private final TextView mText;
  private long mTotalMilis;

  public ScoreGameObject(View view, int viewResId) {
    mText = (TextView) view.findViewById(viewResId);
  }

  @Override
  public void onUpdate(long elapsedMillis, GameEngine gameEngine)
  {
    mTotalMilis += elapsedMillis;
  }

  @Override
  public void startGame() {
    mTotalMilis = 0;
  }

  @Override
  public void onDraw() {
    mText.setText(String.valueOf(mTotalMilis));
  }
}

The onUpdate method just keeps adding milliseconds to the total. The total is reset when a new game starts and onDraw sets the value of the total number of milliseconds in the text view.

As expected, onUpdate is called a lot more often than onDraw. On the other hand, onDraw is executed on the UIThread, which is something we cannot afford to do with onUpdate.

We can now compile and run the example and check that the timer starts and stops when we start and stop the game engine. We can also check that pause and resume work as expected.