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);
//加载完成过后 才会去执行fun
fun();
}
/// <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;
}
//加载完成过后 才会去执行fun
fun();
}
}
资源加载模块
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);
else
callback(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()
{
//没有开启输入检测 就不去检测 直接return
if (!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 update
void 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]);
else
controlDic.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 update
protected 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]);
else
controlDic.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);
}
}
测试