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

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.

主站蜘蛛池模板: 扬中市| 永宁县| 轮台县| 东方市| 新密市| 南靖县| 神农架林区| 延长县| 通江县| 芷江| 磴口县| 宁海县| 荥阳市| 大名县| 常山县| 平和县| 景宁| 兖州市| 高平市| 莱阳市| 安新县| 东山县| 襄垣县| 邯郸县| 曲周县| 凤城市| 岢岚县| 松原市| 永兴县| 金湖县| 勃利县| 丹巴县| 定兴县| 米泉市| 十堰市| 泰顺县| 化德县| 博罗县| 新民市| 柳州市| 治多县|