hot reloading game data at runtime — is anyone doing this cleanly or is it always a hack

302 views 10 replies

Been working on a combat system with a LOT of tunable numbers: enemy stats, ability costs, damage falloff curves, all of it. Tired of recompiling every time a designer (me, it's me, I'm the designer) tweaks a value. So I've been trying to set up proper hot reloading for game data at runtime and it's been a journey.

The approach I landed on: separate your data definitions from your runtime instances. Sounds obvious in retrospect. My mistake was binding ScriptableObject values directly to live game objects, so when you reload the SO, there's no clean way to propagate changes without restarting the scene or writing a ton of refresh logic scattered everywhere.

What actually worked: treat config data as a source of truth read at construction time, not a persistent live reference. Then a hot reload becomes:

fileWatcher.OnChanged += (path) => {
    var newData = JsonLoader.Load<EnemyConfig>(path);
    ConfigRegistry.Replace(path, newData);
    // existing instances don't auto-update — signal them instead
    EventBus.Emit(new ConfigReloadedEvent { Path = path });
};

The catch is now you're managing two things: the definition reload and notifying whatever live objects care about it. Gets messy fast once configs have dependencies on other configs.

I've seen people use Odin Inspector + ScriptableObjects with custom refresh buttons as a lighter shortcut, which honestly might be good enough for most projects. Full file-watcher hot reload only seems worth the architecture cost if you're iterating on balance data constantly throughout the day.

Is anyone running proper runtime hot reload in a Unity or Godot project, or is everyone just living with the compile-reload cycle?

reaction
Replying to ChronoMesh: the exclusive-open retry approach is smart but I'd add one thing: cap your retri...

the capped retry thing is so easy to skip and I learned it the same hard way. I'd also add: log a warning when you hit the cap. silent failure here is brutal because from the outside it looks like hot reload just stopped working, and you'll spend 20 minutes wondering if your FileSystemWatcher died before realizing there's a locked file sitting there from a crash 30 minutes ago. one log line saved me probably hours of future confusion.

the pattern that's worked cleanly for me: all tunable data lives in .toml files, and on startup I register a FileSystemWatcher on the data directory. change event fires, reload the file, push into a global config struct everything reads from.

the thing that makes it actually clean instead of a mess: a generation counter on the config struct. any system that caches a derived value checks the counter on access and re-pulls if stale. no callbacks, no pub/sub, no spaghetti. just "is my cached value from the current generation?"

works great for anything that's pure data with no side effects on load — damage tables, spawn weights, dialogue timing, curve parameters. basically free hot reload for all of it.

Replying to ChronoFlare: the pattern that's worked cleanly for me: all tunable data lives in .toml files,...

the FileSystemWatcher approach is solid but one thing that bit me hard: the watcher fires as soon as the file is opened for writing, not when it's closed and flushed. so if your designer saves a big TOML and your reload fires immediately, you're reading a half-written file and everything explodes.

fix I use: on the change event, wait 100-200ms before actually reading. feels gross but it works. alternatively debounce by holding a CancellationToken that you reset on every new event and only fire the reload when it actually expires.

Replying to VaporBolt: yeah the 'designers don't watch the console' thing is painfully accurate. i put ...

lol the flashing overlay is genuinely inspired. i did the same thing but made it a subtle yellow banner and designers kept ignoring it because it blended into the normal ui. making it aggressively obnoxious is the right call. subtle feedback gets dismissed every single time.

person completely ignoring loud alarm blaring
Replying to NebulaLark: the capped retry thing is so easy to skip and I learned it the same hard way. I'...

the silent failure thing is what got me too. i'd add: if you're in an editor context, pop a toast notification or an in-editor warning, not just a console log. designers who aren't watching the console will reload the level wondering why their numbers aren't changing and spend 20 minutes debugging before it occurs to them to check logs. learned this the embarrassing way lol

person looking around confused office
Replying to CipherFlare: lol the flashing overlay is genuinely inspired. i did the same thing but made it...

the yellow blending in is classic. did the same thing, tasteful little banner, very professional, completely invisible to anyone not actively looking for it. switched to a full-viewport red pulse on failure and the first reaction from everyone was "wait is something broken?" which is honestly exactly the right reaction to a reload failure. mission accomplished.

red alert siren spinning
Replying to IronVale: the premature-fire issue with FileSystemWatcher is so real and it bit me too. th...

the exclusive-open retry approach is smart but I'd add one thing: cap your retries. I had a case where a file got genuinely locked by a crashed process and my reload loop just silently spun forever eating CPU. now I do max 5 retries with exponential backoff and if it still fails I log a warning and move on. designer can just save again. not elegant but it doesn't hang the editor either.

Replying to TurboFern: the FileSystemWatcher approach is solid but one thing that bit me hard: the watc...

the premature-fire issue with FileSystemWatcher is so real and it bit me too. the retry loop approach works but I ended up doing something slightly different: when the event fires, I try to open the file with FileShare.None in a loop with a short sleep. if it throws IOException, the file's still locked, keep waiting. once it opens clean, then reload. feels more reliable than a fixed delay since the delay you need varies by editor and file size.

Replying to BinaryLeap: the silent failure thing is what got me too. i'd add: if you're in an editor con...

yeah the 'designers don't watch the console' thing is painfully accurate. i put a flashing red overlay in the corner of the game viewport that stays visible for 5 seconds on any reload failure — console log was just invisible to everyone who wasn't me. no one came to me with 'my stat changes aren't doing anything' ever again after that.

Replying to SolarFern: the yellow blending in is classic. did the same thing, tasteful little banner, v...

lmao the full-viewport red pulse is genuinely inspired. i tried the tasteful banner approach too and got the exact same result, designers walking over going "hey the game seems broken?" in a completely calm voice like that's a normal sentence to say. the moment i made the failure state aggressively obnoxious is the moment i stopped getting mystery bug reports that were just silent reload failures the whole time.

person casually ignoring fire alarm
Moonjump
Forum Search Shader Sandbox
Sign In Register