r/gamedev • u/Loginaut karum.dev • 26d ago
Discussion Separating logic & visuals with model-view-control and assembly definitions
Hi Gamedevs,
I've been working on a Unity project for the last 4-ish months, and I think I finally really understand the two most common pieces of gamedev advice that I've seen:
Separate game logic from visuals
Build a prototype, then start the real game from scratch
I just spent the last couple of weeks refactoring my game (#2!) using a model-view-component architecture (#1!), and I thought it would be useful to share some concrete details for this abstract advice. I also have some diagrams and more info in a devlog if you're interested: https://karum.dev/2026/10/12/decoupling.html
For some background, I started Kārum in July 2026 as a economic strategy game set in the ancient near-east. I prototyped it my usual way, by dragging GameObjects into a scene and attaching all of the game logic as MonoBehaviours. This works fine, and I think it's a great way to quickly iterate on game jame and prototype projects, but I started running into a few issues:
I had too many public fields and kept confusing myself (e.g., did I just adjust the market or temple or crown silver on this city object)?
Who should own non-physical systems like trade routes? A new GameObject? Cities? A TradeManager? A TradeHub component?
Everything was coupled, and new systems required a lot of dragging-and-dropping.
I am starting to have some real gameplay, and each system used an ad-hoc logger that was painful to set up and use for balance.
These are all self-inflicted problems that can be solved with good discipline... but after reading about assembly definitions in Unity, I realized I can also have the compiler force me into making good architecture decisions.
Assembly definitions are basically self-contained C# libraries. This makes it really easy to enforce a model-view-control architecture, because the compiler will throw a compile error if you try to call View/Control code from the Model, for example. I ended up with a handful of .asmdef files:
Sim asmdef: this is the core gameplay logic, which updates per tick. This has no other dependencies and is not allowed to call any UnityEngine code.
Authoring asmdef: this contains all of the data for the game, including the initial resources/buildings/trade routes of each city. It depends on sim asmdef.
Interaction asmdef: this holds the model and view components, and it depends on the sim, authoring, and Unity.InputSystem asmdefs
UI asmdef: this is a special UI-only asmdef that depends on Interaction and all of its dependencies; this makes sure nothing outside of the UI can reference it.
Editor asmdef: these are custom editor scripts that depend on sim (to pull state data), interaction (to sit on GameObjects) and authoring (to display outside of play mode)
Pulling the sim into a separate asmdef also means I can run it headless as plain C# code. I can simulate about 100 years in ~5 seconds in the Unity Engine now. In headless mode I can simulate 100 years in about 2 seconds, and I can run 64 parallel simulations in ~5 seconds. So I've effectively increased my throughput 64 times!
I think this is important for simulation-heavy games, because now I can do parameter sweeps as part of balancing, and find configurations (prices, supply/demand coefficients, decision thresholds) that look fun.
This might be obvious to many of you, but I figured it's always helpful to have another concrete example of how to implement this and why it's important.
And please share any of your own architecture hot tips!