Data-Driven Design — code for the shape, data for the values
Every game in this almanac hit the same wall and broke through it the same way. The farming
game's stage 04: a turnip and a potato that "differ
only in data — textures, days per stage, yield." The survivors game's stage 03: a Golem variant that's a species because its numbers live
on EnemyStats, not in the controller. Its stage 07:
an upgrade that's a .tres file, and "the entire upgrade system's content: two files, zero
code." The 3D farm's stage 05: the carrot
added with zero code changes, and that sentence is the design's acceptance test.
The pattern is one, and it has three names depending on which edge you're looking at: the definition/state split (what a kind is versus what this one is), the catalogue (the roster of definitions the game can look up), and data-driven (the property that adding content is a file operation, not a code operation). This shelf is the general treatment of the thing the curricula each used a slice of.
What this shelf teaches#
| Part | You build | Right when |
|---|---|---|
| 01 | The @export family, and the test that decides where a number lives | You have a hardcoded constant and a second instance that should differ |
| 02 | The definition/state split, the never-mutate rule, and the three containers | A "kind" has more than one number, and instances of it must disagree |
| 03 | The roster + index, ids as StringName, the bad-id warning | The game needs to find a definition by name at runtime |
| 04 | The fences — enums, versioned data, the save seam — and where data-driven stops | A designer (or future-you) will change the data without touching the code |
The rule the shelf keeps returning to#
If two things of the same kind could ever disagree about a value, it's state, not
definition. The 3D farm's version, in full: days_grown is per-plant (two turnips,
planted a day apart, disagree) so it lives on Plot; days_per_stage is per-kind (every
turnip agrees) so it lives on CropData. The line between the two is the whole design, and
every bug this shelf's parts meet is a value that crossed the line in the wrong direction —
state on a definition (all the turnips grow at once) or definition on state (a number that
should be data is welded into a script).
What data-driven actually buys you#
- Content is a file. The third crop, the fourth upgrade, the second enemy species — each
is a
.tres(or a scene variant), reviewed in a diff the way code is, and addable by someone who's never read the controller. The shape is code; the values are content. - The inspector is a design surface. A weapon's
@export_subgroup("Weapon Stats")block (the survivor's stage 05) reads as a stat sheet. You can look at the game's balance without opening a script, which is the difference between "the numbers are in the code" and "the numbers are in the game." - Saving is a walk. The 3D farm's stage 07: serialize
_plots— cell to{crop id, stage, days_grown}— and the save is the data layer, not the scene tree. The save & load shelf's opening audit passes because the truth is data; it fails the day a value crosses the line into a visual.
What it doesn't buy you#
Data-driven is not a plugin system, and the moment it's reached for as one, it's stopped
being a design tool and started being an architecture. A WeaponUpgrade with an
upgrade_type enum is data — the set of upgrade kinds is closed, and a new kind is a new
enum value plus a match branch, which is code, by design. A "upgrade that runs a script"
is a system, and it deserves a system's review, a version, and a failure mode. Part 04 is
the fence between the two, and it's the part most likely to save you from yourself.
Prerequisites#
- Custom
Resources — the definition is aclass_name … extends Resource. The farming game's stage 04 builds the first one; the programming shelf's typed GDScript covers theclass_nameand typed-array rules this shelf stands on. - Signals — the definition/state split earns its keep when observing the state is a
signal (
Inventory.changed, the inventory shelf). A data model with no observers is a data model you have to remember to re-read. - The curricula are the test cases; this shelf is the treatment. Read a part after you've met the pattern in a stage and it's a generalization; read it before and it's a map of where the stages are going.
Where this shows up in the projects#
The 3D farm is the shelf's cleanest instance —
CropData / Plot / Crop is the definition/state/view triple in its purest form, and
stage 05's zero-code carrot is part 02's acceptance test run in a real project.
The survivors game is the shelf at combat scale —
EnemyStats (part 01's per-kind numbers), WeaponUpgrade (part 02's immutable definition
applied to mutable state), the weapon's upgrade_list and the farm's crop_catalogue (part
03's roster), and stage 07's upgrade_type enum (part 04's fence).
The farming game is the origin — CropData as
the first custom Resource in the almanac, and stage 06's Inventory as the first
observer of a data layer. The inventory shelf is
that stage's general treatment; this shelf is the pattern that stage and inventory both sit
on.