Game Data Design

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.