官术网_书友最值得收藏!

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.

主站蜘蛛池模板: 灌南县| 衡阳市| 大城县| 陕西省| 株洲市| 阜平县| 吉安县| 西平县| 五指山市| 阿合奇县| 繁昌县| 乐平市| 郓城县| 栾川县| 遂平县| 普陀区| 焉耆| 进贤县| 黔南| 松原市| 阳朔县| 祁东县| 恩施市| 祁东县| 安徽省| 任丘市| 中卫市| 郁南县| 泰州市| 城市| 仲巴县| 无棣县| 那坡县| 闻喜县| 葫芦岛市| 新和县| 乳山市| 专栏| 雷波县| 长子县| 玉林市|