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);}
}
测试
打包导出
相关文章:

Unity程序基础框架
概述 单例模式基类 没有继承 MonoBehaviour 继承了 MonoBehaviour 的两种单例模式的写法 缓存池模块 (确实挺有用) using System.Collections; using System.Collections.Generic; using UnityEngine;/// <summary> /// 缓存池模块 /// 知识点 //…...

TiDB 数据库核心原理与架构_Lesson 01 TiDB 数据库架构概述课程整理
作者: 尚雷5580 原文来源: https://tidb.net/blog/beeb9eaf 注:本文基于 TiDB 官网 董菲老师 《TiDB 数据库核心原理与架构(101) 》系列教程之 《Lesson 01 TiDB 数据库架构概述》内容进行整理和补充。 课程链接:…...

计算机毕业设计Python深度学习垃圾邮件分类检测系统 朴素贝叶斯算法 机器学习 人工智能 数据可视化 大数据毕业设计 Python爬虫 知识图谱 文本分类
基于朴素贝叶斯的邮件分类系统设计 摘要:为了解决垃圾邮件导致邮件通信质量被污染、占用邮箱存储空间、伪装正常邮件进行钓鱼或诈骗以及邮件分类问题。应用Python、Sklearn、Echarts技术和Flask、Lay-UI框架,使用MySQL作为系统数据库,设计并实…...

多核DSP(6000系列)设计与调试技巧培训
课程介绍: 为帮助从事DSP开发工程师尽快将DSP技术转化为产品,在较短时间内掌握DSP设计技术和问题的解决方法,缩短产品开发周期、增强产品竞争力、节省研发经费。我们特组织了工程实践和教学经验丰富的专家连续举办了多期DSP C6000的培训&a…...

JMeter脚本开发
环境部署 Ubuntu系统 切换到root用户 sudo su 安装上传下载的命令 apt install lrzsz 切换文件目录 cd / 创建文件目录 mkdir java 切换到Java文件夹下 cd java 输入rz回车 选择jdk Linux文件上传 解压安装包 tar -zxvf jdktab键 新建数据库 运行sql文件 选择sql文件即…...

LabVIEW编程快速提升的关键技术
在LabVIEW程序员的成长道路上,以下几个概念和技术的掌握可以显著提升自我能力: 模块化编程:学会将程序分解成小而独立的模块(如子VI),提高程序的可读性、可维护性和可扩展性。这种方式不仅能帮助快速定位问…...
BSN六周年:迈向下一代互联网
当前,分布式技术作为现代计算机科学和信息技术的重要组成部分,在云计算、区块链等技术的推动下,正以多样化的形式蓬勃发展。 而区块链作为一种特殊的分布式系统,近年来也在各个领域得到了广泛关注。通过在区块链上运行智能合约…...

Android 使用scheme唤起app本地打开
记录一下近期任务。。。 以下操作全部基于手机本地已经安装对应app方可执行。 没安装建议web前端校验一下跳动app下载页吧。 AndroidManifest配置如下: <activity android:name".RouterActivity"><intent-filter><dataandroid:host&quo…...

linux 最简单配置免密登录
需求:两台服务器互信登录需要拉起对端服务 ip: 192.168.1.133 192.168.1.137 一、配置主机hosts,IP及主机名,两台都需要 二、192.168.1.137服务器,生成密钥 ssh-keygen -t rsa三、追加到文件 ~/.ssh/authorized_key…...

易语言源码用键盘按键代替小键盘写法教程
相信大家都有遇到过一些难题 比方说想用一些软件 但是发现一些软件需要有小键盘的用户才能使用 那么这样就对于一些无小键盘用户造成了困扰! 今天就给大家分享一个用易语言写的利用软键盘方法 当按下一个按键启动其他热键的方法 以下为源码写法 .版本 2 .支持库 she…...

深度学习和计算机视觉:实现图像分类
深度学习在计算机视觉领域的应用已经取得了革命性的进展。从图像分类到对象检测,再到图像分割和生成,深度学习模型在这些任务中都展现出了卓越的性能。本篇文章将介绍如何使用深度学习进行图像分类,这是计算机视觉中的一个基础任务。 计算机…...

代码随想录算法训练营第五十八天 | 拓扑排序精讲-软件构建
目录 软件构建 思路 拓扑排序的背景 拓扑排序的思路 模拟过程 判断有环 写代码 方法一: 拓扑排序 软件构建 题目链接:卡码网:117. 软件构建 文章讲解:代码随想录 某个大型软件项目的构建系统拥有 N 个文件,文…...

Spring Cloud常见面试题
1.请说说你用过Spring Cloud哪些组件?这些组件分别有什么作用? 1、注册中心:Eureka、Nacos、Zookeeper、Consul;(服务注册) 2、负载均衡:Ribbon、LoadBalancer;(客户端的…...

老古董Lisp实用主义入门教程(9): 小小先生学习Lisp表达式
小小先生 小小先生个子很小,胃口也很小,每次只能干一件事情,还是一件很小很小的事情。 好奇先生已经把explore-lisp代码库安装好,小小先生就只需要打开VS Code, 新建一个lisp为后缀的文件,就能够开始写Lisp代码。 c…...

基于YOLOV8+Pyqt5光伏太阳能电池板目标检测系统
基于YOLOV8Pyqt5光伏太阳能电池板目标检测系统 高质量太阳能光伏电池板可见光图像数据集,标签包含鸟粪,清洁,脏污,电气损坏,物理损坏,积雪覆盖六类。用于目标检测,缺陷检测,异物检测…...

【C++ 设计模式】单例模式的两种懒汉式和饿汉式
文章目录 1. 单例模式2. 单例模式简单示例3. 懒汉模式4. 饿汉模式5. 懒汉式和饿汉式的区别 1. 单例模式 🐧定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。 单例模式是一种常用的软件设计模式,在它的核心结构中只包…...

计算机的错误计算(九十三)
摘要 探讨 log(y,x) 即以 x 为底 y 的对数的计算精度问题。 Log(y,x)运算是指 x 为底 y 的对数。 例1. 计算 log(123667.888, 0.999999999999999) . 不妨在Python中计算,则有: 若在 Excel 单元格中计算,则有几乎同样的输出: 然…...

基于SpringBoot+Vue的牙科就诊管理系统(带1w+文档)
基于SpringBootVue的牙科就诊管理系统(带1w文档) 基于SpringBootVue的牙科就诊管理系统(带1w文档) 伴随着互联网发展,现今信息类型愈来愈多,信息量也非常大,那也是信息时代的缩影。近些年,电子元器件信息科学合理发展的趋势变的越…...

微信小程序使用 ==== 粘性布局
目录 Chrome杀了个回马枪 position:sticky简介 你可能不知道的position:sticky 深入理解粘性定位的计算规则 粘性定位其他特征 代码实现 微信小程序在scroll-view中使用sticky Chrome杀了个回马枪 position:sticky早有耳闻也有所了解,后来,Chro…...

LineageOS刷机教程
版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/ LineageOS 是一个基于 Android 开源项目(AOSP)的开源操作系统,主要由社区开发者维护。它起源于 CyanogenMod 项目ÿ…...

Unity3D帧同步模式的网络游戏详解
帧同步概述 帧同步(Frame Synchronization)是指在网络游戏中,多个客户端在同一时刻执行相同的游戏逻辑,确保各个客户端的游戏状态保持一致。这种同步方式对于实现公平的多人游戏和减少网络延迟对游戏体验的影响至关重要。Unity3D…...

“树”据结构:并查集从入门到AC
“树”据结构:并查集 前言算法设计代码示例优化相关文章 前言 在一组数据中,数据被分为了不同的集合,那么其中的集合往往可以用树形来表示。而区分集合,与查找集合的元素,就会成为核心的问题。并查集主要就是解决这类…...

高级java每日一道面试题-2024年9月11日-数据库篇-事务回滚的常见原因有哪些?
如果有遗漏,评论区告诉我进行补充 面试官: 事务回滚的常见原因有哪些? 我回答: 在Java高级面试中,讨论事务回滚的常见原因是考察候选人对事务管理的理解深度。事务回滚意味着事务中的所有操作都会被撤销,回到事务开始前的状态。以下是事务…...

目标检测中的解耦和耦合、anchor-free和anchor-base
解耦和耦合 写在前面 在目标检测中,objectness(或 objectness score)指的是一个评分,用来表示某个预测框(bounding box)中是否包含一个目标物体。 具体来说,YOLO等目标检测算法需要在每个候选区…...

git rev-parse
git rev-parse 是 Git 中一个非常有用的命令,用于解析并返回与 Git 对象(如提交、分支、标签等)相关的信息。它可以帮助我们从给定的引用(ref)中解析出 SHA-1 哈希值、路径信息等。这个命令在编写 Git 脚本时尤其有用&…...

【Unity】在Unity 3D中使用Spine开发2D动画
文章目录 内容概括前言下载安装 Spine Pro导入Unity插件Spine动画导入Unity使用展现动画效果展现 内容概括 本文主要讲解 Spine Pro 免(破)费(解)版的安装,以及如何将动画导入到Unity中使用。 前言 通常要用 Spine …...

考试:软件工程(01)
软件开发生命周期 ◆软件定义时期:包括可行性研究和详细需求分析过程,任务是确定软件开发工程必须完成的总目标, 具体可分成问题定义、可行性研究、需求分析等。 ◆软件开发时期:就是软件的设计与实现,可分成概要设计…...

数据结构应用实例(三)——赫夫曼编码
Content: 一、问题描述二、算法思想三、代码实现四、小结 一、问题描述 对一篇英文文章,统计各字符(仅限于26个小写字母)出现的次数,并据此进行 Huffman 编码。 二、算法思想 首先,打开文本文件࿰…...

关于Spring Cloud Gateway中 Filters的理解
Spring Cloud Gateway中 Filters的理解 Filters Filters拦截器的作用是,对请求进行处理 可以进行流量染色 ⭐增加请求头 例子 spring:cloud:gateway:routes:- id: add_request_header_routeuri: http://localhost:8123predicates:- Path/api/**filters:- AddR…...

【实践】应用访问Redis突然超时怎么处理?
目录标题 问题描述分析过程查看监控数据系统监控指标JVM监控指标Redis监控指标分析应用异常单机异常规律集群异常规律统计超时的key 初步结论验证结论访问Redis链路slowlogRedis单节点info all定位redis节点定位异常keybigkeystcpdump定位大key影响 经验总结 问题描述 某产品线…...