单例模式
懒汉模式:
指在第一次访问单例对象时才创建实例
特点是在多线程环境下可能会存在线程安全问题,因为多个线程可能在同一时间检查到实例不存在,从而导致多个实例被创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class UnitySingleton<T> : MonoBehaviour where T : Component { private static T _instance; public static T Instance { get { if (_instance == null) { _instance = FindObjectOfType(typeof(T)) as T; if (_instance == null) { GameObject obj = new GameObject(); _instance = obj.AddComponent<T>(); } } return _instance; } } }
|
饿汉模式:
指在类加载时就创建实例,无论是否需要
这样就可以避免多线程环境下的线程安全问题,但可能会增加启动时间和内存消耗
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class Singleton<T> : MonoBehaviour where T : MonoBehaviour, new() { public static T Instance = null;
public virtual void Awake() { if (Instance == null) { Instance = this as T; } else { Destroy(this.gameObject); Debug.LogError("单例只允许存在一个,架构存在错误,本物体" + this.name + " 已删除"); } } }
|
拓展:
懒汉模式下,结束运行时可能会报错Some objects were not cleaned up when closing
这是因为我们在OnDestroy
里访问了这个单例,结束运行时这个单例已经变成null
了
访问这个单例的时候又产生了一个新的实例才会报这个错误
可以将上述懒汉单例改成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class UnitySingleton<T> : MonoBehaviour where T : Component { private static bool applicationIsQuitting = false; private static T _instance; public static T Instance { get { if (_instance == null) { if (applicationIsQuitting) return _instance; _instance = FindObjectOfType(typeof(T)) as T; if (_instance == null) { GameObject obj = new GameObject(); _instance = obj.AddComponent<T>(); } } return _instance; } } protected virtual void OnDestory() { applicationIsQuitting = true; } }
|
观察者模式
使用委托和事件实现
下面的方法使用的是委托和列表的方式,原理是一样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| using System.Collections; using System.Collections.Generic; using UnityEngine;
public class EventManager : Singleton<EventManager> { public delegate void EventCallBack(object param); Dictionary<int, List<EventCallBack>> mDictEvent = new Dictionary<int, List<EventCallBack>>();
public void AddEvent(int eventId, EventCallBack callBack) { if(!mDictEvent.ContainsKey(eventId)) { mDictEvent.Add(eventId,new List<EventCallBack>()); } if(!mDictEvent[eventId].Contains(callBack)) { mDictEvent[eventId].Add(callBack); } else { Debug.LogWarning("Repeat Add Event CallBack,EventId = " + eventId + ",CallBack = " + callBack.ToString()); } }
public void DelEvent(int eventId, EventCallBack callBack) { if(!mDictEvent.ContainsKey(eventId)) { return; } if(!mDictEvent[eventId].Contains(callBack)) { return; } mDictEvent[eventId].Remove(callBack);
if (mDictEvent[eventId].Count < 1) { mDictEvent.Remove(eventId); }
}
public void NotifyEvent(int eventId,object param) { if(mDictEvent.ContainsKey(eventId)) { foreach(var callback in mDictEvent[eventId]) { callback(param); } } } }
|
组合模式
把公共方法抽象成组件,需要用到这个方法的对象可以将对应的组件添加到该对象上
比较简单,就不举例了
命令模式
适用场景:实现撤销功能
大致由三部分组成:命令类、命令管理器类、输入类
命令类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| using UnityEngine;
public abstract class CommandBase { public abstract void Execute(); public abstract void Undo(); }
public class MoveForWard : CommandBase { private GameObject _player; public MoveForWard(GameObject player) { _player = player; } public override void Execute() { _player.transform.Translate(Vector3.forward); }
public override void Undo() { _player.transform.Translate(Vector3.back); } }
public class MoveLeft : CommandBase { private GameObject _player; public MoveLeft(GameObject player) { _player = player; } public override void Execute() { _player.transform.Translate(Vector3.left); } public override void Undo() { _player.transform.Translate(Vector3.right); } }
|
命令管理器类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| using System.Collections; using System.Collections.Generic; using UnityEngine;
public class CommandManager : MonoBehaviour { public static CommandManager Instance; private readonly List<CommandBase> _commandList = new List<CommandBase>();
private void Awake() { if (Instance) Destroy(Instance); else Instance = this; }
public void AddCommands(CommandBase command) { _commandList.Add(command); }
public IEnumerator UndoStart() { _commandList.Reverse(); foreach (CommandBase command in _commandList) { yield return new WaitForSeconds(.2f); command.Undo(); }
_commandList.Clear(); } }
|
输入类:
监听按键点击,只负责接受输入,具体的操作放在命令的Execute方法中,这样实现解耦
点击W或A键时执行命令,并把命令添加的管理器的列表中,点击B键撤销之前所有的命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| using UnityEngine;
public class InputHandler : MonoBehaviour { private MoveForWard _moveForward; private MoveLeft _moveLeft; private GameObject _playerCube;
private void Start() { _playerCube = GameObject.CreatePrimitive(PrimitiveType.Cube); }
private void Update() { if (Input.GetKeyDown(KeyCode.W)) { _moveForward = new MoveForWard(_playerCube); _moveForward.Execute(); CommandManager.Instance.AddCommands(_moveForward); }
if (Input.GetKeyDown(KeyCode.A)) { _moveLeft = new MoveLeft(_playerCube); _moveLeft.Execute(); CommandManager.Instance.AddCommands(_moveLeft); }
if (Input.GetKeyDown(KeyCode.B)) { StartCoroutine(CommandManager.Instance.UndoStart()); } } }
|
状态模式
一般使用有限状态机实现
- 将对象的行为抽象成几个独立的状态
- 某一时刻只能处于其中一种状态
- 通过管理控制状态的互相切换
有两张使用场景:场景切换、怪物AI
核心代码(状态机):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| using System.Collections.Generic; using UnityEngine; using System;
namespace MY_FSM { public enum StateType { Idle, MOVE, Find_Enemy, Attack, Die, Success, } public interface IState { void OnEnter(); void OnExit(); void OnUpdate(); } [Serializable] public class Blackboard { } public class FSM { public IState curState; public Dictionary<StateType, IState> states; public Blackboard blackboard;
public FSM(Blackboard blackboard) { this.states = new Dictionary<StateType, IState>(); this.blackboard = blackboard; }
public void AddState(StateType stateType, IState state) { if (states.ContainsKey(stateType)) { Debug.Log("[AddState] >>>>>>>>>>>>> map has contain key: " + stateType); return; } states.Add(stateType, state); }
public void SwitchState(StateType stateType) { if (!states.ContainsKey(stateType)) { Debug.Log("[SwitchState] >>>>>>>>>>>>>>>>> not contain key: " + stateType); return; } if (curState != null) { curState.OnExit(); } curState = states[stateType]; curState.OnEnter(); }
public void OnUpdate() { curState.OnUpdate(); }
public void OnFixUpdate() { }
public void OnCheck() { } } }
|