概述
单例模式基类
没有继承 MonoBehaviour
继承了 MonoBehaviour 的两种单例模式的写法
缓存池模块
(确实挺有用)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 缓存池模块
/// 知识点
/// 1.Dictionary、List
/// 2.GameObject 和 Resources 两个公共类中的 API
/// </summary>
public class PoolMgr : BaseManager<PoolMgr>
{//缓存池容器 (衣柜)public Dictionary<string, List<GameObject>> poolDic = new Dictionary<string, List<GameObject>>();/// <summary>/// 往外拿东西/// </summary>/// <param name="name"></param>/// <returns></returns>public GameObject GetObj(string name){GameObject obj = null;//有抽屉 并且抽屉里有东西if (poolDic.ContainsKey(name) && poolDic[name].Count > 0){//得到obj = poolDic[name][0];//将它从抽屉移除poolDic[name].RemoveAt(0);}else{//没有就创建obj = GameObject.Instantiate(Resources.Load<GameObject>(name));//把对象名字改的和池子名字一样obj.name = name;}//激活 让其显示obj.SetActive(true);return obj;}/// <summary>/// 还给 暂时不用的东西回来/// </summary>/// <param name="name"></param>/// <param name="obj"></param>public void PushObj(string name, GameObject obj){//失活 让其隐藏obj.SetActive(false);//里面有抽屉的话if (poolDic.ContainsKey(name)){poolDic[name].Add(obj);}//里面没有抽屉else{poolDic.Add(name, new List<GameObject>() { obj });}}}
测试
调用
优化
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 抽屉数据 池子中的一列数据
/// </summary>
public class PoolData
{//抽屉中 对象挂载的父节点public GameObject fatherObj;//对象的容器public List<GameObject> poolList;public PoolData(GameObject obj, GameObject poolObj){//给我们的抽屉 创建一个父对象 并且把他作为我们pool(衣柜)对象的子物体fatherObj = new GameObject(obj.name);fatherObj.transform.parent = poolObj.transform;poolList = new List<GameObject>();PushObj(obj);}/// <summary>/// 往抽屉里面 压东西/// </summary>/// <param name="obj"></param>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;}
}/// <summary>
/// 缓存池模块
/// 知识点
/// 1.Dictionary、List
/// 2.GameObject 和 Resources 两个公共类中的 API
/// </summary>
public class PoolMgr : BaseManager<PoolMgr>
{//缓存池容器 (衣柜)public Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();private GameObject poolObj;/// <summary>/// 往外拿东西/// </summary>/// <param name="name"></param>/// <returns></returns>public GameObject GetObj(string name){GameObject obj = null;//有抽屉 并且抽屉里有东西if (poolDic.ContainsKey(name) && poolDic[name].poolList.Count > 0){//得到obj = poolDic[name].GetObj();}else{//没有就创建obj = GameObject.Instantiate(Resources.Load<GameObject>(name));//把对象名字改的和池子名字一样obj.name = name;}return obj;}/// <summary>/// 还给 暂时不用的东西回来/// </summary>/// <param name="name"></param>/// <param name="obj"></param>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));}}/// <summary>/// 清空缓存池的方法/// 主要用在 场景切换/// </summary>public void Clear(){poolDic.Clear();poolObj = null;}}
事件中心模块
主逻辑代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;/// <summary>
/// 事件中心 单例模式对象
/// 1.Dictionary
/// 2.委托
/// 3.观察者设计模式
/// </summary>
public class EventCenter : BaseManager<EventCenter>
{//key —— 事件的名字(比如:怪物死亡,玩家死亡,通关等等)//Value —— 对应的是 监听这个事件 对应的委托函数们private Dictionary<string, UnityAction<object>> eventDic = new Dictionary<string, UnityAction<object>>();/// <summary>/// 添加事件监听/// </summary>/// <param name="name">事件的名字</param>/// <param name="action">准备用来处理事件 的委托函数</param>public void AddEventListener(string name, UnityAction<object> action){//有没有对应的事件监听//有的情况if (eventDic.ContainsKey(name)){eventDic[name] += action;}//没有的情况else{eventDic.Add(name, action);}}/// <summary>/// 移除对应的事件监听/// </summary>/// <param name="name">事件的名字</param>/// <param name="action">对应之前添加的委托函数</param>public void RemoveEventListener(string name, UnityAction<object> action){if (eventDic.ContainsKey(name))eventDic[name] -= action;}/// <summary>/// 事件触发/// </summary>/// <param name="name">哪一个名字的事件触发了</param>public void EventTrigger(string name, object info){if (eventDic.ContainsKey(name)){//eventDic[name](); //这样写也可以eventDic[name].Invoke(info);}}/// <summary>/// 清空事件中心/// 主要用在 场景切换时/// </summary>public void Clear(){eventDic.Clear();}
}
测试
公共Mono模块
测试:
场景切换模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;/// <summary>
/// 场景切换模块
/// 知识点
/// 1.场景异步加载
/// 2.协程
/// 3.委托
/// </summary>
public class ScenesMgr : BaseManager<ScenesMgr>
{/// <summary>/// 切换场景 同步/// </summary>/// <param name="name"></param>/// <param name="fun"></param>public void LoadScene(string name, UnityAction fun){//场景同步加载SceneManager.LoadScene(name);//加载完成过后 才会去执行funfun();}/// <summary>/// 提供给外部的 异步加载的接口方法/// </summary>/// <param name="name"></param>/// <param name="fun"></param>public void LoadSceneAsyn(string name, UnityAction fun){MonoMgr.GetInstance().StartCoroutine(ReallyLoadSceneAsyn(name, fun));}/// <summary>/// 协程异步加载场景/// </summary>/// <param name="name"></param>/// <param name="fun"></param>/// <returns></returns>private IEnumerator ReallyLoadSceneAsyn(string name, UnityAction fun){AsyncOperation ao = SceneManager.LoadSceneAsync(name);//可以得到场景加载的一个进度while (!ao.isDone){//事件中心 向外分发 进度情况 外面想用就用EventCenter.GetInstance().EventTrigger("进度条更新", ao.progress);//这里去更新进度条yield return ao.progress;}//加载完成过后 才会去执行funfun();}
}
资源加载模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;/// <summary>
/// 资源加载模块
/// 1.异步加载
/// 2.委托和 lambad表达式
/// 3.协程
/// 4.泛型
/// </summary>
public class ResMgr : BaseManager<ResMgr>
{//同步加载资源public T Load<T>(string name) where T:Object{T res = Resources.Load<T>(name);//如果对象是一个GameObject 类型的 我们把它实例化后 再返回出去 外部就可以直接使用了if (res is GameObject)return GameObject.Instantiate(res);else //如果是 TextAsset(文本) AudioClip(音频) 就直接返回出去即可return res;}//异步加载资源public void LoadAsync<T>(string name, UnityAction<T> callback) where T:Object{//开启异步加载的协程MonoMgr.GetInstance().StartCoroutine(ReallyLoadAsync(name, callback));}//真正的协同程序函数 用于 开启异步加载对应的资源private IEnumerator ReallyLoadAsync<T>(string name, UnityAction<T> callback) where T : Object{ResourceRequest r = Resources.LoadAsync<T>(name);yield return r;if (r.asset is GameObject)callback(GameObject.Instantiate(r.asset) as T);elsecallback(r.asset as T);}
}
查缺补漏——更改缓存池模块
将缓存池模块中同步加载资源改成异步加载资源(用到资源加载模块)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;/// <summary>
/// 抽屉数据 池子中的一列数据
/// </summary>
public class PoolData
{//抽屉中 对象挂载的父节点public GameObject fatherObj;//对象的容器public List<GameObject> poolList;public PoolData(GameObject obj, GameObject poolObj){//给我们的抽屉 创建一个父对象 并且把他作为我们pool(衣柜)对象的子物体fatherObj = new GameObject(obj.name);fatherObj.transform.parent = poolObj.transform;poolList = new List<GameObject>();PushObj(obj);}/// <summary>/// 往抽屉里面 压东西/// </summary>/// <param name="obj"></param>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;}
}/// <summary>
/// 缓存池模块
/// 知识点
/// 1.Dictionary、List
/// 2.GameObject 和 Resources 两个公共类中的 API
/// </summary>
public class PoolMgr : BaseManager<PoolMgr>
{//缓存池容器 (衣柜)public Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();private GameObject poolObj;/// <summary>/// 往外拿东西/// </summary>/// <param name="name"></param>/// <returns></returns>public void GetObj(string name, UnityAction<GameObject> callBack){//有抽屉 并且抽屉里有东西if (poolDic.ContainsKey(name) && poolDic[name].poolList.Count > 0){//得到callBack(poolDic[name].GetObj());}else{//改动: 通过异步加载资源 创建对象给外部使用ResMgr.GetInstance().LoadAsync<GameObject>(name, (o)=>{o.name = name;callBack(o);});//没有就创建//obj = GameObject.Instantiate(Resources.Load<GameObject>(name));//把对象名字改的和池子名字一样//obj.name = name;}}/// <summary>/// 还给 暂时不用的东西回来/// </summary>/// <param name="name"></param>/// <param name="obj"></param>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));}}/// <summary>/// 清空缓存池的方法/// 主要用在 场景切换/// </summary>public void Clear(){poolDic.Clear();poolObj = null;}}
输入控制模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 1.Input类
/// 2.事件中心模块
/// 3.公共Mono模块的使用
/// </summary>
public class InputMgr : BaseManager<InputMgr>
{private bool isStart = false;public InputMgr(){MonoMgr.GetInstance().AddUpdateListener(MyUpdate);}public void StartOrEndCheck(bool isOpen){isStart = isOpen;}private void CheckKeyCode(KeyCode key){//事件中心模块 分发按下事件if (Input.GetKeyDown(key))EventCenter.GetInstance().EventTrigger("某键按下", key);//事件中心模块 分发抬起事件if (Input.GetKeyUp(key))EventCenter.GetInstance().EventTrigger("某键抬起", key);}private void MyUpdate(){//没有开启输入检测 就不去检测 直接returnif (!isStart)return;CheckKeyCode(KeyCode.W);CheckKeyCode(KeyCode.A);CheckKeyCode(KeyCode.S);CheckKeyCode(KeyCode.D);}
}
测试
查缺补漏——事件中心优化、避免装箱拆箱
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);}}/// <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();}}/// <summary>/// 清空事件中心/// 主要用在 场景切换时/// </summary>public void Clear(){eventDic.Clear();}
}
调用测试
音效管理模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;public class MusicMgr : BaseManager<MusicMgr>
{//唯一的背景音乐组件private AudioSource bkMusic = null;//音乐大小private float bkValue = 1;//音效依附对象private GameObject soundObj = null;//音效列表private List<AudioSource> soundList = new List<AudioSource>();//音效大小private float soundValue = 1;public MusicMgr(){MonoMgr.GetInstance().AddUpdateListener(Update);}private void Update(){for (int i = soundList.Count - 1; i >= 0; i--){if (!soundList[i].isPlaying){GameObject.Destroy(soundList[i]);soundList.RemoveAt(i);}}}/// <summary>/// 播放背景音乐/// </summary>/// <param name="name"></param>public void PlayBkMusic(string name){if (bkMusic == null){GameObject obj = new GameObject();obj.name = "BkMusic";bkMusic = obj.AddComponent<AudioSource>();}//异步加载背景音乐 加载完成后 播放ResMgr.GetInstance().LoadAsync<AudioClip>("Music/BK/" + name, (clip) =>{bkMusic.clip = clip;bkMusic.volume = bkValue;bkMusic.Play();bkMusic.loop = true;});}/// <summary>/// 暂停背景音乐/// </summary>public void PauseBKMusic(){if (bkMusic == null)return;bkMusic.Pause();}/// <summary>/// 停止背景音乐/// </summary>public void StopBkMusic(){if (bkMusic == null)return;bkMusic.Stop();}/// <summary>/// 改变背景音乐 音量大小/// </summary>/// <param name="v"></param>public void ChangeBKValue(float v){bkValue = v;if (bkMusic == null)return;bkMusic.volume = bkValue;}/// <summary>/// 播放音效/// </summary>/// <param name="name"></param>public void PlaySound(string name, bool isLoop, UnityAction<AudioSource> callBack = null){if (soundObj == null){soundObj = new GameObject();soundObj.name = "Sound";}//当音效资源加载结束后 再添加一个音效ResMgr.GetInstance().LoadAsync<AudioClip>("Music/Sound/" + name, (clip) =>{AudioSource source = soundObj.AddComponent<AudioSource>();source.clip = clip;source.loop = isLoop;source.volume = soundValue;source.Play();soundList.Add(source);if (callBack != null)callBack(source);});}/// <summary>/// 改变音效大小/// </summary>/// <param name="value"></param>public void ChangeSoundValue(float value){soundValue = value;for (int i = 0; i < soundList.Count; i++){soundList[i].volume = soundValue;}}/// <summary>/// 停止音效/// </summary>/// <param name="source"></param>public void StopSound(AudioSource source){if (soundList.Contains(source)){soundList.Remove(source);source.Stop();GameObject.Destroy(source);}}
}
测试
UI模块
一 先创建 BasePanel
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;/// <summary>
/// 面板基类
/// 找到所有自己面板下的控件对象
/// 它应该提供显示 或者隐藏的行为
///
/// 帮助我们通过代码快速的找到所有的子控件
/// 方便我们在子类中处理逻辑
/// 节约找控件的工作量
/// </summary>
public class BasePanel : MonoBehaviour
{//通过里氏转换原则 来存储所有的控件private Dictionary<string, List<UIBehaviour>> controlDic = new Dictionary<string, List<UIBehaviour>>();// Start is called before the first frame updatevoid Awake(){FindChildrenControl<Button>();FindChildrenControl<Image>();FindChildrenControl<Text>();FindChildrenControl<Toggle>();FindChildrenControl<Slider>();FindChildrenControl<ScrollRect>();}/// <summary>/// 得到对应名字的对应控件脚本/// </summary>/// <typeparam name="T"></typeparam>/// <param name="controlName"></param>/// <returns></returns>protected T GetControl<T>(string controlName) where T:UIBehaviour{if (controlDic.ContainsKey(controlName)){for (int i = 0; i < controlDic[controlName].Count; i++){if (controlDic[controlName][i] is T)return controlDic[controlName][i] as T;}}return null;}/// <summary>/// 显示自己/// </summary>public virtual void ShowMe(){//this.gameObject.SetActive(true);}/// <summary>/// 隐藏自己/// </summary>public virtual void HideMe(){}/// <summary>/// 找到子对象的对应控件 后添加/// </summary>/// <typeparam name="T"></typeparam>private void FindChildrenControl<T>() where T: UIBehaviour{T[] controls = this.GetComponentsInChildren<T>();string objName;for (int i = 0; i < controls.Length; i++){objName = controls[i].gameObject.name;if (controlDic.ContainsKey(objName))controlDic[objName].Add(controls[i]);elsecontrolDic.Add(controls[i].gameObject.name, new List<UIBehaviour>() { controls[i] });}}
}
测试
二:创建 UIManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;/// <summary>
/// UI层级
/// </summary>
public enum E_UI_Layer
{Bot,Mid,Top,System,
}/// <summary>
/// UI管理器
/// 1.管理所有显示的面板
/// 2.提供给外部 显示和隐藏等等接口
/// </summary>
public class UIManager : BaseManager<UIManager>
{public Dictionary<string, BasePanel> panelDic = new Dictionary<string, BasePanel>();//private Transform canvas;private Transform bot;private Transform mid;private Transform top;private Transform system;public UIManager(){//创建 Canvas 并让其过场景不被移除GameObject obj = ResMgr.GetInstance().Load<GameObject>("UI/Canvas");Transform canvas = obj.transform;GameObject.DontDestroyOnLoad(obj);//找到各层bot = canvas.Find("Bot");mid = canvas.Find("Mid");top = canvas.Find("Top");system = canvas.Find("System");//创建 EventSystem 并让其过场景不被移除obj = ResMgr.GetInstance().Load<GameObject>("UI/EventSystem");GameObject.DontDestroyOnLoad(obj);}/// <summary>/// 显示面板/// </summary>/// <typeparam name="T">面板脚本类型</typeparam>/// <param name="panelName">面板名</param>/// <param name="layer">显示在哪一层</param>/// <param name="callBack">当面板预设体创建成功后 你想要做的事情</param>public void ShowPanel<T>(string panelName, E_UI_Layer layer = E_UI_Layer.Mid, UnityAction<T> callBack = null) where T:BasePanel{if (panelDic.ContainsKey(panelName)){panelDic[panelName].ShowMe();//如果有 就直接处理面板创建后的逻辑if (callBack != null)callBack(panelDic[panelName] as T);}ResMgr.GetInstance().LoadAsync<GameObject>("UI/" + panelName, (obj) =>{//把它作为 Canvas的子对象//并且 要设置它的相对位置//找到父对象 到底显示在哪一层Transform father = bot;switch (layer){case E_UI_Layer.Mid:father = mid;break;case E_UI_Layer.Top:father = top;break;case E_UI_Layer.System:father = system;break;}//设置父对象 设置相对位置和大小obj.transform.SetParent(father);obj.transform.localPosition = Vector3.zero;obj.transform.localScale = Vector3.one;(obj.transform as RectTransform).offsetMax = Vector2.zero;(obj.transform as RectTransform).offsetMin = Vector2.zero;//得到预设体身上的面板脚本T panel = obj.GetComponent<T>();//处理面板创建完成后的逻辑if (callBack != null)callBack(panel);panel.ShowMe();//把面板存起来panelDic.Add(panelName, panel);});}/// <summary>/// 隐藏面板/// </summary>/// <param name="panelName"></param>public void HidePanle(string panelName){if (panelDic.ContainsKey(panelName)){panelDic[panelName].HideMe();GameObject.Destroy(panelDic[panelName].gameObject);panelDic.Remove(panelName);}}
}
测试
三:优化
UIManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;/// <summary>
/// UI层级
/// </summary>
public enum E_UI_Layer
{Bot,Mid,Top,System,
}/// <summary>
/// UI管理器
/// 1.管理所有显示的面板
/// 2.提供给外部 显示和隐藏等等接口
/// </summary>
public class UIManager : BaseManager<UIManager>
{public Dictionary<string, BasePanel> panelDic = new Dictionary<string, BasePanel>();//private Transform canvas;private Transform bot;private Transform mid;private Transform top;private Transform system;//记录我们UI的Canvas 父对象 方便以后外部可能会使用它public RectTransform canvas;public UIManager(){//创建 Canvas 并让其过场景不被移除GameObject obj = ResMgr.GetInstance().Load<GameObject>("UI/Canvas");canvas = obj.transform as RectTransform;GameObject.DontDestroyOnLoad(obj);//找到各层bot = canvas.Find("Bot");mid = canvas.Find("Mid");top = canvas.Find("Top");system = canvas.Find("System");//创建 EventSystem 并让其过场景不被移除obj = ResMgr.GetInstance().Load<GameObject>("UI/EventSystem");GameObject.DontDestroyOnLoad(obj);}public Transform GetLayerFather(E_UI_Layer layer){switch (layer){case E_UI_Layer.Bot:return this.bot;case E_UI_Layer.Mid:return this.mid;case E_UI_Layer.Top:return this.top;case E_UI_Layer.System:return this.system;}return null;}/// <summary>/// 显示面板/// </summary>/// <typeparam name="T">面板脚本类型</typeparam>/// <param name="panelName">面板名</param>/// <param name="layer">显示在哪一层</param>/// <param name="callBack">当面板预设体创建成功后 你想要做的事情</param>public void ShowPanel<T>(string panelName, E_UI_Layer layer = E_UI_Layer.Mid, UnityAction<T> callBack = null) where T:BasePanel{if (panelDic.ContainsKey(panelName)){panelDic[panelName].ShowMe();//如果有 就直接处理面板创建后的逻辑if (callBack != null)callBack(panelDic[panelName] as T);return;}ResMgr.GetInstance().LoadAsync<GameObject>("UI/" + panelName, (obj) =>{//把它作为 Canvas的子对象//并且 要设置它的相对位置//找到父对象 到底显示在哪一层Transform father = bot;switch (layer){case E_UI_Layer.Mid:father = mid;break;case E_UI_Layer.Top:father = top;break;case E_UI_Layer.System:father = system;break;}//设置父对象 设置相对位置和大小obj.transform.SetParent(father);obj.transform.localPosition = Vector3.zero;obj.transform.localScale = Vector3.one;(obj.transform as RectTransform).offsetMax = Vector2.zero;(obj.transform as RectTransform).offsetMin = Vector2.zero;//得到预设体身上的面板脚本T panel = obj.GetComponent<T>();//处理面板创建完成后的逻辑if (callBack != null)callBack(panel);panel.ShowMe();//把面板存起来panelDic.Add(panelName, panel);});}/// <summary>/// 隐藏面板/// </summary>/// <param name="panelName"></param>public void HidePanle(string panelName){if (panelDic.ContainsKey(panelName)){panelDic[panelName].HideMe();GameObject.Destroy(panelDic[panelName].gameObject);panelDic.Remove(panelName);}}/// <summary>/// 得到某个已经显示的面板 方便外部使用/// </summary>/// <typeparam name="T"></typeparam>/// <param name="name"></param>/// <returns></returns>public T GetPanel<T>(string name) where T:BasePanel{if (panelDic.ContainsKey(name))return panelDic[name] as T;return null;}
}
BasePanel
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;/// <summary>
/// 面板基类
/// 找到所有自己面板下的控件对象
/// 它应该提供显示 或者隐藏的行为
///
/// 帮助我们通过代码快速的找到所有的子控件
/// 方便我们在子类中处理逻辑
/// 节约找控件的工作量
/// </summary>
public class BasePanel : MonoBehaviour
{//通过里氏转换原则 来存储所有的控件private Dictionary<string, List<UIBehaviour>> controlDic = new Dictionary<string, List<UIBehaviour>>();// Start is called before the first frame updateprotected virtual void Awake(){FindChildrenControl<Button>();FindChildrenControl<Image>();FindChildrenControl<Text>();FindChildrenControl<Toggle>();FindChildrenControl<Slider>();FindChildrenControl<ScrollRect>();FindChildrenControl<InputField>();}/// <summary>/// 得到对应名字的对应控件脚本/// </summary>/// <typeparam name="T"></typeparam>/// <param name="controlName"></param>/// <returns></returns>protected T GetControl<T>(string controlName) where T:UIBehaviour{if (controlDic.ContainsKey(controlName)){for (int i = 0; i < controlDic[controlName].Count; i++){if (controlDic[controlName][i] is T)return controlDic[controlName][i] as T;}}return null;}/// <summary>/// 显示自己/// </summary>public virtual void ShowMe(){//this.gameObject.SetActive(true);}/// <summary>/// 隐藏自己/// </summary>public virtual void HideMe(){}protected virtual void OnClick(string btnName){}protected virtual void OnValueChanged(string toggleName, bool value){}/// <summary>/// 找到子对象的对应控件 后添加/// </summary>/// <typeparam name="T"></typeparam>private void FindChildrenControl<T>() where T: UIBehaviour{T[] controls = this.GetComponentsInChildren<T>();for (int i = 0; i < controls.Length; i++){string objName = controls[i].gameObject.name;if (controlDic.ContainsKey(objName))controlDic[objName].Add(controls[i]);elsecontrolDic.Add(controls[i].gameObject.name, new List<UIBehaviour>() { controls[i] });//如果是按钮控件if(controls[i] is Button){(controls[i] as Button).onClick.AddListener(()=>{OnClick(objName);});}//如果是单选框或者是多选框else if(controls[i] is Button){(controls[i] as Toggle).onValueChanged.AddListener((value) =>{OnValueChanged(objName, value);});}}}
}
测试
四:UIManager 优化 (拖拽事件的优化)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;/// <summary>
/// UI层级
/// </summary>
public enum E_UI_Layer
{Bot,Mid,Top,System,
}/// <summary>
/// UI管理器
/// 1.管理所有显示的面板
/// 2.提供给外部 显示和隐藏等等接口
/// </summary>
public class UIManager : BaseManager<UIManager>
{public Dictionary<string, BasePanel> panelDic = new Dictionary<string, BasePanel>();//private Transform canvas;private Transform bot;private Transform mid;private Transform top;private Transform system;//记录我们UI的Canvas 父对象 方便以后外部可能会使用它public RectTransform canvas;public UIManager(){//创建 Canvas 并让其过场景不被移除GameObject obj = ResMgr.GetInstance().Load<GameObject>("UI/Canvas");canvas = obj.transform as RectTransform;GameObject.DontDestroyOnLoad(obj);//找到各层bot = canvas.Find("Bot");mid = canvas.Find("Mid");top = canvas.Find("Top");system = canvas.Find("System");//创建 EventSystem 并让其过场景不被移除obj = ResMgr.GetInstance().Load<GameObject>("UI/EventSystem");GameObject.DontDestroyOnLoad(obj);}public Transform GetLayerFather(E_UI_Layer layer){switch (layer){case E_UI_Layer.Bot:return this.bot;case E_UI_Layer.Mid:return this.mid;case E_UI_Layer.Top:return this.top;case E_UI_Layer.System:return this.system;}return null;}/// <summary>/// 显示面板/// </summary>/// <typeparam name="T">面板脚本类型</typeparam>/// <param name="panelName">面板名</param>/// <param name="layer">显示在哪一层</param>/// <param name="callBack">当面板预设体创建成功后 你想要做的事情</param>public void ShowPanel<T>(string panelName, E_UI_Layer layer = E_UI_Layer.Mid, UnityAction<T> callBack = null) where T:BasePanel{if (panelDic.ContainsKey(panelName)){panelDic[panelName].ShowMe();//如果有 就直接处理面板创建后的逻辑if (callBack != null)callBack(panelDic[panelName] as T);return;}ResMgr.GetInstance().LoadAsync<GameObject>("UI/" + panelName, (obj) =>{//把它作为 Canvas的子对象//并且 要设置它的相对位置//找到父对象 到底显示在哪一层Transform father = bot;switch (layer){case E_UI_Layer.Mid:father = mid;break;case E_UI_Layer.Top:father = top;break;case E_UI_Layer.System:father = system;break;}//设置父对象 设置相对位置和大小obj.transform.SetParent(father);obj.transform.localPosition = Vector3.zero;obj.transform.localScale = Vector3.one;(obj.transform as RectTransform).offsetMax = Vector2.zero;(obj.transform as RectTransform).offsetMin = Vector2.zero;//得到预设体身上的面板脚本T panel = obj.GetComponent<T>();//处理面板创建完成后的逻辑if (callBack != null)callBack(panel);panel.ShowMe();//把面板存起来panelDic.Add(panelName, panel);});}/// <summary>/// 隐藏面板/// </summary>/// <param name="panelName"></param>public void HidePanle(string panelName){if (panelDic.ContainsKey(panelName)){panelDic[panelName].HideMe();GameObject.Destroy(panelDic[panelName].gameObject);panelDic.Remove(panelName);}}/// <summary>/// 得到某个已经显示的面板 方便外部使用/// </summary>/// <typeparam name="T"></typeparam>/// <param name="name"></param>/// <returns></returns>public T GetPanel<T>(string name) where T:BasePanel{if (panelDic.ContainsKey(name))return panelDic[name] as T;return null;}/// <summary>/// 给控件添加自定义事件监听/// </summary>/// <param name="control">控件对象</param>/// <param name="type">事件类型</param>/// <param name="callBack">事件的响应函数</param>public static void AddCustomEventListener(UIBehaviour control, EventTriggerType type, UnityAction<BaseEventData> callBack){EventTrigger trigger = control.GetComponent<EventTrigger>();if (trigger == null)trigger = control.gameObject.AddComponent<EventTrigger>();EventTrigger.Entry entry = new EventTrigger.Entry();entry.eventID = type;entry.callback.AddListener(callBack);trigger.triggers.Add(entry);}
}
测试