I just finished designing and implementing a state based game using polymorphism.
Using a base abstract class called GamePhase
that has one important method
abstract public GamePhase turn();
What this means is every GamePhase
object holds the current state of the game, and a call to turn()
looks at its current state and returns the next GamePhase
.
Each concrete GamePhase
has constructors that hold the entire game state. Each turn()
method has a little bit of the game rules inside them. While this spreads the rules around, it keeps related rules close together. The end result of each turn()
is just creating the next GamePhase
and passing in the full state into the next phase.
This allows turn()
to be very flexible. Depending on your game a given state can branch to many different types of phases. This forms a graph of all game phases.
At the highest level the code to drive it is very simple:
GamePhase state = ...initial phase
while(true) {
// read the state, do some ui work
state = state.turn();
}
This is extremely useful as I can now easily create any state/phase of the game for testing
Now to answer the second part of your question, how does this work in multiplayer? Within certain GamePhase
s that require user input, a call from turn()
would ask the current Player
their Strategy
given the current state/phase. Strategy
is just a interface of all the possible decisions a Player
can make. This setup also allows Strategy
to be implemented with AI!
Also Andrew Top said:
Your game can probably be in a (close to) infinite amount of states because of the permutations of things like how much money player A has, how much money player B has, and etc... Therefore, I'm pretty sure you want to stay away from state machines.
I think that statement is very misleading, while it is true that there are a lot of different game states, there are only a few game phases. To handle his example, all it would be is an integer parameter to the constructors of my concrete GamePhase
s.
Monopoly
Example of some GamePhase
s would be:
- GameStarts
- PlayerRolls
- PlayerLandsOnProperty (FreeParking, GoToJail, Go, etc)
- PlayerTrades
- PlayerPurchasesProperty
- PlayerPurchasesHouses
- PlayerPurchasesHotels
- PlayerPaysRent
- PlayerBankrupts
- (All Chance and Community Chest cards)
And some states in the base GamePhase
are:
- List of Players
- Current Player (who's turn)
- Player's Money/Property
- Houses/Hotels on Properties
- Player Position
And then some phases would record their own state as needed, for example PlayerRolls would record the number of times a player has roll consecutive doubles. Once we leave the PlayerRolls phase, we don't care about consecutive rolls anymore.
A lot of phases can be reused and linked together. For example the GamePhase
CommunityChestAdvanceToGo
would create the next phase PlayerLandsOnGo
with the current state and return it. In the constructor of PlayerLandsOnGo
the current player would be moved to Go and their money would be incremented by $200.