Unity程序基础框架
单例模式基类(Base文件夹)
不继承Mono的单例模式基类
using System;
using System.Reflection;
using UnityEngine;
/// <summary>
/// 单例模式基类 避免代码冗余
/// </summary>
/// <typeparam name="T">继承class类 用于创建实例</typeparam>
public abstract class BaseManager<T> where T : class
{
private static T instance;
//用于加锁的对象
protected static readonly object lockObj = new object();
//属性的方式
public static T Instance
{
get
{
if (instance == null)
{
lock (lockObj)
{
if (instance == null)
{
//通过反射创建实例
//方法一 通过Activator.CreateInstance直接实例化 参数true:允许反射实例化私有构造函数(推荐使用)
instance = (T)Activator.CreateInstance(typeof(T), true);
//方法二 利用反射获取私有构造函数 通过私有构造函数实例对象(不推荐使用)
//Type type = typeof(T);
//ConstructorInfo constructor = type.GetConstructor(
// BindingFlags.Instance | BindingFlags.NonPublic, //表示成员私有方法
// null, //表示没有绑定对象
// Type.EmptyTypes, //表示没有参数
// null); //表示没有参数修饰符
//if (constructor != null)
//{
// instance = (T)constructor.Invoke(null);
//}
//else
//{
// Debug.LogError("没有得到相应的私有构造函数");
//}
}
}
}
return instance;
}
}
}
继承Mono的手动挂载式单例模式基类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 挂载式 继承MonoBehaviour基类
/// </summary>
/// <typeparam name="T"></typeparam>
public class SingletonMono<T> : MonoBehaviour where T : MonoBehaviour
{
private static T instance;
public static T Instance => instance;
protected virtual void Awake()
{
if (instance != null)
{
Destroy(this.gameObject);
return;
}
instance = this as T;
DontDestroyOnLoad(this.gameObject);
}
}
继承Mono的自动挂载式单例模式基类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 自动挂载 继承MonoBehaviour基类 推荐使用 无需手动挂载脚本 无需考虑切场景带来的问题
/// </summary>
/// <typeparam name="T"></typeparam>
public class SingletonAutoMono<T> : MonoBehaviour where T : MonoBehaviour
{
private static T instance;
public static T Instance
{
get
{
if (instance == null)
{
//动态创建物体
GameObject obj = new GameObject(typeof(T).Name);
//得到T脚本 的类名 为对象改名 这样在编辑器中可以明确的看到 该单例模式脚本对象依附的GameObject
obj.name = typeof(T).Name;
//为物体添加脚本
instance = obj.AddComponent<T>();
//过场景不移除对象
DontDestroyOnLoad(obj);
}
return instance;
}
}
}
缓存池(对象池)模块(Pool文件夹)
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// 抽屉对象
/// </summary>
public class PoolData
{
//抽屉根对象 用来布局
private GameObject rootObj;
//用来存储抽屉中对象的容器
private Stack<GameObject> dataList = new Stack<GameObject>();
//获取容器中对象的数量
public int Count => dataList.Count;
//传入name作为抽屉对象物体名 传入poolObj作为抽屉对象的父对象
public PoolData(string name, GameObject poolObj)
{
if (PoolMgr.isOpenLayout)
{
rootObj = new GameObject(name);
rootObj.transform.parent = poolObj.transform;
}
}
/// <summary>
/// 取出容器中对象
/// </summary>
public GameObject GetObj()
{
//取出对象物体
GameObject obj = dataList.Pop();
//激活取出的对象
obj.SetActive(true);
if (PoolMgr.isOpenLayout)
{
//断开取出对象和抽屉对象的父子关系
obj.transform.parent = null;
}
return obj;
}
/// <summary>
/// 将对象放入抽屉中
/// </summary>
/// <param name="obj"></param>
public void PushObj(GameObject obj)
{
//失活放入的对象
obj.SetActive(false);
if (PoolMgr.isOpenLayout)
{
//建立放入对象和抽屉对象的父子关系
obj.transform.parent = rootObj.transform;
}
//将放入对象存入容器dataList中
dataList.Push(obj);
}
}
/// <summary>
/// 缓存池模块 管理器
/// </summary>
public class PoolMgr
{
private static PoolMgr instance;
public static PoolMgr Instance
{
get
{
if (instance == null)
{
instance = new PoolMgr();
}
return instance;
}
}
//柜子容器当中有抽屉的体现
private Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();
private GameObject poolObj;
//是否开启布局功能
public static bool isOpenLayout = false;
private PoolMgr() { }
/// <summary>
/// 拿东西的方法
/// </summary>
/// <param name="name">抽屉容器的名字</param>
/// <returns>从缓存池中取出的对象</returns>
public void GetObj(string name, UnityAction<GameObject> callBack)
{
GameObject obj;
//有抽屉 并且 抽屉里 有对象 才去直接拿(同步加载资源)
if (poolDic.ContainsKey(name) && poolDic[name].Count > 0)
{
//弹出栈中的对象 直接返回给外部使用
obj = poolDic[name].GetObj();
//取出缓存池对象操作 可用于对象初始化(避免激活失活无法运行Start初始化)
callBack?.Invoke(obj);
}
//否则 就应该去创造
else
{
//异步加载资源
ResourceRequest rq = Resources.LoadAsync<GameObject>(name);
rq.completed += (a) =>
{
obj = GameObject.Instantiate(rq.asset as GameObject);
obj.name = name;
//取出缓存池对象操作 可用于对象初始化(避免激活失活无法运行Start初始化)
callBack?.Invoke(obj);
};
}
}
/// <summary>
/// 往缓存池中放入对象
/// </summary>
/// <param name="name">抽屉(对象)的名字</param>
/// <param name="obj">希望放入的对象 </param>
public void PushObj(GameObject obj)
{
//放入缓存池对象相关操作
//callBack?.Invoke(obj);
if (poolObj == null && isOpenLayout)
{
poolObj = new GameObject("Pool");
}
//没有抽屉 创建抽屉
if (!poolDic.ContainsKey(obj.name))
{
poolDic.Add(obj.name, new PoolData(obj.name, poolObj));
}
//往抽屉当中放对象
poolDic[obj.name].PushObj(obj);
}
/// <summary>
/// 用于清除整个柜子当中的数据
/// 主要是 切场景时 使用
/// </summary>
public void ClearPool()
{
poolDic.Clear();
poolObj = null;
}
}
事件中心模块(Event文件夹)
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
{
private static EventCenter instance;
public static EventCenter Instance
{
get
{
if (instance == null)
{
instance = new EventCenter();
}
return instance;
}
}
// key——事件的名字(比如:怪物死亡 玩家死亡 通过 等等)
// value——对应的是 监听这个事件 对应的委托函数们
private Dictionary<string, IEventInfo> eventDic = new Dictionary<string, IEventInfo>();
private EventCenter() { }
/// <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>
/// <param obj="obj">传入回调的本对象</param>
public void EventTrigger<T>(string name, T obj)
{
// 有没有对应的事件监听
// 有的情况
if (eventDic.ContainsKey(name))
{
if ((eventDic[name] as EventInfo<T>).actions != null)
{
(eventDic[name] as EventInfo<T>).actions.Invoke(obj);
}
}
}
/// <summary>
/// 事件触发(不需要参数的)
/// </summary>
/// <param name="name">哪一个名字的事件触发了</param>
/// <param obj="obj">传入回调的本对象</param>
public void EventTrigger(string name)
{
// 有没有对应的事件监听
// 有的情况
if (eventDic.ContainsKey(name))
{
if ((eventDic[name] as EventInfo).actions != null)
{
(eventDic[name] as EventInfo).actions.Invoke();
}
}
}
/// <summary>
/// 清空事件中心
/// 主要用在 场景切换时
/// </summary>
public void Clear()
{
eventDic.Clear();
}
}
公共Mono模块(Mono文件夹)
MonoMgr.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Internal;
// 为什么不将MonoMgr 和 MonoController合并
// 生命周期管理不够灵活
// 问题点
// 当你把管理器和控制器合并成一个类时,这个单例 MonoBehaviour 的生命周期就和它挂载的 GameObject 完全绑定。
// 如果场景切换时没有 DontDestroyOnLoad,所有的帧更新事件、协程都会被销毁。
// 如果场景切换时加了 DontDestroyOnLoad,但你想要某些帧更新事件只在当前场景生效,就比较麻烦,需要手动移除。
/// <summary>
/// 1. 可以提供给外部添加帧更新事件的方法
/// 2. 可以提供给外部添加 协程的方法
/// </summary>
public class MonoMgr
{
private static MonoMgr instance;
public static MonoMgr Instance
{
get
{
if (instance == null)
instance = new MonoMgr();
return instance;
}
}
private MonoController controller;
private MonoMgr()
{
GameObject obj = new GameObject("MonoController");
controller = obj.AddComponent<MonoController>();
}
/// <summary>
/// 提供给外部 添加帧更新事件的函数
/// </summary>
/// <param name="fun"></param>
public void AddUpdateListener(UnityAction fun)
{
controller.AddUpdateListener(fun);
}
/// <summary>
/// 提供 给外部 用于移除帧更新事件的函数
/// </summary>
/// <param name="fun"></param>
public void RemoveUpdateListener(UnityAction fun)
{
controller.RemoveUpdateListener(fun);
}
/// <summary>
/// 提供给外部 用于协程
/// </summary>
/// <param name="routine"></param>
/// <returns></returns>
public Coroutine StartCoroutine(IEnumerator routine)
{
return controller.StartCoroutine(routine);
}
public Coroutine StartCoroutine(string methodName, [DefaultValue("null")] object value)
{
return controller.StartCoroutine(methodName, value);
}
public Coroutine StartCoroutine(string methodName)
{
return controller.StartCoroutine(methodName);
}
}
MonoController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// Mono的管理者
/// </summary>
public class MonoController : MonoBehaviour
{
private event UnityAction updateEvent;
// Start is called before the first frame update
void Start()
{
DontDestroyOnLoad(gameObject);
}
// Update is called once per frame
void Update()
{
updateEvent?.Invoke();
}
/// <summary>
/// 提供给外部 添加帧更新事件的函数
/// </summary>
/// <param name="fun"></param>
public void AddUpdateListener(UnityAction fun)
{
updateEvent += fun;
}
/// <summary>
/// 提供 给外部 用于移除帧更新事件的函数
/// </summary>
/// <param name="fun"></param>
public void RemoveUpdateListener(UnityAction fun)
{
updateEvent -= fun;
}
}
场景切换模块(Scenes文件夹)
using System.Collections;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
public class SceneMgr
{
private static SceneMgr instance;
public static SceneMgr Instance
{
get
{
if(instance == null)
{
instance = new SceneMgr();
}
return instance;
}
}
private SceneMgr() { }
/// <summary>
/// 切换场景 同步
/// </summary>
/// <param name="name">场景名</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 LoadSceneAsync(string name,UnityAction fun)
{
MonoMgr.Instance.StartCoroutine(ReallyLoadSceneAsync(name, fun));
}
/// <summary>
/// 协程异步加载
/// </summary>
/// <param name="name"></param>
/// <param name="callback"></param>
/// <returns></returns>
private IEnumerator ReallyLoadSceneAsync(string name, UnityAction callback)
{
AsyncOperation ao = SceneManager.LoadSceneAsync(name);
//可以得到场景加载的一个进度
while (!ao.isDone)
{
//事件中心 向外分发 进度情况 外面想用就用
EventCenter.Instance.EventTrigger("进度条", ao.progress);
//在这里面去更新进度条
yield return ao.progress;
}
//加载完成过后 才会去执行fun
callback();
}
}
资源加载模块(Res文件夹)
using System.Collections;
using UnityEngine;
using UnityEngine.Events;
public class ResMgr
{
private static ResMgr instance;
public static ResMgr Instance
{
get
{
if (instance == null)
instance = new ResMgr();
return instance;
}
}
private 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//AduioClip TextAsset
{
return res;
}
}
//异步加载
public void LoadAsync<T>(string name, UnityAction<T> callback) where T : Object
{
MonoMgr.Instance.StartCoroutine(ReallyLoad<T>(name, callback));
}
private IEnumerator ReallyLoad<T>(string name, UnityAction<T> callback) where T : Object
{
ResourceRequest rq = Resources.LoadAsync<T>(name);
yield return rq;
if (rq.asset is GameObject)
{
callback?.Invoke(GameObject.Instantiate(rq.asset) as T);
}
else
{
callback?.Invoke(rq.asset as T);
}
}
}
输入控制模块(Input文件夹)
using UnityEngine;
public class InputMgr
{
private static InputMgr instance;
public static InputMgr Instance
{
get
{
if (instance == null)
instance = new InputMgr();
return instance;
}
}
//是否开启输入检测
private bool isStart = false;
/// <summary>
/// 构造函数中 添加Update监听
/// </summary>
private InputMgr()
{
MonoMgr.Instance.AddUpdateListener(MyUpdate);
}
/// <summary>
/// 是否开启或关闭 我的输入检测
/// </summary>
/// <param name="isOpen"></param>
public void StartOrEndCheck(bool isOpen)
{
isStart = isOpen;
}
/// <summary>
/// 用来检测按键抬起按下 分发事件的
/// </summary>
private void MyUpdate()
{
//没有开启输入检测 就不去检测 直接return
if (!isStart)
{
return;
}
CheckKeyCode(KeyCode.W);
CheckKeyCode(KeyCode.A);
CheckKeyCode(KeyCode.S);
CheckKeyCode(KeyCode.D);
}
private static void CheckKeyCode(KeyCode key)
{
//事件中心模块 分发按下抬起事件
if (Input.GetKey(key))
{
EventCenter.Instance.EventTrigger<KeyCode>("某键按下", key);
}
//事件中心模块 分发按下抬起事件
if (Input.GetKeyUp(key))
{
EventCenter.Instance.EventTrigger<KeyCode>("某键抬起", key);
}
}
}
音效管理模块(Music)
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class MusicMgr
{
private static MusicMgr instance;
public static MusicMgr Instance
{
get
{
if (instance == null)
{
instance = new MusicMgr();
}
return instance;
}
}
//背景音乐组件
private AudioSource bkMusic = null;
//背景音乐默认大小
private float bkVolume = 1f;
//音效物体
private GameObject soundObj = null;
//音效物体上挂载的音效列表
private List<AudioSource> soundList = new List<AudioSource>();
//音效音量
private float soundVolume = 1f;
private MusicMgr()
{
MonoMgr.Instance.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("BKMusic");
bkMusic = obj.AddComponent<AudioSource>();
//背景音乐默认开启循环
bkMusic.loop = true;
}
if (bkMusic.clip != null && bkMusic.clip.name == name)
{
if (bkMusic.isPlaying)
{
return;
}
bkMusic.Play();
return;
}
//异步加载背景音乐 加载完成后 播放
ResourceRequest rq = Resources.LoadAsync<AudioClip>($"Music/BK/{name}");
rq.completed += (value) =>
{
if (rq.asset as AudioClip == null)
{
Debug.LogError($"找不到Music/BK{name}音频文件");
return;
}
bkMusic.clip = rq.asset as AudioClip;
bkMusic.volume = bkVolume;
bkMusic.Play();
};
}
/// <summary>
/// 改变背景音乐 音量大小
/// </summary>
/// <param name="volume"></param>
public void ChangeBKVolume(float volume)
{
if (bkMusic == null)
{
return;
}
bkVolume = volume;
bkMusic.volume = bkVolume;
}
/// <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="name"></param>
public void PlaySound(string name, bool isloop = false, UnityAction<AudioSource> callBack = null)
{
if (soundObj == null)
{
soundObj = new GameObject("SoundMusic");
}
//当音效资源异步加载结束后 再添加音效
ResourceRequest rq = Resources.LoadAsync<AudioClip>($"Music/Sound/{name}");
rq.completed += (value) =>
{
AudioSource source = soundObj.AddComponent<AudioSource>();
source.clip = rq.asset as AudioClip;
source.loop = isloop;
source.volume = soundVolume;
source.Play();
soundList.Add(source);
if (callBack != null)
{
callBack(source);
}
};
}
/// <summary>
/// 改变音效音量大小
/// </summary>
/// <param name="volume"></param>
public void ChangeSoundVolume(float volume)
{
if (soundList.Count == 0)
{
return;
}
soundVolume = volume;
for (int i = 0; i < soundList.Count; i++)
{
soundList[i].volume = soundVolume;
}
}
/// <summary>
/// 停止音效(一个物体上音效有多个 需要指定特定的音效删除)
/// </summary>
public void StopSound(AudioSource audioSource)
{
if (soundList.Contains(audioSource))
{
soundList.Remove(audioSource);
audioSource.Stop();
GameObject.Destroy(audioSource);
}
}
}
UI管理模块(UI文件夹)
BasePanel.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>
/// 面板基类
/// 帮助我们通过代码快速的找到所有的子控件
/// 方便我们在子类中处理逻辑
/// 节约找控件的工作量
/// </summary>
public abstract class BasePanel : MonoBehaviour
{
//通过里氏转换原则 来存储所有控件
private Dictionary<string, List<UIBehaviour>> controlDic = new Dictionary<string, List<UIBehaviour>>();
private CanvasGroup cg;
private bool isShow;
private UnityAction hidePanel;
private float hideSpeed = 10f;
// 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>();
cg = GetComponent<CanvasGroup>();
if (cg == null)
{
cg = this.gameObject.AddComponent<CanvasGroup>();
}
}
protected virtual void Start()
{
Init();
}
protected abstract void Init();
// Update is called once per frame
protected virtual void Update()
{
if (isShow && cg.alpha < 1)
{
cg.alpha += Time.deltaTime * hideSpeed;
if (cg.alpha > 1)
{
cg.alpha = 1;
}
}
if (!isShow)
{
cg.alpha -= Time.deltaTime * hideSpeed;
if (cg.alpha <= 0)
{
cg.alpha = 0;
//由外界 隐藏面板
hidePanel?.Invoke();
}
}
}
public virtual void ShowMe()
{
cg.alpha = 0;
isShow = true;
}
public virtual void HideMe(UnityAction callback)
{
cg.alpha = 1;
isShow = false;
hidePanel = callback;
}
public virtual T GetControler<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;
}
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(objName, new List<UIBehaviour>() { controls[i] });
}
//如果是按钮组件
if (controls[i] is Button)
{
(controls[i] as Button).onClick.AddListener(() =>
{
OnClick(objName);
});
}
//如果是单选框或多选框
if (controls[i] is Toggle)
{
(controls[i] as Toggle).onValueChanged.AddListener((value) =>
{
OnValueChanged(objName, value);
});
}
}
}
protected virtual void OnClick(string btnName) { }
protected virtual void OnValueChanged(string toggleName, bool value) { }
}
UIManager.cs
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
/// <summary>
/// UI层级
/// </summary>
public enum E_UI_Layer
{
Bot, Mid, Top, System,
}
public class UIManager
{
private static UIManager instance = new UIManager();
public static UIManager Instance => instance;
//存储面板的容器
public Dictionary<Type, BasePanel> panelDic = new Dictionary<Type, BasePanel>();
private Transform bot;//背景、底图
private Transform mid;//常规 UI
private Transform top;//弹窗、提示
private Transform system;//Loading / Mask / 强制遮挡
//记录UI的Canvas父对象 方便以后外部使用
public RectTransform canvas;
private UIManager()
{
//动态创建Canvas 过场景不移除
GameObject obj = ResMgr.Instance.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.Instance.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="panel"></param>
public T GetPanel<T>() where T : BasePanel
{
if (panelDic.ContainsKey(typeof(T)))
{
return (T)panelDic[typeof(T)];
}
return null;
}
/// <summary>
/// 显示面板
/// </summary>
/// <typeparam name="T">面板脚本类型</typeparam>
/// <param name="layer">显示在那一层</param>
/// <param name="callBack">当面板预设体创建成功后 想做的事</param>
public void ShowPanel<T>(E_UI_Layer layer = E_UI_Layer.Mid, UnityAction<T> callBack = null) where T : BasePanel
{
T panel = GetPanel<T>();
//是否已经显示了该面板 如果有 不用创建 直接指向回调
if (panel != null)
{
callBack?.Invoke(panel);
return;
}
//根据得到的 类名 就是面板名 直接创建
ResMgr.Instance.LoadAsync<GameObject>("UI/" + typeof(T).Name, (obj) =>
{
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;//offsetMin表示物体左下角相对AnchorMin的偏移
(obj.transform as RectTransform).offsetMin = Vector2.zero;//offsetMax表示物体右上角相对AnchorMax的偏移
//得到预设体身上的面板脚本
panel = obj.GetComponent<T>();
//调用显示自己的逻辑
panel.ShowMe();
callBack?.Invoke(panel);
//把面板存起来
panelDic.Add(typeof(T), panel);
});
}
/// <summary>
/// 隐藏面板
/// </summary>
/// <typeparam name="T">要隐藏的面板类</typeparam>
/// <param name="isFade">淡出:true,直接隐藏:false</param>
public void HidePanel<T>(bool isFade = true) where T : BasePanel
{
//得到根据泛型类型 得到 面板名字
T panel = GetPanel<T>();
//判断字典有没有要隐藏的面板
if (panel != null)
{
if (isFade)
{
panel.HideMe(() =>
{
//删除面板后 从字典中移除
panelDic.Remove(typeof(T));
//面板淡出成功后删除面板
GameObject.Destroy(panel.gameObject);
});
}
else
{
//直接删除面板
GameObject.Destroy(panel.gameObject);
//从字典移除
panelDic.Remove(typeof(T));
}
}
}
/// <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);
}
}

浙公网安备 33010602011771号