Unity程序基础框架

devtools/2024/9/19 23:01:38/

概述

单例模式基类

没有继承 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);}
}

测试

打包导出


http://www.ppmy.cn/devtools/113133.html

相关文章

Qt 基础按钮布局管理

cpp public: Content(QWidget *parent0); ~Content(); QStackedWidget *stack; QPushButton *AmendBtn; QPushButton *CloseBtn; Baseinfo *baseInfo; Contact *contact; Detail *detail; // 打开 "Content.cpp" 文件&#xff0c;添加如下代码&#xff1a; Content:…

RabbitMQ(高阶使用)死信队列

文章内容是学习过程中的知识总结&#xff0c;如有纰漏&#xff0c;欢迎指正 文章目录 一、什么是死信队列&#xff1f; 二、死信队列使用场景 三、死信队列如何使用 四、打车超时处理 1.打车超时实现 以下是本篇文章正文内容 一、什么是死信队列&#xff1f; 先从概念解释上搞…

python教程(二):python数据结构大全(附代码)

Python 中数据结构的重要性不言而喻&#xff0c;它们是构建高效、可维护代码的基础。数据结构决定了如何存储、组织和操作数据。理解和使用合适的数据结构能够极大地提升程序的性能、简洁性以及代码的可读性。 Python 的基础数据结构有 4 种&#xff0c;分别是 列表 (list)、元…

Gateway学习笔记

目录 介绍&#xff1a; 核心概念 依赖 路由 断言 基本的断言工厂 自定义断言 过滤器 路由过滤器 过滤器工厂 自定义路由过滤器 全局过滤器 其他 过滤器执行顺序 前置后置&#xff08;&#xff1f;&#xff09; 跨域问题 yaml 解决 配置类解决 介绍&#x…

tcp线程进程多并发

tcp线程多并发 #include<myhead.h> #define SERPORT 8888 #define SERIP "192.168.0.118" #define BACKLOG 20 typedef struct { int newfd; struct sockaddr_in cin; }BMH; void *fun1(void *sss) { int newfdaccept((BMH *)sss)->newfd; …

C语言:结构体

一、结构体的概念和定义 1. 为什么要定义结构体 结构体是由用户自己定义&#xff08;设计&#xff09;的数据类型。 其实就是各种信息的打包。比如说&#xff0c;每个学生都有学号、姓名和成绩&#xff0c;100个学生就有100份这种数据&#xff0c;打包起来整合就会方便很多。…

Leetcode面试经典150题-349.两个数组的交集

题目比较简单&#xff0c;散散心吧 解法都在代码里&#xff0c;不懂就留言或者私信 class Solution {public int[] intersection(int[] nums1, int[] nums2) {/**先排个序 */Arrays.sort(nums1);Arrays.sort(nums2);int curIndex1 0;int curIndex2 0;/**先把数组的大小设置…

LIMS实验室管理系统的特点

LIMS实验室管理系统在实验室管理中发挥着不可或缺的作用。首要特点是其强大的自动化数据管理功能&#xff0c;该系统能够无缝集成实验室从样品接收到测试结果录入与存储的全过程&#xff0c;显著提升了数据的准确性和可靠性&#xff0c;减少了人为错误的可能性。 流程优化是LI…