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:
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:
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:
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:
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.