Unity is a commercial game engine using a component-based architecture with C# scripting. GameObjects hold Components that define behavior. Unity supports 2D and 3D, with rendering pipelines (URP, HDRP, Built-in), a mature asset store, and broad platform export.
Unity's core pattern is Entity-Component: GameObjects are containers, Components add behavior.
Key concepts:
Typical hierarchy:
Scene
├── --- MANAGERS ---
│ ├── GameManager
│ ├── AudioManager
│ └── UIManager
├── --- ENVIRONMENT ---
│ ├── Terrain / Tilemap
│ ├── Props
│ └── Lighting
├── --- ENTITIES ---
│ ├── Player
│ │ ├── PlayerController
│ │ ├── PlayerInput
│ │ ├── Health
│ │ ├── Rigidbody
│ │ └── Collider
│ └── Enemies
├── --- UI ---
│ ├── Canvas (HUD)
│ └── Canvas (Menus)
└── --- CAMERAS ---
└── Main Camera + Cinemachine
Understanding execution order is critical for architecture:
Awake() → Called once when object instantiates (before Start)
OnEnable() → Called when object/component enabled
Start() → Called once before first Update (after all Awake)
FixedUpdate() → Called every physics step (default 50Hz)
Update() → Called every frame
LateUpdate() → Called every frame after all Updates
OnDisable() → Called when object/component disabled
OnDestroy() → Called when object destroyed
Rules:
Awake() for self-initialization (setting up internal references)Start() for cross-object initialization (finding other objects)FixedUpdate() for physics and movementLateUpdate() for camera follow, UI updates after gameplayScript Execution OrderSplit code into assemblies for faster compilation and clean dependency boundaries:
Assets/
├── Scripts/
│ ├── Core/
│ │ ├── Core.asmdef → Core systems, no game dependencies
│ │ ├── EventSystem.cs
│ │ └── ServiceLocator.cs
│ ├── Gameplay/
│ │ ├── Gameplay.asmdef → References: Core
│ │ ├── Player/
│ │ └── Combat/
│ ├── UI/
│ │ ├── UI.asmdef → References: Core, Gameplay
│ │ └── Screens/
│ └── Infrastructure/
│ ├── Infrastructure.asmdef → References: Core
│ ├── SaveSystem.cs
│ └── NetworkManager.cs
└── Tests/
├── EditMode/
│ └── EditModeTests.asmdef → References: Core, Gameplay
└── PlayMode/
└── PlayModeTests.asmdef → References: Core, Gameplay
Benefits:
UI.asmdef don't recompile Core or GameplayTrade-offs on larger projects:
ScriptableObjects are Unity's most powerful data pattern:
[CreateAssetMenu(fileName = "NewWeapon", menuName = "Game/Weapon Stats")]
public class WeaponStats : ScriptableObject
{
[Header("Base Stats")]
public float baseDamage = 10f;
public float attackSpeed = 1f;
public float range = 5f;
[Header("Scaling")]
public AnimationCurve damageFalloff;
public float DPS => baseDamage * attackSpeed;
public float GetDamageAtRange(float distance)
{
float t = Mathf.Clamp01(distance / range);
return baseDamage * damageFalloff.Evaluate(t);
}
}
Use ScriptableObjects for:
ScriptableObject Event Channel pattern:
[CreateAssetMenu(menuName = "Events/Void Event")]
public class VoidEvent : ScriptableObject
{
private readonly List<VoidEventListener> _listeners = new();
public void Raise()
{
for (int i = _listeners.Count - 1; i >= 0; i--)
_listeners[i].OnEventRaised();
}
public void Register(VoidEventListener listener) => _listeners.Add(listener);
public void Unregister(VoidEventListener listener) => _listeners.Remove(listener);
}
// PREFERRED: GetComponent cached in Awake
private Health _health;
private Rigidbody _rb;
private void Awake()
{
_health = GetComponent<Health>();
_rb = GetComponent<Rigidbody>();
}
// AVOID: GetComponent in Update (expensive)
void Update()
{
GetComponent<Health>().TakeDamage(1); // BAD
}
// For optional dependencies
private void Awake()
{
if (TryGetComponent<AudioSource>(out var audio))
_audioSource = audio;
}
// PREFERRED: Private with SerializeField (encapsulated but Inspector-visible)
[SerializeField] private float moveSpeed = 5f;
[SerializeField] private GameObject bulletPrefab;
[SerializeField] private Transform firePoint;
// Use Header and Tooltip for organization
[Header("Movement")]
[SerializeField] private float acceleration = 10f;
[SerializeField] private float deceleration = 15f;
[Header("Combat")]
[Tooltip("Seconds between attacks")]
[SerializeField, Range(0.1f, 5f)] private float attackCooldown = 0.5f;
// AVOID: public fields for serialization
public float speed = 5f; // Exposes internal state
// Coroutines: good for frame-based sequences
private IEnumerator SpawnWave(int count, float interval)
{
for (int i = 0; i < count; i++)
{
SpawnEnemy();
yield return new WaitForSeconds(interval);
}
}
// Async: good for I/O, network, complex async logic
// Requires UniTask (any Unity version) or Awaitable (Unity 2023.1+)
private async Awaitable LoadLevelAsync(string levelName)
{
var op = SceneManager.LoadSceneAsync(levelName);
while (!op.isDone)
{
OnLoadingProgress?.Invoke(op.progress);
await Awaitable.NextFrameAsync();
}
}
// Awaitable is built-in since Unity 2023.1 (also called Unity 6 in later branding):
await Awaitable.WaitForSecondsAsync(1f);
await Awaitable.NextFrameAsync();
// For Unity 2022 LTS and earlier, use UniTask instead
// Simple service locator pattern (no framework needed)
public static class Services
{
private static readonly Dictionary<Type, object> _services = new();
public static void Register<T>(T service) where T : class
=> _services[typeof(T)] = service;
public static T Get<T>() where T : class
=> _services[typeof(T)] as T;
}
// Register in bootstrap scene
Services.Register<IAudioManager>(audioManager);
Services.Register<ISaveSystem>(saveSystem);
// Or use VContainer / Zenject for complex projects
Assets/
├── Art/
│ ├── Sprites/
│ ├── Models/
│ ├── Materials/
│ ├── Textures/
│ ├── Animations/
│ └── Shaders/
├── Audio/
│ ├── Music/
│ ├── SFX/
│ └── Mixers/
├── Prefabs/
│ ├── Characters/
│ ├── Environment/
│ ├── UI/
│ └── VFX/
├── Scenes/
│ ├── Boot.unity
│ ├── MainMenu.unity
│ └── Levels/
├── Scripts/
│ ├── Core/
│ ├── Gameplay/
│ ├── UI/
│ └── Infrastructure/
├── Data/ # ScriptableObject instances
│ ├── Items/
│ ├── Enemies/
│ └── Config/
├── Resources/ # Only for runtime-loaded assets
├── StreamingAssets/ # Platform-specific raw files
├── Plugins/ # Native plugins
└── Tests/
├── EditMode/
└── PlayMode/
Naming conventions:
PascalCase (e.g., PlayerController.cs, MainMenu.unity)PascalCasePascalCase_camelCase with underscore prefixcamelCasePascalCase or UPPER_SNAKE_CASEIPascalCase prefixFind() at runtime — cache references in Awake()/Start()Instantiate/Destroy in gameplay — pool insteadnew, string concatenation, LINQ, or foreach on non-List collectionsCompareTag() not tag == (avoids string allocation)Async scene loading:
private IEnumerator LoadSceneAsync(string sceneName)
{
AsyncOperation op = SceneManager.LoadSceneAsync(sceneName);
op.allowSceneActivation = false;
while (op.progress < 0.9f)
{
loadingBar.value = op.progress;
yield return null;
}
// Ready to activate
op.allowSceneActivation = true;
}
Span<T> and stackalloc for temporary buffers (advanced)NativeArray<T> with Jobs system for parallel computationobjectStringBuilder for string building in loops| Package | Purpose | Source |
|---|---|---|
| DOTween | Tweening and animation | Asset Store (free) |
| UniTask | Zero-allocation async/await | OpenUPM / GitHub |
| NaughtyAttributes | Inspector enhancement (buttons, labels) | OpenUPM / GitHub |
| Odin Inspector | Advanced Inspector/serialization | Asset Store (paid) |
| VContainer | Lightweight DI container | OpenUPM |
| Package | Purpose | Source |
|---|---|---|
| Cinemachine | Camera system (Unity package) | Package Manager |
| Input System | Modern input handling (Unity package) | Package Manager |
| ProBuilder | In-editor level prototyping (Unity package) | Package Manager |
| Yarn Spinner | Dialogue and branching narrative | OpenUPM / GitHub |
| Ink | Narrative scripting language | GitHub + Unity integration |
| Package | Purpose | Source |
|---|---|---|
| Shader Graph | Visual shader authoring (Unity package) | Package Manager |
| VFX Graph | GPU particle system (Unity package) | Package Manager |
| FMOD for Unity | Professional audio middleware | FMOD website (free for indie) |
| Master Audio | Audio management suite | Asset Store (paid) |
| Feel / Nice Vibrations | Game juice and haptic feedback | Asset Store |
| Package | Purpose | Source |
|---|---|---|
| Netcode for GameObjects | Unity's official networking | Package Manager |
| Photon Fusion/Quantum | Hosted multiplayer solution | Photon website |
| Mirror | Open-source networking (Fork of UNet) | Asset Store (free) |
| FishNet | High-performance open-source networking | Asset Store (free) |
| Steamworks.NET | Steam API integration | GitHub |
| Package | Purpose | Source |
|---|---|---|
| Addressables | Async asset management (Unity package) | Package Manager |
| Unity Test Framework | NUnit-based testing (Unity package) | Package Manager |
| Zenject/Extenject | Full DI framework | OpenUPM |
| R3 (Reactive Extensions) | Reactive programming (successor to UniRx) | NuGet / GitHub |
| MessagePipe | High-performance pub/sub messaging | NuGet / GitHub |
Unity now ships its own AI tooling — the Unity AI Assistant — alongside an official MCP (Model Context Protocol) server that exposes the Editor to external AI clients such as Claude Code and Cursor. With Unity MCP connected, an agent can manage scenes, run asset operations, edit scripts, read the console, and automate in-editor tasks directly instead of working from chat descriptions.
Enable it under Edit > Project Settings > AI > Unity MCP. The bridge starts with the Editor, and direct external connections require your approval. Overview docs: https://docs.unity3d.com/Packages/com.unity.ai.assistant@2.8/manual/integration/unity-mcp-overview.html.
Cost caveat: Unity MCP is part of Unity AI, which needs a paid Unity AI subscription (around $10/month — verify current terms, since Unity's AI pricing has changed more than once). Prefer the free, MIT-licensed open-source servers unless you already pay for Unity AI:
CoderGamester/mcp-unity) — 30+ tools for scenes, GameObjects, components, materials, and the Test Runner.CoplayDev/unity-mcp) — natural-language editor control with fast batch operations; supports Unity 2021.3 LTS and up.Both cover the same workflow as the official server at no cost. The engine MCP catalogue (engine-mcps.yaml) has full capability lists and install steps.
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
}
}
Use sparingly. Prefer ScriptableObject events or dependency injection for decoupling.
public class ObjectPool<T> where T : Component
{
private readonly Queue<T> _pool = new();
private readonly T _prefab;
private readonly Transform _parent;
public ObjectPool(T prefab, int initialSize, Transform parent = null)
{
_prefab = prefab;
_parent = parent;
for (int i = 0; i < initialSize; i++)
_pool.Enqueue(CreateInstance());
}
public T Get()
{
T obj = _pool.Count > 0 ? _pool.Dequeue() : CreateInstance();
obj.gameObject.SetActive(true);
return obj;
}
public void Return(T obj)
{
obj.gameObject.SetActive(false);
_pool.Enqueue(obj);
}
private T CreateInstance()
{
T obj = Object.Instantiate(_prefab, _parent);
obj.gameObject.SetActive(false);
return obj;
}
}
Use a lightweight entry scene that initializes systems before loading gameplay:
// Boot.unity scene with single BootLoader GameObject
public class BootLoader : MonoBehaviour
{
[SerializeField] private string firstSceneName = "MainMenu";
private async void Start()
{
// Initialize systems
await InitializeSaveSystem();
await InitializeAudio();
InitializeAnalytics();
// Load first real scene
SceneManager.LoadScene(firstSceneName);
}
}
| Pipeline | Use Case | Trade-offs |
|---|---|---|
| URP | Most projects, mobile, 2D, stylized 3D | Good performance, extensible, growing feature set |
| HDRP | High-fidelity 3D, PC/console | Best visuals, highest hardware requirements |
| Built-in | Legacy projects only | Deprecated for new projects, limited future support |
Default recommendation: URP for all new projects unless targeting AAA-quality visuals on high-end hardware.
Unity uses a subscription model with tiers based on revenue:
Important cost considerations:
Unity may be the wrong choice when:
link.xml to preserve types. Metal shader compilation adds to build timeTask.Run). Memory is limited by browser tab. Many .NET APIs unavailable. Build sizes can be large — enable compressionWhen choosing Unity for a project, verify: