During development of a character combat game, it is important to be able to materialize and iterate on ideas quickly. Some games use XML files, others use Microsoft Excel, and games like Balatro and Hades 2 are written mostly in Lua (to the delight of their modding communities). Overwatch uses a visual programming language called "Statescript", described in Dan Reed's GDC 2017 talk (alt link), and it is a particularly exciting example of how such a system can be designed. According to the talk, Statescript is used to manage all the game logic, animations, and sounds that comprise a hero ability (including their primary fire weapons), as well as things like HUD UI elements (ex: ultimate meter, character portrait, health bar, ability icons).
A whirlwind primer on finite state machines
Feel free to skip this section if you are familiar with the term "finite state machine"
Note that for every node (that is not an ending node) always transitions to exactly one another node. For example, the
conditions of the outgoing edges of node C
are a bit weird, but no matter what the values of u
and w
are, C
will
always transition to only one of F
, B
, or D
:
u | w | !u (to F ) | u && w (to B ) | u && !w (to D ) | node | ||
---|---|---|---|---|---|---|---|
โ | โ | โ | โ | โ | F | ||
โ | โ | โ | โ | โ | D | ||
โ | โ | โ | โ | โ | F | ||
โ | โ | โ | โ | โ | B |
Now that we're on the same page about what a finite state machine is, it's time for something completely different because:
Actually, I don't want a finite state machine
Though finite state machines are one of the most common types of state machines in the context of programming, they aren't the only one. In the aforementioned Statescript, for example, multiple states can be "active" at once, and the flow of states interacts with the "tick" timing system used to synchronize game logic in Overwatch. Loosening the rules like this allows for a more flexible system and (hopefully) cleaner state machines.
Current design
- Each spell/skill is its own state machine containing:
- states
- attributes - in-game variables turning speed, position
- resources - skill-specific variables such as ammo and fire rate, as well as timers
- States can transition between each other
- States can be triggered to be "active" even without a preceding state
- When activated, states perform actions, such as starting a timer or dealing damage
Here is an extremely simple example of a "dash" skill that moves the player quickly in the direction they are currently facing:
// skill
"dash": ,
Note that all the variables are prefixed with dash::
. Variables are all global, and the prefix serves as a sort of
namespace so that the dash "cooldown" timer doesn't interfere with the shoot "cooldown" timer.
next time
why is ammo a non-integer value
meters
implementation details:
- colliders
- variables
- serialization
- stats (move speed, etc)
Considering wiping the slate and reimplementing it as Lua scripts. depends on how much i need the Lua functionality, and how consise the scripts end up looking.