👨💻个人主页:@元宇宙-秩沅
👨💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅!
👨💻 本文由 秩沅 原创
👨💻 收录于专栏:Unity基础实战
⭐🅰️⭐
文章目录
- ⭐🅰️⭐
- ⭐前言⭐
- 🎶(==1==) 常用设计模式
- 1.单例模式
- 2.对象池模式
- 3.享元模式(缓存池)
- 4.观察者模式
- 5.代理模式
- 6.外观模式
- 6.工厂模式
- 7.命令模式
- 🎶(==2==) 设计模式Unity模板
- 1.单例模式
- 2.对象池模式
- 3.享元模式(缓存池)
- 4.观察者模式(事件中心)
- 5.代理模式(Mediator中介)
- 6.外观模式
- 🎶(==3==) 程序框架
- 1. UIManager
- 2.UIManager中的面板基类BasePanel
- ⭐🅰️⭐
⭐前言⭐
🎶(1) 常用设计模式
1.单例模式
-
单例模式就像是一个特殊的工厂,它确保一个类只能创建一个对象,就像是工厂只生产一种特定的产品。这种模式有助于确保在整个程序中,某个类的实例只有一个,避免了不必要的资源浪费和数据混乱。
-
单例模式也可以类比为一个全局的管理员。它确保了在整个应用程序中只有一个实例存在,因此可以用来管理全局状态、资源、配置等,确保这些全局数据在程序中的一致性和唯一性。
2.对象池模式
-
是创建型设计模式的一种,将对象预先创建并初始化后放入对象池中,对象提供者就能利用已有的对象来处理请求,减少频繁创建对象所占用的内存空间和初始化时间。
-
对象池模式(Object Pool Pattern):重点在于管理可重用对象的集合,以避免频繁创建和销毁对象带来的性能开销。对象池会预先创建一定数量的对象,并在需要时将它们分配给客户端,客户端使用完毕后将对象归还给池而不是销毁。这样可以避免频繁地创建和销毁对象,提高了性能和资源利用率。
3.享元模式(缓存池)
享元模式(Flyweight Pattern):主要关注的是共享对象以减少内存占用和提高性能。它通过共享尽可能多的相似对象来减少内存使用,特别是当对象具有大量相似的状态时。它通常通过将对象的共享状态(内在状态)与对象的特定状态(外在状态)相分离来实现。
4.观察者模式
在Unity中,观察者模式是一种设计模式,用于实现对象之间的一对多依赖关系。它允许一个对象(称为主题或被观察者)将其状态的改变通知给一组依赖于该对象的其他对象(称为观察者),使得这些观察者能够自动更新。
在Unity中,观察者模式通常使用事件(Event)来实现。被观察者对象定义一个事件,并在适当的时候触发该事件。观察者对象通过订阅(Subscribe)这个事件来注册自己,一旦事件被触发,观察者对象会收到通知并执行相应的操作。
观察者模式在游戏开发中经常使用,例如在实现游戏中的事件系统、UI更新等方面。它提供了一种松耦合的方式,使得对象之间的通信更加灵活和可维护。
5.代理模式
在Unity中,代理模式是一种设计模式,用于通过创建一个代理类来控制对另一个对象的访问。代理类充当了被代理对象的中介,可以在访问被代理对象之前或之后执行一些额外的操作。
在游戏开发中,代理模式常用于以下情况:
- 远程代理:在网络游戏中,代理模式可以用于处理客户端和服务器之间的通信。客户端通过代理类与服务器进行通信,代理类负责将请求转发给服务器并返回响应。
- 虚拟代理:当处理大量资源时,可以使用代理模式来延迟创建或加载这些资源。代理类可以在需要时才实例化或加载资源,以提高程序的性能。
- 安全代理:代理模式可以用于实现访问控制。代理类可以根据用户的权限来控制对某些功能的访问,从而增强系统的安全性。
在Unity中,代理模式可以通过创建一个继承自某个接口的代理类来实现。代理类可以与被代理对象实现相同的接口,并在必要时调用被代理对象的方法或属性。这样,代理类就可以代替被代理对象与其他对象进行交互。
6.外观模式
在Unity中,外观模式是一种设计模式,用于提供一个统一的接口,封装了一组复杂的接口或子系统,并使其更容易使用。外观模式的目的是简化客户端与接口之间的交互,通过隐藏复杂的实现细节,提供一个简单的接口给客户端使用。
-
在Unity中,外观模式通常用于简化复杂的系统或子系统的使用。例如,当使用Unity的物理引擎时,可以使用Rigidbody外观来处理物体的物理行为。这样,开发者可以使用简单的方法,如AddForce和Rotate,来控制物体的运动和旋转,而不必关心底层的物理计算和碰撞检测。
-
另一个常见的使用外观模式的地方是在游戏开发中的音频管理中。通过创建一个AudioManager外观,开发者可以使用简单的方法,如PlaySound和StopSound,来控制游戏中的音频播放,而不必直接访问底层的音频引擎。
使用外观模式可以提高代码的可读性和可维护性,简化客户端代码,并且使系统更加灵活和可扩展。
6.工厂模式
在Unity中,工厂模式是一种创建型设计模式,用于封装对象的实例化过程。工厂模式将对象的创建委托给一个工厂类,该工厂类负责根据不同的条件或参数返回适当的对象实例。
-
工厂模式的主要目的是隐藏对象的创建细节,并提供一种统一的方式来创建对象,使代码更加灵活和可维护。通过使用工厂模式,可以降低代码的耦合性,使得代码的扩展更加容易。
-
在Unity中,工厂模式常用于创建游戏对象的实例。通过使用工厂模式,可以根据不同的需求和场景,以一种统一的方式来创建游戏对象,并在需要时对其进行初始化和配置。工厂模式可以帮助我们简化游戏对象的创建过程,提高代码的可读性和可维护性。
在Unity中,工厂模式可以通过自定义脚本和组件来实现。通过编写工厂类,可以根据不同的条件或参数来创建不同类型的游戏对象,并将其添加到场景中或作为其他对象的子对象。同时,还可以通过工厂类的接口来控制对象的创建和初始化过程,以及处理对象的销毁和回收。
7.命令模式
在Unity中,命令模式是一种设计模式,用于解耦游戏对象的行为和输入命令之间的关系。该模式通过将命令封装成对象,使得游戏对象不需要直接处理输入命令,而是通过调用命令对象来执行相应的操作。
在命令模式中,通常有四个主要的角色:
-
命令(Command):定义了需要执行的操作接口,通常包含一个执行方法(Execute)和一个撤销方法(Undo)。
-
具体命令(Concrete Command):实现了命令接口的具体类,包含了具体的操作逻辑。
-
命令调用者(Invoker):负责调用具体命令的对象,通常包含一个命令对象的引用。
-
接收者(Receiver):真正执行命令的对象,通常是游戏对象或者组件。
在Unity中,可以将输入命令封装成具体的命令对象。当玩家触发某个操作时,比如按下按钮或者触摸屏幕,命令调用者会创建相应的命令对象,并调用其执行方法。命令对象会将具体的操作逻辑委托给接收者来执行。
通过使用命令模式,可以实现游戏对象的行为和输入命令之间的解耦,提高代码的可维护性和扩展性。
🎶(2) 设计模式Unity模板
1.单例模式
- SingleManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//不继承Mono的泛型模板
public class SingleManager<T> where T:new()
{
private static T instance;
public static T GetInstance()
{
if (instance == null)
instance = new T();
return instance;
}
}
- SingletonMono
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//继承了 Mono的泛型模板 --激活只能拖拽或者AddComponent
public class SingletonMono<T> : MonoBehaviour where T: MonoBehaviour
{
private static T instance;
public static T GetInstance()
{
return instance;
}
protected virtual void Awake()
{
instance = this as T;
}
}
- SingletonAutoMono
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//激活--不需要拖拽或者AddComponent--直接在面板中创建
public class SingletonAutoMono<T> : MonoBehaviour where T : MonoBehaviour
{
private static T instance;
public static T GetInstance()
{
if( instance == null )
{
GameObject obj = new GameObject();
obj.name = typeof(T).ToString(); //设置对象的名字为脚本名
DontDestroyOnLoad(obj); //过场景 不移除
instance = obj.AddComponent<T>();
}
return instance;
}
}
2.对象池模式
//音符块(栈)声明
private Stack<NoteObject> noteObjectPool = new Stack<NoteObject>();
/// <summary>
/// 对象池有关
/// </summary>
/// <returns></returns>
public NoteObject GetFreshNoteObject() // 从对象池中取音符对象
{
NoteObject retObj;
if (noteObjectPool.Count>0)
{
retObj = noteObjectPool.Pop();
}
else
{
//资源源
//retObj = Instantiate(noteObject);
retObj = Instantiate(noteObject);
}
retObj.transform.position = Vector3.one*2;
retObj.gameObject.SetActive(true);
//retObj.SetActive(true);
retObj.enabled = true;
return retObj;
}
public void ReturnNoteObjectToPool(NoteObject obj) //将音符对象放入对象池
{
if (obj!=null)
{
obj.enabled = false;
obj.gameObject.SetActive(false);
noteObjectPool.Push(obj);
}
}
/// <summary>
/// 从特效对象池中取对象
/// </summary>
/// <param name="stack"></param>
/// <param name="effectObject"></param>
public GameObject GetFreshEffectObject(Stack<GameObject> stack,GameObject effectObject)
{
GameObject effectGo;
if (stack.Count>0)
{
effectGo=stack.Pop();
}
else
{
effectGo = Instantiate(effectObject);
}
effectGo.SetActive(true); //确保特效对象是激活状态
return effectGo;
}
3.享元模式(缓存池)
- PoolManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
// 抽屉数据
public class PoolData
{
public GameObject fatherObj;
public List<GameObject> poolList;
public PoolData(GameObject obj, GameObject poolObj)
{
fatherObj = new GameObject(obj.name);
fatherObj.transform.parent = poolObj.transform;
poolList = new List<GameObject>() {};
PushObj(obj);
}
//存
public void PushObj(GameObject obj)
{
obj.SetActive(false);
poolList.Add(obj);
obj.transform.parent = fatherObj.transform;
}
//取
public GameObject GetObj()
{
GameObject obj = null;
obj = poolList[0]; //取第一个
poolList.RemoveAt(0); //然后容器中移除
obj.SetActive(true);
obj.transform.parent = null; //断开了父子关系
return obj;
}
}
//缓存池管理器
public class PoolManager : SingleManager<PoolManager>
{
//存储衣柜
public Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();
private GameObject poolObj;
//衣柜里面拿东西
public void GetObj(string name, UnityAction<GameObject> callBack)
{
//有抽屉 并且抽屉里有东西
if (poolDic.ContainsKey(name) && poolDic[name].poolList.Count > 0)
{
callBack(poolDic[name].GetObj());
}
else
{
//通过异步加载资源 创建对象给外部用
ResourceManager .GetInstance().LoadAsync<GameObject>(name, (obj2) =>
{
obj2.name = name;
callBack(obj2); //异步加载出的资源不能直接给外部使用所以需要委托
});
}
}
//放东西进衣柜
public void PushObj(string name, GameObject obj)
{
if (poolObj == null) //防止报错
poolObj = new GameObject("Pool");
//里面有抽屉
if (poolDic.ContainsKey(name))
{
poolDic[name].PushObj(obj);
}
else
{
poolDic.Add(name, new PoolData(obj, poolObj));
}
}
//清空缓存池——在场景切换时
public void Clear()
{
poolDic.Clear();
poolObj = null;
}
}
4.观察者模式(事件中心)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public interface IEventInfo
{
}
public class EventInfo<T> : IEventInfo
{
public UnityAction<T> actions;
public EventInfo( UnityAction<T> action)
{
actions += action;
}
}
public class EventInfo : IEventInfo
{
public UnityAction actions;
public EventInfo(UnityAction action)
{
actions += action;
}
}
/// <summary>
/// 事件中心 单例模式对象
/// 1.Dictionary
/// 2.委托
/// 3.观察者设计模式
/// 4.泛型
/// </summary>
public class EventCenter : BaseManager<EventCenter>
{
//key —— 事件的名字(比如:怪物死亡,玩家死亡,通关 等等)
//value —— 对应的是 监听这个事件 对应的委托函数们
private Dictionary<string, IEventInfo> eventDic = new Dictionary<string, IEventInfo>();
/// <summary>
/// 添加事件监听
/// </summary>
/// <param name="name">事件的名字</param>
/// <param name="action">准备用来处理事件 的委托函数</param>
public void AddEventListener<T>(string name, UnityAction<T> action)
{
//有没有对应的事件监听
//有的情况
if( eventDic.ContainsKey(name) )
{
(eventDic[name] as EventInfo<T>).actions += action;
}
//没有的情况
else
{
eventDic.Add(name, new EventInfo<T>( action ));
}
}
/// <summary>
/// 监听不需要参数传递的事件
/// </summary>
/// <param name="name"></param>
/// <param name="action"></param>
public void AddEventListener(string name, UnityAction action)
{
//有没有对应的事件监听
//有的情况
if (eventDic.ContainsKey(name))
{
(eventDic[name] as EventInfo).actions += action;
}
//没有的情况
else
{
eventDic.Add(name, new EventInfo(action));
}
}
/// <summary>
/// 移除对应的事件监听
/// </summary>
/// <param name="name">事件的名字</param>
/// <param name="action">对应之前添加的委托函数</param>
public void RemoveEventListener<T>(string name, UnityAction<T> action)
{
if (eventDic.ContainsKey(name))
(eventDic[name] as EventInfo<T>).actions -= action;
}
/// <summary>
/// 移除不需要参数的事件
/// </summary>
/// <param name="name"></param>
/// <param name="action"></param>
public void RemoveEventListener(string name, UnityAction action)
{
if (eventDic.ContainsKey(name))
(eventDic[name] as EventInfo).actions -= action;
}
/// <summary>
/// 事件触发
/// </summary>
/// <param name="name">哪一个名字的事件触发了</param>
public void EventTrigger<T>(string name, T info)
{
//有没有对应的事件监听
//有的情况
if (eventDic.ContainsKey(name))
{
//eventDic[name]();
if((eventDic[name] as EventInfo<T>).actions != null)
(eventDic[name] as EventInfo<T>).actions.Invoke(info);
//eventDic[name].Invoke(info);
}
}
/// <summary>
/// 事件触发(不需要参数的)
/// </summary>
/// <param name="name"></param>
public void EventTrigger(string name)
{
//有没有对应的事件监听
//有的情况
if (eventDic.ContainsKey(name))
{
//eventDic[name]();
if ((eventDic[name] as EventInfo).actions != null)
(eventDic[name] as EventInfo).actions.Invoke();
//eventDic[name].Invoke(info);
}
}
/// <summary>
/// 清空事件中心
/// 主要用在 场景切换时
/// </summary>
public void Clear()
{
eventDic.Clear();
}
}
5.代理模式(Mediator中介)
using PureMVC.Interfaces;
using PureMVC.Patterns.Mediator;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//-------------------------------
//-------功能: 角色面板视图中介
//-------创建者: -------
//------------------------------
/// <summary>
/// 角色面板视图中介
/// 固定:
/// 1.继承PureMVC的Mediator脚本
/// 2.写构造函数
/// 3.重写监听通知的方法
/// 4.重写处理通知的方法
/// 5.可选:重写注册时的方法
/// </summary>
public class RoleViewMediator : Mediator
{
//铭牌名
public static string NAME = "RoleViewMediator";
/// <summary>
/// 构造函数
/// </summary>
public RoleViewMediator( ) : base(NAME)
{
//可以去写创捷面板预设体的逻辑等
}
/// <summary>
/// 重写监听通知的方法,返回需要的监听(通知)
/// </summary>
/// <returns>返回你需要监听的通知的名字数组</returns>
public override string[] ListNotificationInterests()
{
return new string[] {
PureNotification.UPDATA_ROLE_INFO,
PureNotification.ROLE_PANEL
};
}
/// <summary>
/// 重写处理通知的方法,处理通知
/// </summary>
/// <param name="notification">通知</param>
public override void HandleNotification(INotification notification)
{
switch (notification.Name)
{
case PureNotification.UPDATA_ROLE_INFO:
(ViewComponent as RoleView).UpdateView(notification.Body as PlayerDataObj);
break;
}
}
/// <summary>
/// 可选:重写注册方法(他们需要到Facde中注册)
/// </summary>
public override void OnRegister()
{
base.OnRegister();
}
}
6.外观模式
🎶(3) 程序框架
1. UIManager
其中的异步加载方法,缓冲时间很有可能会影响参数的及时传递
采用单例模式构造的UIManager脚本,对所有面板类Panel进行管理,在UIManager中
- ①加载预制体。Canvas和所有面板都以预制体的方式进行异步加载
- ②构建层级。并构建显示层级(Bot,Mid,Top,System) ,方便面板之间的显隐
- ③提供面板显示、隐藏、移除和 自定义事件添加的方法
2.UIManager中的面板基类BasePanel
面板基类作为,所需面板的继承源,为其子类提供了很多便利,如下:
- ①激活查询控件。内部构建了找寻面板中所有UI控件的方法,一激活就寻找并将其存储到字典当中
- ②精准获取控件。可通过UI控件名字和类型,来精确得到对应UI控件(在字典中遍历)
⭐🅰️⭐
⭐【Unityc#专题篇】之c#进阶篇】
⭐【Unityc#专题篇】之c#核心篇】
⭐【Unityc#专题篇】之c#基础篇】
⭐【Unity-c#专题篇】之c#入门篇】
⭐【Unityc#专题篇】—进阶章题单实践练习
⭐【Unityc#专题篇】—基础章题单实践练习
⭐【Unityc#专题篇】—核心章题单实践练习
你们的点赞👍 收藏⭐ 留言📝 关注✅是我持续创作,输出优质内容的最大动力!、