Quick answer: Extract the types both assemblies need into a third “Core” or “Interfaces” assembly that both reference. Or invert one direction by raising events from one assembly that the other subscribes to. Define Constraints don’t fix this.
You add an asmdef to your Combat folder, another to UI, and reference each other. Compile fails: Assembly definition file Game.Combat references Game.UI, which references Game.Combat in a circular dependency. The fix is structural, not a flag flip.
The Symptom
Console error: “Assembly with name ‘Game.Combat’ has a circular reference with ‘Game.UI’.” All scripts in both assemblies become uncompilable. The Inspector shows red on every component.
What Causes This
Compile order. The C# compiler builds dependencies first; if A needs B and B needs A, neither can be compiled first. Unity rejects the configuration outright rather than picking arbitrarily.
The Fix Pattern: Extract Common Types
Most circular refs come from shared types. Solution: a Core or Interfaces assembly.
Game.Core // IDamageable, DamageEvent, GameSettings
Game.Combat -> Game.Core
Game.UI -> Game.Core
Game.Combat declares class Enemy : IDamageable. Game.UI accepts an IDamageable reference rather than an Enemy. Both reference Game.Core; neither references the other.
The Fix Pattern: Event Inversion
If Combat needs to tell UI “health changed”, inverting the dependency works:
// Game.Combat (no reference to UI)
public static class CombatEvents
{
public static event Action<int> OnHealthChanged;
public static void RaiseHealthChanged(int hp) => OnHealthChanged?.Invoke(hp);
}
// Game.UI (references Game.Combat)
public class HealthBar : MonoBehaviour
{
void OnEnable() => CombatEvents.OnHealthChanged += UpdateBar;
void OnDisable() => CombatEvents.OnHealthChanged -= UpdateBar;
}
UI references Combat (one-way). Combat publishes events; UI subscribes. Cycle broken.
Detecting Circular Refs Before They Happen
Window → Analysis → Project Auditor (Unity 2022+). It maps assembly dependencies as a DAG. Cycles light up in red. Run before merging branches that touch asmdef structure.
Or open the .asmdef JSON manually and trace references in a graph tool.
Per-Folder Hierarchy
A clean asmdef structure looks like a tree:
Game.Core (no deps)
Game.Common -> Core
Game.Audio -> Common
Game.UI -> Common
Game.Combat -> Common
Game.AI -> Combat, Common
Game.Editor -> Combat, UI, Common
(Editor-only assembly, never referenced by runtime)
Lower nodes know about higher ones; never the reverse. Where they need to communicate, they use events or interfaces declared in Core/Common.
Verifying
After refactor, force a recompile (right-click any script → Reimport). Console clears the circular reference error. If new errors appear referencing missing types, those are the cases where you accepted a concrete type and now need to swap to the interface.
“Extract the shared into Core. Invert with events. Build a tree, not a cycle.”
Related Issues
For asmdef compile slowdowns, see compile slow. For Editor-only references, see editor-only asmdefs.
Common assembly. Events. The DAG flows downhill.