- Mastering Android Game Development
- Raul Portales
- 640字
- 2021-07-16 13:59:07
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.
- 在最好的年紀學Python:小學生趣味編程
- Computer Vision for the Web
- Developing Mobile Web ArcGIS Applications
- 實用防銹油配方與制備200例
- RTC程序設計:實時音視頻權威指南
- Instant 960 Grid System
- JavaScript+Vue+React全程實例
- Hands-On GPU:Accelerated Computer Vision with OpenCV and CUDA
- 大學計算機基礎(第2版)(微課版)
- INSTANT Django 1.5 Application Development Starter
- Getting Started with Laravel 4
- Go語言精進之路:從新手到高手的編程思想、方法和技巧(2)
- 智能手機APP UI設計與應用任務教程
- 汽車人機交互界面整合設計
- .NET Standard 2.0 Cookbook