The bottom line
Giving each modifier its own small script that inherits from one shared base class lets a game handle over 100 perks with a single clean loop. It makes every perk easy to find, change, test and add, and the same pattern works for enemies, guns and upgrades.
Key takeaways
- Checking every modifier with if statements inside one score script does not scale. 2:30
- Each new modifier makes the script longer, so finding and fixing things gets harder.
- Changing how modifiers work in general could mean editing the same code in 100 different if statements.
- Each modifier should be its own tiny script with a function the main score script can call. 4:55
- Trigger order then follows the player's modifier list, not the order of the code, which mattered for Balatro-style play.
- The score script only needs a list of owned modifiers and one loop.
- A shared base class makes an arbitrary list of scripts safe to loop through. 5:27
- The abstract ModifierClass has a virtual onWordScored that returns -1 by default, meaning "nothing happened, move on".
- The vowel modifier inherits from it and overrides the function to return the score times 1.5 when the first letter is a vowel.
- The same pattern extends to every trigger in the game. 9:32
- A base-class bool OnUpgradeUsed returns false by default, so modifiers that ignore upgrades do nothing and still work with the rest.
- Triggers cover selling a modifier, losing a life, rerolling perks, reaching a new round and more, and upgrades use an OnTileDropped function in the same way.
- Inheritance suits any game with many things that share core behavior but differ in details. 24:24
- Enemies could share movement, health and death code, with a unique override for each attack pattern.
- Guns could share aiming and reloading code, with unique special abilities.
So this is the power of inheritance, a really useful way of working with classes.
Mark Brown
Chapter summaries
0:00 Word Play's perks and why the system matters
Modifiers are one of three perk types, and the game needs a system that scales to 120 of them.
- Modifiers are passive perks that pay off under the right conditions. 0:58
- Word Play is a vocabulary roguelike where players spell words to score points.
- Gifts add letters to the bag and upgrades improve tiles.
- Modifiers work like Slay the Spire relics or Balatro Jokers.
- An example is multiply the Final Score by 1.5 if the first letter is a vowel.
- Modifiers also touch refreshes, bonus rounds, special tiles and the basic rules of spelling.
- The examples use Unity, but the idea should apply to pretty much any engine.
2:41 The first version: inline checks
| Inline if statements | One script per modifier | |
|---|---|---|
| Where logic lives | One ever-growing score script | A tiny script per modifier |
| Tweak one modifier | Hunt through all the code | Open its own script |
| Change how all work | Edit ~100 if statements | Edit the base class once |
| Trigger order | Order in the code | Player's modifier list |
| Score script | Spaghetti of ifs | One short loop |
The simplest way to score a modifier is to test for it directly inside the scoring code.
- The prototype checked ownership and the condition in one place. 3:12
- The code first adds up the points on the submitted tiles.
- Each modifier is a separate GameObject, and a list records which ones the player owns.
- If the player owns "VowelFirst" and the first letter is a vowel, the score is multiplied by 1.5.
- This is exactly how the scrappy game jam prototype worked, and it did the job.
- Changing it to the LAST letter means hunting through all the code to find it.
- Order barely matters in Word Play, but Balatro made it a requirement for this system.
4:55 One script per modifier, and the need for a shared type
A list of scripts can only be looped through if they all share one class.
- Each modifier script takes the word and its score and returns a new score. 5:00
- It checks its own condition and multiplies the score if the condition is met.
- It passes the result back to the main score script.
- The main script loops through the owned scripts, calling onWordScore on each.
- Asking for a list of ten items and then saying eat them fails if one item is a motorbike.
- Every item in the list must be the same type of class. 5:55
- Every Unity script already inherits from MonoBehaviour.
- That inheritance gives scripts handy functions like OnCollisionExit, OnMouseOver and OnDestroy.
- The same idea lets all modifiers share one parent class.
6:15 Building the base class and the vowel modifier
A custom abstract base class gives every modifier the same function, so one list can hold them all.
- The base class sets a safe default that means "do nothing". 6:42
- The shared function returns -1 unless a modifier overrides it.
- Scores are never negative in Word Play, so -1 is free to mean "move on".
- The function takes in the word and the current word score.
- It must be marked virtual so child scripts can override it.
- Each modifier overrides only the behavior that makes it unique. 7:14
- mVowelFirst inherits from ModifierClass and checks whether the first letter is A, E, I, O or U.
- If it is a vowel, it returns the word score times 1.5. Otherwise it returns -1.
- Without an override, it would just use the base version and return -1.
- The score script becomes one short loop. 8:16
- Every script in the list is guaranteed to have OnWordScored, so the loop is safe.
- Any result other than -1 updates the score, and can trigger a ping animation and sound.
- The loop runs in the player's modifier order until the calculation ends.
- The result is one loop instead of 100 if statements, with each modifier in its own script.
9:32 Extending the pattern to the rest of the game
The same base class handles every trigger and upgrade, and it suits any game with many similar things.
- New triggers are added as extra functions on the base class. 9:42
- OnUpgradeUsed returns false by default, so modifiers that ignore it stay untouched.
- The "use upgrade" code loops through owned modifiers and calls OnUpgradeUsed on each.
- One example override raises its bonus points whenever an upgrade is used.
- Other triggers include losing a life, game over, opening the perk screen, refreshing tiles and adding a tile to the letter bag.
- Each modifier script holds both its logic and its data. 10:44
- It has a few lines of code plus its icon, description, rarity and special numbers.
- Because the data is in the Unity inspector, changing numbers is easy.
- Upgrades use the same inheritance idea. 10:59
- Each upgrade overrides OnTileDropped from its base class.
- Overrides can destroy the tile, turn it into an emerald tile or raise its score.
- The system let the developer add, remove, debug and balance more than 150 perks in Word Play.
In the growth library
Mark Brown, creator of Game Maker's Toolkit and developer of Word Play. Shows how to build a roguelike perk system so that each of over 100 passive modifiers is its own small script built on a shared base class, instead of one giant block of if statements. The worked example is Word Play's 'first letter is a vowel, multiply score by 1.5' modifier, which grows into a system of triggers across the whole game.
More from Game Maker's Toolkit · The whole library, and ask it anything