Unity程序基础框架

概述

单例模式基类

没有继承 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.rhkb.cn/news/423654.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++STL~~stackqueue

文章目录 容器适配器一、stack&queue的概念二、stack&queue的使用三、stack&queue的练习四、总结 容器适配器 什么是适配器 适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结)&#xff0c;该种模式是将一个类…

Leetcode 移动零

要求将数组中的所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。下面是该题的 C 解决方案&#xff1a; class Solution { public:void moveZeroes(vector<int>& nums) {int nonZeroPos 0; // 记录非零元素应该放置的位置// 遍历数组&#xff0c;…

【北京迅为】《STM32MP157开发板使用手册》- 第三十章Cortex-M4通用定时器实验

iTOP-STM32MP157开发板采用ST推出的双核cortex-A7单核cortex-M4异构处理器&#xff0c;既可用Linux、又可以用于STM32单片机开发。开发板采用核心板底板结构&#xff0c;主频650M、1G内存、8G存储&#xff0c;核心板采用工业级板对板连接器&#xff0c;高可靠&#xff0c;牢固耐…

相图的科学应用,陶瓷材料创新

陶瓷材料因其优异的物理和化学性能&#xff0c;在航空航天、电子、生物医学等多个领域展现出广阔的应用前景。陶瓷材料的性能很大程度上取决于其微观结构&#xff0c;包括晶粒大小、相组成和分布。相图作为描述陶瓷材料在不同条件下的相变行为和相平衡关系的图表反映了陶瓷材料…

Element-ui el-table 全局表格排序

实现效果如下&#xff1a; 一、当页数据排序 如果只想要当前页面排序&#xff0c;只会涉及到前端&#xff0c;只需在<el-table-column>标签上添加 :sortable"true"即可 二、自定义排序 如果想要全局排序&#xff0c;需要自定义排序函数&#xff0c;请求后台排…

Springboot项目打war包运行及错误解决

一,打war包 1. 修改pom.xml 为了不影响原pom.xml, 我复制了一个文件叫pom_war.xml , 需要打war包就采用pom_war.xml进行打war包, 你也可以直接修改pom.xml ① 打包方式改为war 没有就增加此配置 <packaging>war</packaging> ② 排除内嵌tomcat依赖 <de…

怎样在备忘录中添加提醒?怎么设置备忘录提醒

备忘录作为我们日常生活中常用的软件&#xff0c;其记录事项的便捷性已经得到了广泛认可。无论是工作计划、购物清单还是个人日记&#xff0c;备忘录都能帮助我们将这些信息快速记录下来。然而&#xff0c;如果备忘录能够进一步提供提醒功能&#xff0c;那么它将变得更加实用&a…

122.rk3399 uboot(2017.09) 源码分析2-initf_dm(2024-09-09)

这里接着上一篇来吧&#xff1a; https://blog.csdn.net/zhaozhi0810/article/details/141927053 本文主要是dm_init_and_scan函数的分析&#xff0c;这个内容比较复杂&#xff0c;我也是第一次阅读&#xff0c;错误之处在所难免&#xff0c;请多指教。 uboot的dm框架需要了解…

MyBatis 面试题11-27

11、Mybatis 是如何将 sql 执行结果封装为目标对象并返回的? 都有哪些映射形式&#xff1f; Mybatis 在执行 SQL 查询后&#xff0c;会将结果集封装为目标对象并返回。这主要依赖于 Mybatis 的映射机制&#xff0c;它提供了两种主要的映射形式&#xff1a; 第一种&#xff1…

代码质量护航:结合Checkstyle、SpringBoot与Git的最佳实践

在团队开发中&#xff0c;保持一致的代码风格和高质量的代码至关重要。为了提升团队的整体代码质量&#xff0c;防止低质量代码的提交&#xff0c;使用工具对代码进行自动化检查是非常有效的手段之一。在这篇博客中&#xff0c;我将介绍如何通过结合 Checkstyle、Spring Boot 和…

Zotero使用(一)PDF文件导入不会自动识别

上面两种&#xff0c;一种中文&#xff0c;一种英文&#xff0c;会发现&#xff0c;中文的导入进去之后不会自动识别&#xff0c;部分英文也是。不能自动识别就会缺少导出参考文献的功能&#xff0c;怎么办&#xff1f; 发现之前导入喜欢使用PDF格式 可以结合.ris格式&#xf…

网络安全 day5 --- 反弹SHELL不回显带外正反向连接防火墙出入站文件下载

免责声明 本免责声明适用于作者所有文章内容。使用者需明确&#xff0c;网络安全技术仅供学习和合法研究使用&#xff0c;不得用于任何非法活动&#xff0c;如未经授权的入侵、攻击或数据窃取&#xff0c;所有相关法律责任由使用者自行承担。由于网络安全操作可能带来系统崩溃、…

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统 在产品将要上线之前&#xff0c;需要制作不同类型格式的根文件系统 在产品研发阶段&#xff0c;我们还是需要使用nfs的方式挂载根文件系统 优点&#xff1a;可以直接在上位机中修改文件系统内容&#xff0c;延长EMMC的寿命 【1】重启上位机nfs服…

【计算机网络】HTTPHTTPS

HTTP&HTTPS HTTP协议初识HTTP如何抓包Fiddler的使用抓包查看包的信息 报文格式请求报文响应报文报文对比 URLHTTP方法认识Header初识状态码 HTTPS协议为什么需要 HTTPS加密基础知识HTTPS的工作流程引入对称加密引入非对称加密引入证书HTTPS 的工作流程 浏览器从输入URL到展…

短视频剪辑从简单到复杂,这四款很OK!

作为一个刚刚踏入视频剪辑世界的新手&#xff0c;我最近可是忙得不亦乐乎。我尝试了四款流行的视频剪辑软件&#xff0c;今天&#xff0c;就让我来和大家分享一下我的使用感受&#xff0c;看看哪款软件更适合我们这些初学者。这里先说一句&#xff0c;选择视频剪辑软件就像挑衣…

opencv学习:calcHist 函数绘制图像直方图及代码实现

cv2.calcHist 函数是 OpenCV 库中用于计算图像直方图的函数。直方图是一种统计图像中像素值分布的工具&#xff0c;它可以提供图像的亮度、颜色等信息。这个函数可以用于灰度图像和彩色图像。 函数语法 hist cv2.calcHist(images, channels, mask, histSize, ranges, accumu…

解决 PyCharm 无法启动 Jupyter 服务器的问题:报错分析与解决方案

文章目录 报错背景报错详细信息解决方案pycharm 设置 报错背景 在使用 pycharm 付费版的过程中&#xff0c;发现一直无法启动 jupyter 服务器。 一直也不知道是为什么&#xff0c;直到在终端输入&#xff1a; jupyter notebook发现 jupyter 服务无法启动。 报错详细信息 下…

数据库系列之GaussDB数据库中逻辑对象关系简析

初次接触openGauss或GaussDB数据库的逻辑对象&#xff0c;被其中的表空间、数据库、schema和用户之间的关系&#xff0c;以及授权管理困惑住了&#xff0c;与熟悉的MySQL数据库的逻辑对象又有明显的不同。本文旨在简要梳理下GaussDB数据库逻辑对象之间的关系&#xff0c;以加深…

浅谈EXT2文件系统(1)

简介 EXT2&#xff08;Second Extended Filesystem&#xff09;文件系统是Linux操作系统的早期文件系统之一&#xff0c;它于 1993 年推出&#xff0c;是第一个旨在克服 Ext 文件系统限制的商业文件系统。EXT2 没有日志功能&#xff0c;EXT2 支持的单个文件大小为 2TB&#xf…

如何在Word中插入复选框

如何在Word中插入复选框&#xff1a;详细教程与技巧 在Word中插入复选框是一项非常实用的技巧&#xff0c;尤其是在制作问卷调查、待办事项清单、交互式表单或文档中需要用户进行选择时&#xff0c;复选框不仅能提高文档的功能性&#xff0c;还能显得更加专业。本文将详细讲解…