Build every gameplay system as a self-contained UPM package. Data lives in ScriptableObjects, behavior lives in small single-responsibility MonoBehaviours, and systems talk through SO Event Channels — never direct references.
| One system = one UPM package | Create a "shared contracts" package with interfaces | | Data in ScriptableObjects, behavior in MonoBehaviours | Put data and behavior in the same class |
| MonoBehaviour [SerializeField] only for SO refs, component refs, scene refs, UnityEvents | [SerializeField] primitives (float, int, bool, string, LayerMask, enum, AnimationCurve) directly on MonoBehaviours — these belong in an SO Config asset | | SO Event Channels for cross-system communication | Use singletons, service locators, or static managers |
Use when building reusable Unity game systems, creating UPM packages, or designing ScriptableObject-based modular architecture for gameplay systems like inventory, combat, dialogue, quests, or save/load. Also use when connecting multiple independent Unity packages that need to communicate without direct dependencies. Source: eyenpi/unity-systems-skills.