unity-architecture
from cryptorabea/claude_unity_dev_plugin
No description
npx skills add https://github.com/cryptorabea/claude_unity_dev_plugin --skill unity-architectureSKILL.md
Unity Game Architecture
Essential architectural patterns and design principles for scalable, maintainable Unity projects.
Overview
Good architecture separates concerns, reduces coupling, and makes code testable and maintainable. This skill covers proven patterns for Unity game development.
Core architectural concepts:
- Manager patterns and global systems
- ScriptableObject-based data architecture
- Event-driven communication
- Component composition patterns
- Dependency management
Manager Pattern
Centralized systems that coordinate game-wide functionality.
Singleton Manager
Most common pattern for global managers:
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);
}
public void StartGame() { }
public void PauseGame() { }
public void EndGame() { }
}
// Access from anywhere
public class Player : MonoBehaviour
{
private void Start()
{
GameManager.Instance.StartGame();
}
}
When to use:
- Game state management (GameManager)
- Audio management (AudioManager)
- Input management (InputManager)
- Save/load systems (SaveManager)
- UI management (UIManager)
When NOT to use:
- Everything (avoid "singleton hell")
- Temporary systems
- Systems that need multiple instances
Generic Singleton Base
Reusable singleton pattern:
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
private static T instance;
public static T Instance
{
get
{
if (instance == null)
{
instance = FindObjectOfType<T>();
if (instance == null)
{
GameObject singleton = new GameObject(typeof(T).Name);
instance = singleton.AddComponent<T>();
DontDestroyOnLoad(singleton);
}
}
return instance;
}
}
protected virtual void Awake()
{
if (instance == null)
{
instance = this as T;
DontDestroyOnLoad(gameObject);
}
else if (instance != this)
{
Destroy(gameObject);
}
}
}
// Usage
public class GameManager : Singleton<GameManager>
{
protected override void Awake()
{
base.Awake();
// Additional initialization
}
}
Manager Initialization Order
Control manager initialization:
// Use Script Execution Order:
// Edit > Project Settings > Script Execution Order
// Or explicit initialization
public class GameBootstrap : MonoBehaviour
{
private void Awake()
{
InitializeManagers();
}
private void InitializeManagers()
{
// Initialize in specific order
var saveManager = SaveManager.Instance;
var audioManager = AudioManager.Instance;
var gameManager = GameManager.Instance;
// Managers initialize in Awake, but access here ensures order
}
}
Best practice: Use explicit initialization scene or bootstrapper.
Service Locator Pattern
Alternative to singleton for dependency injection:
public class ServiceLocator
{
private static ServiceLocator instance;
public static ServiceLocator Instance => instance ?? (instance = new ServiceLocator());
private readonly Dictionary<Type, object> services = new Dictionary<Type, object>();
public void RegisterService<T>(T service)
{
services[typeof(T)] = service;
}
public T GetService<T>()
{
if (services.TryGetValue(typeof(T), out var service))
{
return (T)service;
}
throw new Exception($"Service {typeof(T)} not found");
}
}
// Register services
public class GameBootstrap : MonoBehaviour
{
private void Awake()
{
var audioManager = new AudioManager();
ServiceLocator.Instance.RegisterService(audioManager);
var saveManager = new SaveManager();
ServiceLocator.Instance.RegisterService(saveManager);
}
}
// Access services
public class Player : MonoBehaviour
{
private void Start()
{
var audio = ServiceLocator.Instance.GetService<AudioManager>();
audio.PlaySound("Jump");
}
}
Benefits over singleton:
- Testable (inject mock services)
- No static dependencies
- Clear dependencies
Drawbacks:
- More setup code
- Runtime dictionary lookup
- Less discoverable
ScriptableObject Architecture
Data-driven design using ScriptableObjects.
ScriptableObject Data Containers
Store data separate from behavior:
[CreateAssetMenu(fileName = "WeaponData", menuName = "Game/Weapon Data")]
public class WeaponData : ScriptableObject
{
public string
...