Unity Architecture by cryptorabea/claude_unity_dev_plugin
npx skills add https://github.com/cryptorabea/claude_unity_dev_plugin --skill 'Unity Architecture'适用于可扩展、可维护 Unity 项目的核心架构模式与设计原则。
良好的架构能够分离关注点、降低耦合度,并使代码易于测试和维护。本技能涵盖经过验证的 Unity 游戏开发模式。
核心架构概念:
协调游戏全局功能的集中式系统。
全局管理器最常用的模式:
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() { }
}
// 从任何地方访问
public class Player : MonoBehaviour
{
private void Start()
{
GameManager.Instance.StartGame();
}
}
适用场景:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
不适用场景:
可复用的单例模式:
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);
}
}
}
// 使用方式
public class GameManager : Singleton<GameManager>
{
protected override void Awake()
{
base.Awake();
// 额外的初始化
}
}
控制管理器初始化顺序:
// 使用脚本执行顺序:
// Edit > Project Settings > Script Execution Order
// 或显式初始化
public class GameBootstrap : MonoBehaviour
{
private void Awake()
{
InitializeManagers();
}
private void InitializeManagers()
{
// 按特定顺序初始化
var saveManager = SaveManager.Instance;
var audioManager = AudioManager.Instance;
var gameManager = GameManager.Instance;
// 管理器在 Awake 中初始化,但在此处访问可确保顺序
}
}
最佳实践:使用显式初始化场景或引导程序。
依赖注入的单例替代方案:
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");
}
}
// 注册服务
public class GameBootstrap : MonoBehaviour
{
private void Awake()
{
var audioManager = new AudioManager();
ServiceLocator.Instance.RegisterService(audioManager);
var saveManager = new SaveManager();
ServiceLocator.Instance.RegisterService(saveManager);
}
}
// 访问服务
public class Player : MonoBehaviour
{
private void Start()
{
var audio = ServiceLocator.Instance.GetService<AudioManager>();
audio.PlaySound("Jump");
}
}
相比单例的优势:
缺点:
使用 ScriptableObjects 的数据驱动设计。
将数据与行为分离存储:
[CreateAssetMenu(fileName = "WeaponData", menuName = "Game/Weapon Data")]
public class WeaponData : ScriptableObject
{
public string weaponName;
public int damage;
public float fireRate;
public Sprite icon;
public GameObject projectilePrefab;
}
public class Weapon : MonoBehaviour
{
[SerializeField] private WeaponData data;
public void Fire()
{
Instantiate(data.projectilePrefab, firePoint.position, firePoint.rotation);
}
public int GetDamage() => data.damage;
}
优势:
适用于:
使用 ScriptableObjects 的事件系统:
[CreateAssetMenu(fileName = "GameEvent", menuName = "Events/Game Event")]
public class GameEvent : ScriptableObject
{
private readonly List<GameEventListener> listeners = new List<GameEventListener>();
public void Raise()
{
for (int i = listeners.Count - 1; i >= 0; i--)
{
listeners[i].OnEventRaised();
}
}
public void RegisterListener(GameEventListener listener)
{
if (!listeners.Contains(listener))
listeners.Add(listener);
}
public void UnregisterListener(GameEventListener listener)
{
listeners.Remove(listener);
}
}
public class GameEventListener : MonoBehaviour
{
[SerializeField] private GameEvent gameEvent;
[SerializeField] private UnityEvent response;
private void OnEnable()
{
gameEvent.RegisterListener(this);
}
private void OnDisable()
{
gameEvent.UnregisterListener(this);
}
public void OnEventRaised()
{
response.Invoke();
}
}
用法:
创建 GameEvent 资源:"OnPlayerDeath"
将 GameEventListener 附加到 UI
配置响应:显示死亡屏幕
玩家死亡时触发事件
优势:
跨场景共享变量:
public abstract class ScriptableVariable<T> : ScriptableObject
{
[SerializeField] private T value;
public T Value
{
get => value;
set
{
this.value = value;
OnValueChanged?.Invoke(value);
}
}
public event Action<T> OnValueChanged;
}
[CreateAssetMenu(fileName = "IntVariable", menuName = "Variables/Int")]
public class IntVariable : ScriptableVariable<int> { }
[CreateAssetMenu(fileName = "FloatVariable", menuName = "Variables/Float")]
public class FloatVariable : ScriptableVariable<float> { }
用法:
public class Player : MonoBehaviour
{
[SerializeField] private IntVariable playerHealth;
public void TakeDamage(int damage)
{
playerHealth.Value -= damage; // 更新所有订阅者
}
}
public class HealthUI : MonoBehaviour
{
[SerializeField] private IntVariable playerHealth;
[SerializeField] private Text healthText;
private void OnEnable()
{
playerHealth.OnValueChanged += UpdateUI;
UpdateUI(playerHealth.Value);
}
private void OnDisable()
{
playerHealth.OnValueChanged -= UpdateUI;
}
private void UpdateUI(int health)
{
healthText.text = $"Health: {health}";
}
}
优势:
组件间的解耦通信。
标准 C# 事件模式:
public class Health : MonoBehaviour
{
public event Action<int> OnHealthChanged;
public event Action OnDeath;
private int health = 100;
public void TakeDamage(int damage)
{
health -= damage;
OnHealthChanged?.Invoke(health);
if (health <= 0)
{
OnDeath?.Invoke();
}
}
}
public class HealthUI : MonoBehaviour
{
[SerializeField] private Health playerHealth;
private void OnEnable()
{
playerHealth.OnHealthChanged += UpdateHealthBar;
playerHealth.OnDeath += ShowDeathScreen;
}
private void OnDisable()
{
playerHealth.OnHealthChanged -= UpdateHealthBar;
playerHealth.OnDeath -= ShowDeathScreen;
}
private void UpdateHealthBar(int health) { }
private void ShowDeathScreen() { }
}
关键:务必在 OnDisable 中取消订阅,防止内存泄漏。
可在 Inspector 中分配的事件:
public class Interactable : MonoBehaviour
{
[SerializeField] private UnityEvent onInteract;
[SerializeField] private UnityEvent<int> onScoreChanged;
public void Interact()
{
onInteract?.Invoke();
}
public void AddScore(int points)
{
onScoreChanged?.Invoke(points);
}
}
优势:
缺点:
适用于:
集中式事件系统:
public static class EventBus
{
private static readonly Dictionary<Type, Delegate> eventTable = new Dictionary<Type, Delegate>();
public static void Subscribe<T>(Action<T> handler)
{
if (eventTable.TryGetValue(typeof(T), out var existingHandler))
{
eventTable[typeof(T)] = Delegate.Combine(existingHandler, handler);
}
else
{
eventTable[typeof(T)] = handler;
}
}
public static void Unsubscribe<T>(Action<T> handler)
{
if (eventTable.TryGetValue(typeof(T), out var existingHandler))
{
var newHandler = Delegate.Remove(existingHandler, handler);
if (newHandler == null)
eventTable.Remove(typeof(T));
else
eventTable[typeof(T)] = newHandler;
}
}
public static void Publish<T>(T eventData)
{
if (eventTable.TryGetValue(typeof(T), out var handler))
{
(handler as Action<T>)?.Invoke(eventData);
}
}
}
// 事件数据类型
public struct PlayerDiedEvent
{
public Vector3 position;
public string killedBy;
}
// 订阅
public class DeathUI : MonoBehaviour
{
private void OnEnable()
{
EventBus.Subscribe<PlayerDiedEvent>(OnPlayerDied);
}
private void OnDisable()
{
EventBus.Unsubscribe<PlayerDiedEvent>(OnPlayerDied);
}
private void OnPlayerDied(PlayerDiedEvent data)
{
ShowDeathScreen(data.position, data.killedBy);
}
}
// 发布
public class Player : MonoBehaviour
{
private void Die()
{
EventBus.Publish(new PlayerDiedEvent
{
position = transform.position,
killedBy = "Enemy"
});
}
}
优势:
缺点:
可互换的行为:
public interface IMovementStrategy
{
void Move(Transform transform, float speed);
}
public class GroundMovement : MonoBehaviour, IMovementStrategy
{
public void Move(Transform transform, float speed)
{
// 基于地面的移动
}
}
public class FlyingMovement : MonoBehaviour, IMovementStrategy
{
public void Move(Transform transform, float speed)
{
// 飞行移动
}
}
public class Character : MonoBehaviour
{
[SerializeField] private float speed = 5f;
private IMovementStrategy movementStrategy;
private void Awake()
{
movementStrategy = GetComponent<IMovementStrategy>();
}
private void Update()
{
movementStrategy.Move(transform, speed);
}
}
优势:
管理对象状态:
public interface IState
{
void Enter();
void Execute();
void Exit();
}
public class IdleState : MonoBehaviour, IState
{
public void Enter() => Debug.Log("Entering Idle");
public void Execute() { }
public void Exit() => Debug.Log("Exiting Idle");
}
public class StateMachine : MonoBehaviour
{
private IState currentState;
public void ChangeState(IState newState)
{
currentState?.Exit();
currentState = newState;
currentState?.Enter();
}
private void Update()
{
currentState?.Execute();
}
}
public class Enemy : MonoBehaviour
{
private StateMachine stateMachine;
private IdleState idleState;
private ChaseState chaseState;
private void Awake()
{
stateMachine = GetComponent<StateMachine>();
idleState = GetComponent<IdleState>();
chaseState = GetComponent<ChaseState>();
stateMachine.ChangeState(idleState);
}
public void OnPlayerSpotted()
{
stateMachine.ChangeState(chaseState);
}
}
一对多通知:
public interface IObserver
{
void OnNotify(string eventType);
}
public class Subject : MonoBehaviour
{
private readonly List<IObserver> observers = new List<IObserver>();
public void Attach(IObserver observer)
{
if (!observers.Contains(observer))
observers.Add(observer);
}
public void Detach(IObserver observer)
{
observers.Remove(observer);
}
protected void Notify(string eventType)
{
for (int i = observers.Count - 1; i >= 0; i--)
{
observers[i].OnNotify(eventType);
}
}
}
public class Player : Subject
{
public void Jump()
{
Notify("PlayerJumped");
}
}
public class AudioObserver : MonoBehaviour, IObserver
{
[SerializeField] private Player player;
private void OnEnable()
{
player.Attach(this);
}
private void OnDisable()
{
player.Detach(this);
}
public void OnNotify(string eventType)
{
if (eventType == "PlayerJumped")
PlayJumpSound();
}
}
分离数据、展示和逻辑:
// 模型 - 数据
public class PlayerModel
{
public int Health { get; private set; } = 100;
public int Score { get; private set; } = 0;
public event Action<int> OnHealthChanged;
public event Action<int> OnScoreChanged;
public void TakeDamage(int damage)
{
Health -= damage;
OnHealthChanged?.Invoke(Health);
}
public void AddScore(int points)
{
Score += points;
OnScoreChanged?.Invoke(Score);
}
}
// 视图 - 展示
public class PlayerView : MonoBehaviour
{
[SerializeField] private Text healthText;
[SerializeField] private Text scoreText;
public void UpdateHealth(int health)
{
healthText.text = $"Health: {health}";
}
public void UpdateScore(int score)
{
scoreText.text = $"Score: {score}";
}
}
// 控制器 - 逻辑
public class PlayerController : MonoBehaviour
{
private PlayerModel model;
private PlayerView view;
private void Awake()
{
model = new PlayerModel();
view = GetComponent<PlayerView>();
model.OnHealthChanged += view.UpdateHealth;
model.OnScoreChanged += view.UpdateScore;
}
private void OnDestroy()
{
model.OnHealthChanged -= view.UpdateHealth;
model.OnScoreChanged -= view.UpdateScore;
}
public void TakeDamage(int damage)
{
model.TakeDamage(damage);
}
public void AddScore(int points)
{
model.AddScore(points);
}
}
优势:
适用场景:
详细架构模式请参考:
references/manager-patterns.md - 管理器实现、初始化、通信references/scriptableobject-architecture.md - 高级 SO 模式、运行时集合、变量references/event-systems.md - 事件模式、消息总线、发布-订阅系统references/design-patterns.md - Unity 的工厂、池、命令、策略模式✅ 应该:
❌ 不应该:
黄金法则:为变化而设计。松耦合、高内聚的系统更易于维护、测试和扩展。
应用这些架构模式,打造经得起时间考验的可扩展、可维护的 Unity 项目。
每周安装
0
代码仓库
GitHub 星标
3
首次出现
1970年1月1日
安全审计
Essential architectural patterns and design principles for scalable, maintainable Unity projects.
Good architecture separates concerns, reduces coupling, and makes code testable and maintainable. This skill covers proven patterns for Unity game development.
Core architectural concepts:
Centralized systems that coordinate game-wide functionality.
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:
When NOT to use:
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
}
}
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.
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:
Drawbacks:
Data-driven design using ScriptableObjects.
Store data separate from behavior:
[CreateAssetMenu(fileName = "WeaponData", menuName = "Game/Weapon Data")]
public class WeaponData : ScriptableObject
{
public string weaponName;
public int damage;
public float fireRate;
public Sprite icon;
public GameObject projectilePrefab;
}
public class Weapon : MonoBehaviour
{
[SerializeField] private WeaponData data;
public void Fire()
{
Instantiate(data.projectilePrefab, firePoint.position, firePoint.rotation);
}
public int GetDamage() => data.damage;
}
Benefits:
Use for:
Event system using ScriptableObjects:
[CreateAssetMenu(fileName = "GameEvent", menuName = "Events/Game Event")]
public class GameEvent : ScriptableObject
{
private readonly List<GameEventListener> listeners = new List<GameEventListener>();
public void Raise()
{
for (int i = listeners.Count - 1; i >= 0; i--)
{
listeners[i].OnEventRaised();
}
}
public void RegisterListener(GameEventListener listener)
{
if (!listeners.Contains(listener))
listeners.Add(listener);
}
public void UnregisterListener(GameEventListener listener)
{
listeners.Remove(listener);
}
}
public class GameEventListener : MonoBehaviour
{
[SerializeField] private GameEvent gameEvent;
[SerializeField] private UnityEvent response;
private void OnEnable()
{
gameEvent.RegisterListener(this);
}
private void OnDisable()
{
gameEvent.UnregisterListener(this);
}
public void OnEventRaised()
{
response.Invoke();
}
}
Usage:
Create GameEvent asset: "OnPlayerDeath"
Attach GameEventListener to UI
Configure response: Show death screen
Raise event when player dies
Benefits:
Shared variables across scenes:
public abstract class ScriptableVariable<T> : ScriptableObject
{
[SerializeField] private T value;
public T Value
{
get => value;
set
{
this.value = value;
OnValueChanged?.Invoke(value);
}
}
public event Action<T> OnValueChanged;
}
[CreateAssetMenu(fileName = "IntVariable", menuName = "Variables/Int")]
public class IntVariable : ScriptableVariable<int> { }
[CreateAssetMenu(fileName = "FloatVariable", menuName = "Variables/Float")]
public class FloatVariable : ScriptableVariable<float> { }
Usage:
public class Player : MonoBehaviour
{
[SerializeField] private IntVariable playerHealth;
public void TakeDamage(int damage)
{
playerHealth.Value -= damage; // Updates all subscribers
}
}
public class HealthUI : MonoBehaviour
{
[SerializeField] private IntVariable playerHealth;
[SerializeField] private Text healthText;
private void OnEnable()
{
playerHealth.OnValueChanged += UpdateUI;
UpdateUI(playerHealth.Value);
}
private void OnDisable()
{
playerHealth.OnValueChanged -= UpdateUI;
}
private void UpdateUI(int health)
{
healthText.text = $"Health: {health}";
}
}
Benefits:
Decoupled communication between components.
Standard C# event pattern:
public class Health : MonoBehaviour
{
public event Action<int> OnHealthChanged;
public event Action OnDeath;
private int health = 100;
public void TakeDamage(int damage)
{
health -= damage;
OnHealthChanged?.Invoke(health);
if (health <= 0)
{
OnDeath?.Invoke();
}
}
}
public class HealthUI : MonoBehaviour
{
[SerializeField] private Health playerHealth;
private void OnEnable()
{
playerHealth.OnHealthChanged += UpdateHealthBar;
playerHealth.OnDeath += ShowDeathScreen;
}
private void OnDisable()
{
playerHealth.OnHealthChanged -= UpdateHealthBar;
playerHealth.OnDeath -= ShowDeathScreen;
}
private void UpdateHealthBar(int health) { }
private void ShowDeathScreen() { }
}
Critical : Always unsubscribe in OnDisable to prevent memory leaks.
Inspector-assignable events:
public class Interactable : MonoBehaviour
{
[SerializeField] private UnityEvent onInteract;
[SerializeField] private UnityEvent<int> onScoreChanged;
public void Interact()
{
onInteract?.Invoke();
}
public void AddScore(int points)
{
onScoreChanged?.Invoke(points);
}
}
Benefits:
Drawbacks:
Use for:
Centralized event system:
public static class EventBus
{
private static readonly Dictionary<Type, Delegate> eventTable = new Dictionary<Type, Delegate>();
public static void Subscribe<T>(Action<T> handler)
{
if (eventTable.TryGetValue(typeof(T), out var existingHandler))
{
eventTable[typeof(T)] = Delegate.Combine(existingHandler, handler);
}
else
{
eventTable[typeof(T)] = handler;
}
}
public static void Unsubscribe<T>(Action<T> handler)
{
if (eventTable.TryGetValue(typeof(T), out var existingHandler))
{
var newHandler = Delegate.Remove(existingHandler, handler);
if (newHandler == null)
eventTable.Remove(typeof(T));
else
eventTable[typeof(T)] = newHandler;
}
}
public static void Publish<T>(T eventData)
{
if (eventTable.TryGetValue(typeof(T), out var handler))
{
(handler as Action<T>)?.Invoke(eventData);
}
}
}
// Event data types
public struct PlayerDiedEvent
{
public Vector3 position;
public string killedBy;
}
// Subscribe
public class DeathUI : MonoBehaviour
{
private void OnEnable()
{
EventBus.Subscribe<PlayerDiedEvent>(OnPlayerDied);
}
private void OnDisable()
{
EventBus.Unsubscribe<PlayerDiedEvent>(OnPlayerDied);
}
private void OnPlayerDied(PlayerDiedEvent data)
{
ShowDeathScreen(data.position, data.killedBy);
}
}
// Publish
public class Player : MonoBehaviour
{
private void Die()
{
EventBus.Publish(new PlayerDiedEvent
{
position = transform.position,
killedBy = "Enemy"
});
}
}
Benefits:
Drawbacks:
Interchangeable behaviors:
public interface IMovementStrategy
{
void Move(Transform transform, float speed);
}
public class GroundMovement : MonoBehaviour, IMovementStrategy
{
public void Move(Transform transform, float speed)
{
// Ground-based movement
}
}
public class FlyingMovement : MonoBehaviour, IMovementStrategy
{
public void Move(Transform transform, float speed)
{
// Flying movement
}
}
public class Character : MonoBehaviour
{
[SerializeField] private float speed = 5f;
private IMovementStrategy movementStrategy;
private void Awake()
{
movementStrategy = GetComponent<IMovementStrategy>();
}
private void Update()
{
movementStrategy.Move(transform, speed);
}
}
Benefits:
Manage object states:
public interface IState
{
void Enter();
void Execute();
void Exit();
}
public class IdleState : MonoBehaviour, IState
{
public void Enter() => Debug.Log("Entering Idle");
public void Execute() { }
public void Exit() => Debug.Log("Exiting Idle");
}
public class StateMachine : MonoBehaviour
{
private IState currentState;
public void ChangeState(IState newState)
{
currentState?.Exit();
currentState = newState;
currentState?.Enter();
}
private void Update()
{
currentState?.Execute();
}
}
public class Enemy : MonoBehaviour
{
private StateMachine stateMachine;
private IdleState idleState;
private ChaseState chaseState;
private void Awake()
{
stateMachine = GetComponent<StateMachine>();
idleState = GetComponent<IdleState>();
chaseState = GetComponent<ChaseState>();
stateMachine.ChangeState(idleState);
}
public void OnPlayerSpotted()
{
stateMachine.ChangeState(chaseState);
}
}
One-to-many notifications:
public interface IObserver
{
void OnNotify(string eventType);
}
public class Subject : MonoBehaviour
{
private readonly List<IObserver> observers = new List<IObserver>();
public void Attach(IObserver observer)
{
if (!observers.Contains(observer))
observers.Add(observer);
}
public void Detach(IObserver observer)
{
observers.Remove(observer);
}
protected void Notify(string eventType)
{
for (int i = observers.Count - 1; i >= 0; i--)
{
observers[i].OnNotify(eventType);
}
}
}
public class Player : Subject
{
public void Jump()
{
Notify("PlayerJumped");
}
}
public class AudioObserver : MonoBehaviour, IObserver
{
[SerializeField] private Player player;
private void OnEnable()
{
player.Attach(this);
}
private void OnDisable()
{
player.Detach(this);
}
public void OnNotify(string eventType)
{
if (eventType == "PlayerJumped")
PlayJumpSound();
}
}
Separate data, presentation, and logic:
// Model - Data
public class PlayerModel
{
public int Health { get; private set; } = 100;
public int Score { get; private set; } = 0;
public event Action<int> OnHealthChanged;
public event Action<int> OnScoreChanged;
public void TakeDamage(int damage)
{
Health -= damage;
OnHealthChanged?.Invoke(Health);
}
public void AddScore(int points)
{
Score += points;
OnScoreChanged?.Invoke(Score);
}
}
// View - Presentation
public class PlayerView : MonoBehaviour
{
[SerializeField] private Text healthText;
[SerializeField] private Text scoreText;
public void UpdateHealth(int health)
{
healthText.text = $"Health: {health}";
}
public void UpdateScore(int score)
{
scoreText.text = $"Score: {score}";
}
}
// Controller - Logic
public class PlayerController : MonoBehaviour
{
private PlayerModel model;
private PlayerView view;
private void Awake()
{
model = new PlayerModel();
view = GetComponent<PlayerView>();
model.OnHealthChanged += view.UpdateHealth;
model.OnScoreChanged += view.UpdateScore;
}
private void OnDestroy()
{
model.OnHealthChanged -= view.UpdateHealth;
model.OnScoreChanged -= view.UpdateScore;
}
public void TakeDamage(int damage)
{
model.TakeDamage(damage);
}
public void AddScore(int points)
{
model.AddScore(points);
}
}
Benefits:
When to use:
For detailed architectural patterns, consult:
references/manager-patterns.md - Manager implementations, initialization, communicationreferences/scriptableobject-architecture.md - Advanced SO patterns, runtime sets, variablesreferences/event-systems.md - Event patterns, message buses, pub-sub systemsreferences/design-patterns.md - Factory, Pool, Command, Strategy patterns for Unity✅ DO:
❌ DON'T:
Golden rule : Design for change. Loosely coupled, highly cohesive systems are easier to maintain, test, and extend.
Apply these architectural patterns for scalable, maintainable Unity projects that stand the test of time.
Weekly Installs
0
Repository
GitHub Stars
3
First Seen
Jan 1, 1970
Security Audits
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
103,800 周安装