设计模式
单例模式
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
private static T instance;
public static T Instance
{
get { return instance; }
}
protected virtual void Awake()
{
if (instance != null)
{
Destroy(gameObject);
}
else
instance = (T)this;
}
public static bool IsInitialized
{
get { return instance != null; }
}
protected virtual void OnDestroy()
{
if (instance == this)
{
instance = null;
}
}
}
对象池模式
using System.Collections.Generic;
using UnityEngine;
public class ObjectPool<T> where T : Component
{
private Stack<T> _pool = new Stack<T>();
private readonly Transform _parent; // 用于存放对象池中的对象
private readonly System.Func<T> _createFunc; // 创建对象的方法
public ObjectPool(int initialSize, System.Func<T> createFunc, Transform parent)
{
_createFunc = createFunc;
_parent = parent;
for (int i = 0; i < initialSize; i++)
{
T obj = CreateObject();
ReturnToPool(obj);
}
}
private T CreateObject()
{
T obj = _createFunc();
if (_parent != null)
{
obj.transform.SetParent(_parent, false);
}
return obj;
}
public T GetFromPool()
{
if (_pool.Count == 0)
{
Debug.LogWarning("Object pool is empty, creating a new object.");
return CreateObject();
}
T obj = _pool.Pop();
obj.gameObject.SetActive(true); // 激活对象
return obj;
}
public void ReturnToPool(T obj)
{
obj.gameObject.SetActive(false); // 禁用对象
_pool.Push(obj);
}
}
// 使用示例
public class ExampleUsage : MonoBehaviour
{
public GameObject prefab; // 要池化的预制件
private ObjectPool<GameObject> _objectPool;
private void Start()
{
// 初始化对象池,传入初始大小、创建方法和父物体
_objectPool = new ObjectPool<GameObject>(10, () => Instantiate(prefab), transform);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
// 从池中获取对象
GameObject obj = _objectPool.GetFromPool();
// 设置对象位置
obj.transform.position = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10));
}
}
// 返回对象到池中(通常由对象自身或其它逻辑触发)
public void ReturnObject(GameObject obj)
{
_objectPool.ReturnToPool(obj);
}
}
工厂模式
工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。通过工厂模式,一个类(称为“工厂”)负责决定应该实例化哪一个类,这使得代码更加灵活和易于维护。在Unity中,工厂模式可以用来管理游戏对象的创建,例如敌人、道具、子弹等。
1. 定义产品接口或基类
首先,我们需要定义一个所有具体产品都应该实现的接口或继承的基类。在这个例子中,我们将创建一个IEnemy接口,所有的敌人都将实现这个接口。
public interface IEnemy
{
void Initialize();
void Move();
void Attack();
void TakeDamage(int damage);
void Die();
}
2. 创建具体的产品类
接下来,我们创建几个具体的敌人类型,它们都实现了IEnemy接口。
public class Orc : MonoBehaviour, IEnemy
{
public void Initialize()
{
// 初始化逻辑
Debug.Log("Orc Initialized");
}
public void Move()
{
// 移动逻辑
Debug.Log("Orc Moving");
}
public void Attack()
{
// 攻击逻辑
Debug.Log("Orc Attacking");
}
public void TakeDamage(int damage)
{
// 受损逻辑
Debug.Log($"Orc takes {damage} damage");
}
public void Die()
{
// 死亡逻辑
Debug.Log("Orc Died");
Destroy(gameObject);
}
}
// 可以添加更多类型的敌人...
3. 创建工厂类
现在,我们创建一个工厂类来负责创建这些敌人。这个工厂可以根据传入的参数决定创建哪种类型的敌人。
public class EnemyFactory : MonoBehaviour
{
[System.Serializable]
public class EnemyPrefab
{
public string enemyType;
public GameObject prefab;
}
[SerializeField] private EnemyPrefab[] enemyPrefabs;
private Dictionary<string, GameObject> _enemyDictionary = new Dictionary<string, GameObject>();
private void Awake()
{
foreach (var enemyPrefab in enemyPrefabs)
{
_enemyDictionary[enemyPrefab.enemyType] = enemyPrefab.prefab;
}
}
public IEnemy CreateEnemy(string type, Vector3 position, Quaternion rotation)
{
if (_enemyDictionary.TryGetValue(type, out GameObject prefab))
{
GameObject obj = Instantiate(prefab, position, rotation);
return obj.GetComponent<IEnemy>();
}
else
{
Debug.LogError($"Enemy type '{type}' not found.");
return null;
}
}
}
4. 使用工厂
最后,我们在需要的地方使用工厂来创建敌人。例如在Spawner脚本中:
public class Spawner : MonoBehaviour
{
public EnemyFactory enemyFactory;
public string enemyType; // 在Inspector中设置要生成的敌人类型
public float spawnInterval = 2f;
private void Start()
{
InvokeRepeating(nameof(SpawnEnemy), 0f, spawnInterval);
}
private void SpawnEnemy()
{
IEnemy enemy = enemyFactory.CreateEnemy(enemyType, transform.position, Quaternion.identity);
if (enemy != null)
{
enemy.Initialize();
}
}
}
观察者模式
在Unity中,观察者模式可以用于实现事件驱动的架构,例如游戏中的敌人需要响应玩家的生命值变化,或者UI元素需要响应游戏角色的状态变化等。
1. 定义主题(Subject)接口
首先,我们定义一个ISubject接口,该接口包含添加、移除和通知观察者的操作。
using System;
using System.Collections.Generic;
public interface ISubject
{
void RegisterObserver(IObserver observer);
void RemoveObserver(IObserver observer);
void NotifyObservers();
}
2. 创建具体的主题类
接下来,我们创建一个具体的主题类,例如Player,它可以注册和移除观察者,并在特定事件发生时通知它们。
public class Player : MonoBehaviour, ISubject
{
private List<IObserver> _observers = new List<IObserver>();
public event Action OnHealthChanged; // 当玩家生命值变化时触发的事件
public int Health { get; private set; } = 100;
public void TakeDamage(int damage)
{
Health -= damage;
Debug.Log($"Player took {damage} damage, health is now {Health}");
if (OnHealthChanged != null) OnHealthChanged(); // 触发事件
NotifyObservers();
}
public void RegisterObserver(IObserver observer)
{
if (!_observers.Contains(observer))
{
_observers.Add(observer);
}
}
public void RemoveObserver(IObserver observer)
{
_observers.Remove(observer);
}
public void NotifyObservers()
{
foreach (var observer in _observers)
{
observer.Update(this); // 通知每个观察者
}
}
}
3. 定义观察者(Observer)接口
然后,我们定义一个IObserver接口,该接口包含一个Update方法,这是所有观察者必须实现的方法。
public interface IObserver
{
void Update(ISubject subject);
}
4. 创建具体的观察者类
现在,我们创建一些具体的观察者类,这些类实现了IObserver接口,并定义了如何响应主题的通知。
public class Enemy : MonoBehaviour, IObserver
{
private Player _player;
public void SetPlayer(Player player)
{
_player = player;
_player.RegisterObserver(this); // 注册自己为观察者
}
public void Update(ISubject subject)
{
if (subject is Player player)
{
Debug.Log($"{name} received notification from Player: Health is {player.Health}");
// 可以在这里添加敌人的反应逻辑,比如改变状态或攻击
}
}
private void OnDestroy()
{
if (_player != null)
{
_player.RemoveObserver(this); // 移除自己作为观察者
}
}
}
public class UIManager : MonoBehaviour, IObserver
{
private Player _player;
public void SetPlayer(Player player)
{
_player = player;
_player.RegisterObserver(this); // 注册自己为观察者
}
public void Update(ISubject subject)
{
if (subject is Player player)
{
Debug.Log("UIManager updating UI elements based on player's health");
// 更新UI元素,如健康条
}
}
private void OnDestroy()
{
if (_player != null)
{
_player.RemoveObserver(this); // 移除自己作为观察者
}
}
}5. 使用观察者模式
最后,在场景中创建Player、Enemy和UIManager实例,并将Enemy和UIManager设置为Player的观察者。
public class GameInitializer : MonoBehaviour
{
public Player player;
public Enemy enemy;
public UIManager uiManager;
private void Start()
{
// 将敌人和UI管理器注册为玩家的观察者
enemy.SetPlayer(player);
uiManager.SetPlayer(player);
// 模拟玩家受到伤害
player.TakeDamage(20);
}
}
代理模式
代理模式可以在不改变原对象接口的情况下,添加额外的功能,如延迟加载、权限检查、日志记录等。在Unity中,代理模式可以用于网络通信、资源管理、性能优化等多种场景。
1. 定义主题接口
首先,我们定义一个接口,所有的真实主题和代理都将实现这个接口。这确保了客户端代码可以透明地与真实主题或代理交互。
public interface IGameService
{
void LoadLevel(string levelName);
void SaveProgress();
void ExitGame();
}
2. 创建真实主题类
接下来,我们创建一个具体的GameService类,它实现了IGameService接口,并提供了实际的游戏服务功能。
public class GameService : IGameService
{
public void LoadLevel(string levelName)
{
Debug.Log($"Loading level: {levelName}");
// 实际的加载逻辑
}
public void SaveProgress()
{
Debug.Log("Saving game progress...");
// 实际的保存逻辑
}
public void ExitGame()
{
Debug.Log("Exiting game...");
// 实际的退出逻辑
}
}
3. 创建代理类
现在,我们创建一个代理类GameServiceProxy,它也实现了IGameService接口。代理类可以在调用真实主题的方法之前或之后执行额外的操作,例如权限检查、日志记录等。
public class GameServiceProxy : IGameService
{
private GameService _realService;
public GameServiceProxy(GameService realService)
{
_realService = realService;
}
public void LoadLevel(string levelName)
{
// 在调用真实方法之前执行额外操作
if (CheckPermissions())
{
LogAction("LoadLevel");
_realService.LoadLevel(levelName);
}
else
{
Debug.LogWarning("Permission denied to load level.");
}
}
public void SaveProgress()
{
// 在调用真实方法之前执行额外操作
if (CheckPermissions())
{
LogAction("SaveProgress");
_realService.SaveProgress();
}
else
{
Debug.LogWarning("Permission denied to save progress.");
}
}
public void ExitGame()
{
// 在调用真实方法之前执行额外操作
if (CheckPermissions())
{
LogAction("ExitGame");
_realService.ExitGame();
}
else
{
Debug.LogWarning("Permission denied to exit game.");
}
}
private bool CheckPermissions()
{
// 模拟权限检查逻辑
return UnityEngine.Random.value > 0.5f; // 随机返回true或false
}
private void LogAction(string action)
{
// 记录日志
Debug.Log($"[Log] Action: {action} called at {DateTime.Now}");
}
}
4. 使用代理模式
最后,在需要的地方使用代理来代替直接调用真实主题。这样可以确保所有的调用都经过代理,从而执行额外的操作。
public class GameManager : MonoBehaviour
{
private IGameService _gameService;
private void Start()
{
// 创建真实的服务实例
GameService realService = new GameService();
// 创建代理实例,传入真实的服务
_gameService = new GameServiceProxy(realService);
// 使用代理来调用游戏服务的方法
_gameService.LoadLevel("Level1");
_gameService.SaveProgress();
_gameService.ExitGame();
}
}