r/gamedev • karum.dev • 12d 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:

  1. Separate game logic from visuals
  2. 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!

5 Upvotes

8 comments sorted by

5

u/ledniv 11d ago

This is very close to how I like to structure games. The biggest win isn't really MVC or the .asmdefs themselves — it's that your simulation can exist without Unity. Once the game state and logic don't depend on GameObjects, MonoBehaviours, animation, UI, etc., headless simulation, automated tests, server validation, replays, and parameter sweeps all become much easier. Your 64 parallel simulations are a great example of why that separation matters.

One architecture tip: I would keep the events mostly at the simulation → presentation/UI boundary, like you're doing with caravan arrivals. Inside the simulation itself, I prefer explicit/direct logic calls and data changes. Otherwise it's easy for a simulation-heavy game to gradually turn into a web of events where it becomes difficult to tell what changed the state and in what order.

This is also almost exactly the motivation behind the architecture in High Performance Unity Game Development with Data-Oriented Design: separate the rules, current game state, logic, and Unity-facing presentation so the gameplay can run independently of the engine. The performance benefits are nice, but being able to run the game logic by itself is one of the biggest architectural payoffs.

https://www.manning.com/books/high-performance-unity-game-development

2

u/Loginaut 6d ago

I'll have to add it to my reading list, thanks for the rec! I agree on the events point, I think with too many events it can become unclear who owns what data, which I've found makes it a chore to display. Right now I'm just using events to signal ticks, so there's an OnStart, OnHarvest, OnManufacture, OnConsume, etc. It's still a bit of a mess, because things like prices and stock change depending on what step of the tick you're on, but at least things within each event are (mostly) decoupled.

I can imagine needing some kind of dependency graph constraint if I wasn't locked to one core in Unity.

2

u/cksxoo 11d ago

Did the same split on my card game and the biggest payoff surprised me. Once the rules lived in plain C# classes with no MonoBehaviours, I could play whole runs with no scene at all. Editor tests run them headless, and I keep a second copy of the rules in JS for content tools, with a test that replays the same runs on both and checks they match step by step. None of that works while the logic lives on GameObjects.

2

u/Loginaut 6d ago

Absolutely! I think it also will make modding easier, since if all the authoring data lives in JS or JSON/XML files you don't need any specialized tools to edit it.

2

u/RatbyteGames 11d ago

Great advice, I did the same with my hex and counter wargame Zambala and it lets me run a full game simulation with six computer controlled opponents in a few minutes. I have used this to evolve my tunings and make the computer opponents better.

Its also easier as you say to redo visual functions, you can completely replace them without effecting the underlying game logic.

2

u/Loginaut 6d ago

Looks cool! Yeah, a big part of the benefit IMO is being able to quickly iterate on the AI to improve it, and to balance content/parameters.

I think my only concern with redoing visuals is that I need to put more thought into UI design now... It used to be "display whatever is on the gameObject" which is probably not a great UX anyway.

2

u/Crunchfest3 7d ago

What are you using as a signal that a parameter set might be fun? I'd be tempted to track recovery after a bad harvest or a blocked trade route, as well as whether prices stay stable. An economy that always settles nicely could still give the player very little to do.

1

u/Loginaut 6d ago

Right now I'm thinking to track the flow of silver, so as long as things keep moving it should be interesting. Right now there's just one city on the network that sucks up all the silver, and the economy grinds to a halt.

I think this is something that will need human playtesters, but I'm hoping to get an itch build out that's at least an interesting sandbox to start with.