The GameState
class has exactly the same role as in the previous project. Obviously, however, the details of the state in this project is different. In the previous project we had score, high score lasers and aliens; in this one we have time, multiple fastest times and which level is being played. The GameState
class also takes care of knowing (and sharing) the current state of paused, playing, drawing, thread running, etc.
Create a new class called GameState
and add the member variables and constructor as shown next.
import android.content.Context; import android.content.SharedPreferences; final class GameState { private static volatile boolean mThreadRunning = false; private static volatile boolean mPaused = true; private static volatile boolean mGameOver = true; private static volatile boolean mDrawing = false; private EngineController engineController; private int mFastestUnderground; private int mFastestMountains; private int mFastestCity; private long startTimeInMillis; private int mCoinsAvailable; private int coinsCollected; private SharedPreferences.Editor editor; private String currentLevel; GameState(EngineController gs, Context context) { engineController = gs; SharedPreferences prefs = context .getSharedPreferences("HiScore", Context.MODE_PRIVATE); editor = prefs.edit(); mFastestUnderground = prefs.getInt( "fastest_underground_time", 1000); mFastestMountains = prefs.getInt( "fastest_mountains_time", 1000); mFastestCity = prefs.getInt( "fastest_city_time", 1000); } }
We have Boolean variables to represent whether the game thread is running, player has paused, game is over or currently drawing. As in the previous project we also have an EngineController
reference so GameState
can reinitialize a game/level directly.
Next up we have three int
variables to hold the fastest time on each of the three levels. The startTimeInMillis
variable will be initialized each time a level is attempted to record the time the level was started so it is possible to calculate how long the level took.
There are two more int
members to hold the number of coins it is possible to collect in a level and the number of coins actually collected. They are mCoinsAvailable
and coinsCollected
.
The final two members in the previous code is an instance of SharedPrefferences.Editor
for writing new high scores and a String which will represent the current level to be played, City
, Underground
or Mountains
.
Now add this quite long list of getters and setters to the GameState
class.
void coinCollected() { coinsCollected++; } int getCoinsRemaining() { return mCoinsAvailable - coinsCollected; } void coinAddedToLevel() { mCoinsAvailable++; } void resetCoins() { mCoinsAvailable = 0; coinsCollected = 0; } void setCurrentLevel(String level) { currentLevel = level; } String getCurrentLevel() { return currentLevel; } void objectiveReached() { endGame(); } int getFastestUnderground() { return mFastestUnderground; } int getFastestMountains() { return mFastestMountains; } int getFastestCity() { return mFastestCity; }
A detailed description of each of the methods we just added would be somewhat laborious because they each do just one thing.
It is however, well worth closely inspecting each method's name to aid understanding as we proceed.
Next, add three more methods to the GameState
class for starting a new game, getting the current time and taking action when the player dies.
void startNewGame() { // Don't want to be handling objects while // clearing ArrayList and filling it up again stopEverything(); engineController.startNewLevel(); startEverything(); startTimeInMillis = System.currentTimeMillis(); } int getCurrentTime() { long MILLIS_IN_SECOND = 1000; return (int) ((System.currentTimeMillis() - startTimeInMillis) / MILLIS_IN_SECOND); } void death() { stopEverything(); SoundEngine.playPlayerBurn(); }
The startNewGame
method calls the stopEverything
method. We will code the stopEverything
method soon. The next line of code uses the GameController
reference to call the startNewLevel
method on the GameEngine
class. Once the startNewLevel
method has done its work we call the startEverything
method (which we will code soon) to get things going again. The reason we do these three steps is because otherwise we will be trying to update and draw objects at the same time as the GameEngine
is also deleting and reinitializing them. This would be bound to cause a crash. The last thing we do in startNewGame
is initialize the startTimeInMillis
variable with the current time.
The getCurrentTime
method shares the current time. Note that it takes the start time from the current time and divides the result by one thousand. This is because we want the player to see their time in seconds not milliseconds.
The death
method simply calls stopEverything
and then uses the SoundEngine
to play a death sound.
Now code the endGame
method and then we will discuss it.
private void endGame() { stopEverything(); int totalTime = ((mCoinsAvailable - coinsCollected) * 10) + getCurrentTime(); switch (currentLevel) { case "underground": if (totalTime < mFastestUnderground) { mFastestUnderground = totalTime; // Save new time editor.putInt("fastest_underground_time", mFastestUnderground); editor.commit(); } break; case "city": if (totalTime < mFastestCity) { mFastestCity = totalTime; // Save new time editor.putInt("fastest_city_time", mFastestCity); editor.commit(); } break; case "mountains": if (totalTime < mFastestMountains) { mFastestMountains = totalTime; // Save new time editor.putInt("fastest_mountains_time", mFastestMountains); editor.commit(); } break; } }
The first line of code in endGame
calls stopEverything
so the game engine will halt updating and drawing.
Next, a local variable, totalTime
is declared and initialized. The total time (as you might remember from Chapter 22: Platform Game: Bob was in a hurry section) is calculated by the total time the player took added to a penalty for each coin that the player failed to collect.
Next in the endGame
method we enter a switch
block where the condition is the currentLevel String
. The first case
is when the underground
level has been played. The code uses an if
statement to check if totalTime
is less than mFastestUnderground
. If it is then the player has a new fastest time. The editor.putInt
and editor.commit
methods of the SharedPrefferences.Editor
instance are then used to save the new record for posterity.
The next two case
blocks do the same thing for the city
then the mountains
levels.
Now code the final methods for the GameState
class.
void stopEverything() {// Except the thread mPaused = true; mGameOver = true; mDrawing = false; } private void startEverything() { mPaused = false; mGameOver = false; mDrawing = true; } void stopThread() { mThreadRunning = false; } boolean getThreadRunning() { return mThreadRunning; } void startThread() { mThreadRunning = true; } boolean getDrawing() { return mDrawing; } boolean getPaused() { return mPaused; } boolean getGameOver() { return mGameOver; }
The final getters and setters control when the game engine does certain tasks like start/stop the thread and call update
and/or draw
. Familiarize yourself with their names and which members they interact with.
Let's make some noise.
3.133.156.251