Quick answer: Time every phase of your inner loop — save, import, compile, enter-playmode, load — and attack the slowest one. The common wins are assembly definitions or module boundaries to shrink compile scope, import presets and platform overrides to cut asset processing, and hot-reload for data so you don’t restart for every tweak.
Iteration time is the hidden tax on every game you ship. A developer who waits 90 seconds between saving and seeing a result makes maybe 40 focused changes a day. A developer who waits 8 seconds makes 200. Over a year, the difference is enormous — not just in raw throughput, but in the willingness to experiment. Nobody tries ten variations of a jump feel when each try costs two minutes.
Measure Before You Optimize
You can’t fix what you haven’t measured. Every engine has some profiling tool for the editor itself; use it. In Unity, the Profiler’s Editor mode shows time spent in domain reload, asset import, and shader compilation. In Unreal, turn on stat unit inside PIE and use the Insights tool for cook times. In Godot, the Profiler in debug builds reports frame breakdown, and --verbose startup logs show import timing.
Separate the phases: save-to-import, import-to-compile, compile-to-play, play-to-loaded. Time at least ten iterations for a typical change and write down the medians. You’ll usually find that one phase dominates, and attacking anything else is a waste of effort until the big one is dealt with.
Cut Compile Scope
Script compilation is the most common bottleneck. In Unity, the default setup recompiles the entire project on every change. Solve this with Assembly Definitions (.asmdef) files. Split your code into logical modules — gameplay, UI, networking, editor tools — with clear dependency boundaries. A change in a leaf module only recompiles that module and its dependents, not the world.
// Unity asmdef example — put in Assets/Scripts/Gameplay/
{
"name": "Studio.Gameplay",
"references": [
"Studio.Core",
"Studio.Audio"
],
"includePlatforms": [],
"autoReferenced": false
}
In Unreal, the equivalent is splitting your code across multiple modules in your .uproject. Keep cross-module dependencies minimal and prefer forward declarations over #include in headers. Unreal’s Live Coding system can patch C++ into a running editor in seconds if your module boundaries are clean.
Godot’s GDScript has almost-instant reloads; if you’re on C# in Godot 4, the compile is also generally fast but can be slowed by editor-side reflection scans. Minimize [Tool]-attributed scripts unless you need them.
Asset Import Triage
Asset imports are the second usual suspect. Every time you save a texture, mesh, or audio file, your engine processes it for the target platform. Death by a thousand cuts: each import is a few hundred milliseconds but they queue up.
Set import presets by folder. UI textures shouldn’t be compressed the same way as world textures; world textures shouldn’t use mipmaps if they’re always seen at native resolution. In Unity, use Preset assets and override platform settings. In Unreal, use Import Settings on source asset types. In Godot 4, configure importers via the Import dock with presets.
Audit your longest imports. In Unity, the Asset Import Pipeline v2 caches results, but a misconfigured importer — say, one that force-reimports on every platform switch — defeats the cache. The Cache Server (Unity Accelerator) shares the cache across your team, turning first-time imports into downloads.
Hot Reload Data
Even if you can’t hot-reload code, you can almost always hot-reload data. Tunable values — jump height, damage numbers, enemy stats — should live in data assets or JSON files that your game watches at runtime. Change the value, save, and see the result without restarting the level.
public class GameTuning : ScriptableObject {
[Range(1, 20)] public float jumpHeight = 6f;
[Range(0.1f, 3)] public float moveSpeed = 1f;
// Unity OnValidate fires in-editor whenever values change
void OnValidate() {
Messenger.Broadcast("TuningChanged");
}
}
For data driven entirely from external files (CSV or JSON, common for balancing spreadsheets), add a FileSystemWatcher in editor-only code that fires an event whenever the file changes. The editor rebuilds the game’s cached tables and broadcasts a reload event. This alone saves hundreds of restarts over the course of a balancing pass.
We once spent two weeks optimizing editor startup from 90 seconds to 25. Useful, but the change that actually transformed our team’s productivity was the day we could tweak enemy HP in a JSON file and watch bosses rebalance mid-fight.
Enter-Playmode Shortcuts
Unity’s Enter Play Mode Options (Project Settings > Editor) lets you skip domain reload and scene reload when entering play mode. Once your code is warmed up, entering play becomes nearly instantaneous. The catch is that static fields persist across plays, so any code relying on “fresh” statics needs explicit reset handlers. It’s worth the discipline.
Unreal’s PIE (Play In Editor) is much faster than a packaged build but slower than a VR-optimized mobile preview. Use the correct PIE mode: Standalone for networking tests, PIE for quick checks. Avoid Cook Content during iteration unless you’re debugging cook-specific bugs.
Runtime Code Injection
For the deepest iteration gains, invest in runtime code reloading. Unity’s Hot Reload plugin, Unreal’s Live Coding, and third-party tools like Roslyn CodeRunner all let you patch code into a running session. Set expectations: reloads won’t handle layout changes (new fields, changed signatures) reliably. Code them for expression-level tweaks — tuning numbers, tweaking logic paths — and fall back to stop-edit-play for structural changes.
Related Issues
For the asset-pipeline deep dive, see how to track and reduce asset reimport time. For related build-system topics, read best practices for error logging in game code.
A ten-second change is a hundred-second idea. A hundred-second change is the idea you never have.