This was a series of tweets.
https://twitter.com/mevdev/status/1318779823951507456
Game Data Design is something I learned best by doing. I’ve released 4 games on the app store (only one is still available). I’ve gotten to a playable game with 4 others. I am by no means an expert but I feel like I’ve learned a lot and now know how it works best for me.
Have a model that holds your data and state. Make it separated from your UI so it changes its own state. Build for serialization (save/open). Write and rewrite in an evolutionary manner. Make the UI an engine that reacts to and drives your model’s data and state.
A good model will ease all scenarios making bugs easier to fix but also the ability to save/resume a game in any state. Practice good naming. Make variable names verbose if that describes them well. Do not reuse terms for unrelated items. Be a bit silly at times.
I start by roughing out a model and to understand state. Understanding data transformations and calculations will suss out player objects vs the model. What holds what, which direction should communication flow. How is a turn kept track of.
This is my current game’s state enumeration:
enum State {
case startingGame,
rolling,
rolledAwaitingSelection,
selectedRoll,
awaitingStopOrContinue,
awaitingLocalPlayerRoll,
busted,
win, // network game only
waitingOnNetworkPlayers
}
// My main model variables:
struct GameModel {
var players: [Player]
var state: State
//MARK: Potential and Selection Calculated vars
var currentSelections: [DiePairing]
var gameBoardPotentialKeys: [Int]
var availableNumbers: [Int]
Model manipulation and UI interaction
func isAvailableNumber(_ num: Int) -> Bool
// MARK: - Mutation for States mutating
func rolling()
mutating func selectedRoll(_ pairing: [Int])
mutating func stopSelected()
mutating func currentPlayerRoll()
// Player turn management
// MARK: Turn Management
func nextPlayerIndex() -> Int
func nextPlayer() -> Player
mutating func continueToNextPlayer()
mutating func continueSelected() -> Bool
mutating func didSomeoneJustWin() -> Player?
// MARK: State Observation
func isWinningState() -> Bool
func isBusted() -> Bool
}
I know when the model is changed & how it changes state. It holds an array of Players which encapsulate their own data (If I were on a team more would be private).
// The Player manages its board, potential board & win state. I allow the UI to introspect in to draw.
struct Player {
var gameBoard: [Int: Int]
var gameBoardPotential: [Int: Int]
var hasWon: Bool
func winningRowsCount() -> Int
func winningRows() -> [Int]
// Then this last bit changes the state of Player
mutating func addPotentialDie(_ die: Die) {
mutating func convertPotential()
mutating func convertPotentialToGameBoard()
}
Maybe this isn’t the best way but it currently works for me.
Thanks for playing!
I realized after writing that ‘func convertPotential()’ was an exact duplicate and not called. It was more elegant though so I tested it then used that. I should be making a TestFlight build next week for testing. Also all of my structs and enums conform to Codable btw.