Happy new year!
How time flies! Apart from a Christmas vacation, and some other personal things that happened, I have been working quite regularly on Share Wars. What took so long, was a classic case of totally under estimating the amount of work in the tasks I set for myself. That’s one of the things I like about game development, that you can never become fully educated. Even after 25 years, I still mess up.
The tasks I gave myself, were simple enough:
- Establish a new corporation. Show it in the corporations list, add company logo to the tiles.
- Design and implement a popup for choosing which corporation to establish.
Before I implement new features, I always make some sketches and notes to get a better overview of the work, and to lay a simple plan. I never fully plan out every detail, because in my experience, it’s actually faster to get started working and refactor when needed. During the first phase, I try to write as much of the code in one file, to save time on jumping between files. And if I get to a point where it’s a total mess, I use git to discard all changes and start over. What I learn about the feature I’m making is more important than the code.
Inspired by something John Romero once said, I always try to keep the game in a playable state, and if I find something that doesn’t work, I fix it immediately. That way of working has really helped me write better and more stable code.
As I was implementing the corporations, I found so many things that didn’t work. The things I have fixed resulted in:
- Better handling of loading previous game session
- A more organized state API
- More strict data flow
- Simplication of game architecture
- Standardized handling of players, regardless of human or NPC
- A more stable game mode
I have learnt a lot of lessons, but I think the most useful for others, are:
Player classes belongs to a game mode
For a while, I thought the player classes could be generalized and reused between game modes. For context, a game mode is a specific way of playing the game. It could be a time challenge mode, local multiplayer, maybe some rules are relaxed for practice, etc. But the more I have worked on this, the more I see the player classes as an extension of the game mode. As an extension of the game mode, it’s completely fine to put game mode related code in a player class.
A well defined data flow leads to more a stable game
It’s a lot easier to identify bugs and to understand why they happen, and also how to fix them, when you can internalize a mental image of the data flow. It can be hard to get the data flow right on the first try. As you keep working on the code, always be mindful of who’s allowed to read and write state data, and when. Over time, you’ll see patterns that can help you streamline the data flow.
A simple and strict architecture makes it easier to separate engine code from gameplay scripts
With the architecture that Share Wars has now, I can easily take most of the core classes out of gdscript and into a C++ extension, and use gdscript for scripting of gameplay, game modes and player scripts. The rules of the game, the state data and config files are locked inside an extension. The benefits of putting the core code in a C++ extensions are better performance, improved type safety and true private functions.
New architecture and data flow
The new architecture of Share Wars looks like this:
The rules class is gone. Game mode and Game state has taken over the responsibility of the old rules class.
Game mode is responsible for creating the player instances.
Game state is one class with all the data. For organizational purposes, the state API is divided into smaller classes, grouped by responsibility. For instance, there is a PlayerAPI class which exposes functions for looking up players, check who’s turn it is, getting the player’s info, etc.
The new data flow looks like this:
State API’s are the only ones who are allowed to write to the State.
When some of the state changes, certain signals are emitted out to the Game Board and UI. Board and UI are responsible for displaying the game to the local player.
Game Mode is in charge of the player classes and is allowed to write to them. Players emits signals back to game mode, to let mode know about when a player is done with its turn, and other events.
The human player is the only player who is allowed to write to the UI. This is because that player class is only a proxy for the human. Without the UI or the game board, the human player is not capable of making informed decisions. NPC’s only need access to the state API’s to make sense of the game.
You can think of this map as the rules for how data flows. If I wanted to read from state directly in a game mode, that would be a vioation of the rules. In that case, I might have to extend the state API with a new function.
The next steps
For the next update, I will focus on:
- Merge corporations that grow into each other
- Make the test NPC actually do some trading of shares