UI框架—基于UGUI
一、需求分析
(一)、需求分析UML图
1、通过UIManger来自作解析json文件。
2、UML各种关系
二、知识点
(一)、原理知识
1、单例模式
-
定义一个静态的对象 在外界访问 在内部构造。
-
构造方法私有化。
public static UIManager _instance; public static UIManager Instance { get { if (_instance == null) { _instance = new UIManager(); } return _instance; } }
(二)、插件知识
1、使用DOTween插件来制作panel出现和现实的动画
- 导入插件,注意命名空间的引入
- 其他的根据需求来制作
(三)、操作知识
1、场景的搭建
- 注意文字格式要设置为跟随屏幕自适应而改变其大小
(四)、代码相关
1、编写json信息
-
编写json相关信息
-
因为使用了unity自带的两个json类来解析json所以要把一个整体包装成另一个整体来读取
-
json信息如下
{ "infoList": [ {"panelTypeString":"ItemMessage", "path":"UIPanel/ItemMessagePanel"}, {"panelTypeString":"Knapsack", "path":"UIPanel/KnapsackPanel"}, {"panelTypeString":"MainMenu", "path":"UIPanel/MainMenuPanel"}, {"panelTypeString":"Shop", "path":"UIPanel/ShopPanel"}, {"panelTypeString":"Skill", "path":"UIPanel/SkillPanel"}, {"panelTypeString":"System", "path":"UIPanel/SystemPanel"}, {"panelType":"Task", "path":"UIPanel/TaskPanel"} ] }
2、解析json信息
-
使用unity自带的json解析类来解析
-
使用两个字典来存储Panel跟路径
private Dictionary<UIPanelType, string> panelPathDict;//存储所有面板的Prefab的路径 private Dictionary<UIPanelType, BasePanel> panelDict;//保存所有实例化面板的游戏物体BasePanel组件 private Stack<BasePanel> panelStack; [Serializable] class UIPanelTypeJson { public List<UIPanelInfo> infoList; } /// <summary> /// 解析Json信息 /// </summary> private void ParseUIPanelTypeJson() { panelPathDict = new Dictionary<UIPanelType, string>(); TextAsset ta = Resources.Load<TextAsset>("UIPanelType"); UIPanelTypeJson jsonObject = JsonUtility.FromJson<UIPanelTypeJson>(ta.text); foreach (UIPanelInfo info in jsonObject.infoList) { panelPathDict.Add(info.panelType,info.path);//保存读取后的名字跟路径 } }
- UIPanelInfo来读取里面的信息
using System; using System.Collections; using UnityEngine; [Serializable] public class UIPanelInfo : ISerializationCallbackReceiver { [NonSerialized] //不进行读取 public UIPanelType panelType; public string panelTypeString; //用来存储读取json里面的地址字符串 public string path; /// <summary> /// 实现接口,读取json里面的信息 /// 反序列化 从文本信息到对象 /// </summary> public void OnAfterDeserialize() { UIPanelType type = (UIPanelType) System.Enum.Parse(typeof(UIPanelType), panelTypeString); panelType = type; } public void OnBeforeSerialize() { } }
3、扩展Dictionary类里面的方法
-
原理:有些类是系统定义好的不能修改其中的东西,只能对其中一些东西进行扩展,向现有类型“添加”方法,而不创建派生类,
-
扩展类和扩展方法都必须是static的,如果扩展方法该类型中定义的方法具有相同的名字,则扩展方法不会被调用
-
如果扩展的父类型更改了,扩展方法也失效
using System.Collections.Generic; /// <summary> /// 对Dictionary的扩展 /// 必须是静态方法和静态类 /// </summary> public static class DictionaryExtension { /// <summary> /// 尝试根据key得到value,得到了的话直接返回value,没有得到直接返回null /// this Dictionary<Tkey,Tvalue>dict 这个字典表示我们要获取值的字典 /// </summary> /// <typeparam name="Tkey">泛型一</typeparam> /// <typeparam name="Tvalue">泛型二</typeparam> /// <param name="dict">方法对象</param> /// <param name="key">获取的值</param> /// <returns>value的值</returns> public static Tvalue TryGet<Tkey,Tvalue>(this Dictionary<Tkey,Tvalue>dict,Tkey key) { Tvalue value; dict.TryGetValue(key, out value); return value; } }
4、通过CanvasGroup来控制panel的显示和交互
-
里面有一个alpha值来控制显示和隐藏
-
BlockRaycasts 来控制能不能进行交互
private CanvasGroup canvasGroup; void Start() { if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>(); } public override void OnEnter() { if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>(); canvasGroup.alpha = 1; canvasGroup.blocksRaycasts = true; Vector3 temp = transform.localPosition; temp.x = 600; transform.localPosition = temp; transform.DOLocalMoveX(0, 0.5f); } public override void OnExit() { canvasGroup.blocksRaycasts = false; transform.DOLocalMoveX(600, 0.5f).OnComplete(() => canvasGroup.alpha = 0); } public override void OnPause() { canvasGroup.blocksRaycasts = false; } public override void OnResume() { canvasGroup.blocksRaycasts = true; } public void OnClosePanel() { UIManager.Instance.PopPanel(); } public void OnItemButtonClick() { UIManager.Instance.PushPanel(UIPanelType.ItemMessage); }
5、根据面板类型来实例化面板
-
实例化面板所需要的参数自行设置
// <summary> /// 根据面板类型,得到实例化的面板 /// </summary> /// <param name = "panelType" > 面板的名字 </ param > /// < returns ></ returns > public BasePanel GetPanel(UIPanelType panelType) { if (panelDict == null) { panelDict = new Dictionary<UIPanelType, BasePanel>(); } BasePanel panel = panelDict.TryGet(panelType); if (panel == null) { // 乳沟找不到,那么就找这个面板的prefab的路径,然后去根据prefab去实例化面板 string path = panelPathDict.TryGet(panelType); GameObject instPanel = GameObject.Instantiate(Resources.Load(path)) as GameObject; instPanel.transform.SetParent(CanvasTransform, false); panelDict.Add(panelType, instPanel.GetComponent<BasePanel>()); return instPanel.GetComponent<BasePanel>(); } else { return panel; } }
6、通过栈来控制面板的进入和移出
-
把每个panel看做是一个整体进行栈操作。需要的时候进栈,不需要的时候出栈
-
入栈和入栈
private Stack<BasePanel> panelStack; //栈用来存放panel组件 /// <summary> /// 把某个页面入栈,把某个页面显示在界面上 /// </summary> /// <param name = "panelType" > 页面的名字 </ param > public void PushPanel(UIPanelType panelType) { if (panelStack == null) panelStack = new Stack<BasePanel>(); //判断一下栈里面是否有页面 if (panelStack.Count > 0) { BasePanel topPanel = panelStack.Peek(); topPanel.OnPause(); } BasePanel panel = GetPanel(panelType); panel.OnEnter(); panelStack.Push(panel); } /// <summary> /// 出栈,把页面从页面上面移除 /// </summary> public void PopPanel() { if (panelStack == null) panelStack = new Stack<BasePanel>(); if (panelStack.Count <= 0) return; //关闭栈顶页面的显示 BasePanel topPanel = panelStack.Pop(); topPanel.OnExit(); if (panelStack.Count <= 0) return; BasePanel topPanel2 = panelStack.Peek(); topPanel2.OnResume(); }
7、构建BasePanel 基类来进行面板操作
-
让其他Panel来继承BasePanel
-
里面有四个方法,每个方法之间的关系不一样,详细查看UML图
三、遇到的问题
(一)、老师错误
1、Json解析出现报错
-
Json文件重复了
-
Json文件没有按照Unity里面的Json解析需求来编辑
(二)、自己的错误
1、运行的时候发现报错,提示Dictionary重复。
- 描述:Dictionary重复
- 原因:Json文件不对,有重复的内容
- 解决:验证Json文件的正确性,在重新读取
- 注意:以后如果还是类似问题要优先考虑是文件读取的问题。
2、关闭面板不能关闭,也就是不能出栈
-
描述:关闭面板不能关闭,也就是不能出栈
-
原因:有一句判断写错了
if (panelStack.Count > 0) return;//错误代码 if (panelStack.Count <= 0) return; //正确代码
-
解决:根据出现问题检查代码,步骤如下
-
先检查按钮点击事件看看是否正确。
-
在查看关闭按钮调用的是什么方法
-
-
注意:写代码的时候要小心一点,特别是相关
if
判断的时候
3、点击商店报错
- 描述:要实例化的对象是null。
- 原因:不知道
- 解决:从小制作商店Panel
- 注意:有时候找不到问题就关闭软件,或者解除组件然后再装上试试,或者重新制作游戏物体看看。