Ben Traje
← Back to unity

Unity C# Architecture: Utility Scripts vs. MonoBehaviours

11 Sep 26 (1mo ago)

In Unity, the beginner trap of attaching a MonoBehaviour to a GameObject for every piece of logic creates a messy, inefficient hierarchy. Professional architecture separates the "hands" (MonoBehaviours interacting with the scene) from the "brain" (pure C# classes handling logic).

Script Types & When to Use Them

Script TypeBest ForNeeds GameObject?Access Method
MonoBehaviourPhysics, Visuals, Coroutines, Inspector tweakingYesGetComponent<T>() / Inspector reference
Static UtilityStateless math, formatting, damage formulasNoDirect call (e.g., LevelMath.Calc())
Plain C# (POCO)Per-instance data (Inventories, AI states)NoInstantiation (e.g., new Inventory())
ScriptableObjectShared game data, configurations, rulesetsNo (Asset)Inspector reference

Core Advantages of Pure C# Logic

  • Instant Access: Call static methods instantly, eliminating the need for performance-heavy GameObject.Find() or GetComponent() calls.
  • Scene Independence: Pure C# scripts exist independently in memory and aren't destroyed when scenes unload, making them perfect for persistent logic.
  • Better Performance: You avoid forcing Unity to manage the overhead of GameObjects and hidden lifecycle callbacks (like empty Update() methods).
  • Cleaner Hierarchy: Keeps your Unity Editor hierarchy focused purely on physical and visual entities.

How Access Works (The "Static" Way)

One of the biggest quality of life upgrades you experience when you stop making everything a MonoBehaviour is frictionless communication. When a script is a Static Utility, it exists globally in your project’s memory. You don't need to use GameObject.Find, GetComponent, or drag-and-drop references in the Inspector to talk to it.

If you define a class or a method as static, it belongs to the type itself, not a specific object in your scene.

Script TypeHow to Access itRequirement
MonoBehaviourmyObject.GetComponent<MyScript>().DoSomething();Must be attached to a GameObject in the scene.
Static UtilityMyUtil.DoSomething();Just the script file in your project.

Example: Stateless Utility Script

Utility scripts are pure C# classes defined with the static keyword. They are instantly accessible globally because you are talking directly to the script file itself.

public static class LevelMath 
{
    // Called from anywhere via: LevelMath.CanLevelUp(current, required)
    public static bool CanLevelUp(int currentXP, int requiredXP) 
    {
        return currentXP >= requiredXP;
    }
}

The Golden Rule: Watch Your State

The biggest risk with static utility scripts is shared state.

If you declare a static variable (e.g., public static int health), there is only one global copy. Using this for an enemy's health means hitting one enemy damages every enemy in the game. Additionally, static variables can unexpectedly wipe during Unity Domain Reloads (such as entering Play Mode).

Best Practice: Keep static classes strictly stateless (methods only, no variables). Manage stateful data via instantiated Plain C# Objects (POCOs) managed by a central controller, or leverage ScriptableObjects.