Game content as data: Hydro's ScriptableObjects
Dialogue, items, quests and enemies moved out of C++ into reflected definition files and tables, edited in the inspector.
By the end of October the RPG had NPCs, items, heroes, enemies and quests, all written in C++. NPC dialogue was one if (name == "Farmer") block per character. Every new line of dialogue meant editing code, rebuilding and restarting. Interiors and more villages would multiply that, and save games need content with lasting IDs ("has 2 potions, talked to the elder").
Unity has ScriptableObjects; Unreal has Data Assets and Data Tables. Hydro now has definitions and data tables.
The model
- Types are C++ structs marked
HY_DATA, with fields markedHY_PROPERTY, plusHY_ENUMenums. A generator produces their reflection at build time. - A definition (
.hydef) is one record: a hero, a party setup. - A table (
.hytable) is rows by ID: enemies, loot, quests, NPC lines. - Both are YAML on disk, assets in the asset manager, and edited in the inspector like any component, including nested structs, lists and enums.
Game code reads them with Data::Get<T>(handle) or Data::GetRow<T>(rowRef). A missing file or row gives the type's defaults and one warning, never a crash.
Moving content without touching scenes
NPCs needed dialogue from data, but adding a script to every NPC in every scene would have been a lot of churn. Instead, an NPC's lines are found in Npcs.hytable by the name in its tag, with a Default row for anyone not listed. Not one scene file changed. Signs, quest lines and each scene's lighting presets became data the same way.
Tested without the engine
The generator, the serializer, the lookups and the editing operations have headless tests that run without a window or GPU, in CI and in an agent's sandbox. A data bug shows up as a failing test, not as an empty dialogue box during play.
What it unlocked
Adding a hero is now a row in a table. Battle balancing happens in the inspector. And because every item, quest and enemy has a lasting ID, save games could store state by ID. That's what made the game session and saving possible a day later.