200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > unity网络实战开发(丛林战争)-前期知识准备(012-UI框架开发)

unity网络实战开发(丛林战争)-前期知识准备(012-UI框架开发)

时间:2020-08-02 20:20:13

相关推荐

unity网络实战开发(丛林战争)-前期知识准备(012-UI框架开发)

使用工具:VS,Unity.3,DoTween插件

使用语言:c#

作者:Gemini_xujian

参考:siki老师-《丛林战争》视频教程

继上一篇文章内容,这节课讲解一下如何在实际案例中使用UGUI搭建UI框架。

UI框架的作用:

1.管理场景中所有的面板

2.管理面板之间的跳转

01-unity项目创建以及前期准备

首先创建一个新的unity工程,命名为UIFramewrok,导入素材资源,并在unity中创建Image、Scenes、Sprites、UIFramework、Resources/UIPanel这几个文件夹,其中UIFramework文件夹方便以后我们导出,导入到其他工程使用我们的UI框架;Resources/UIPanel用来存放我们做的UI面板。

02-设计主菜单面板、任务面板、背包和弹框信息面板、以及其他的一些面板

几个面板效果如图所示:

主面板:

任务面板:

背包面板:

弹框面板:

还有其他一些界面就不一一列举了,将所有搭建好的UI面板放到Resources/UIPanel文件夹下,做成预制体。然后将在Hierarchy面板中仅仅只保留一个主面板,如图:

03-通过所有的json和枚举保存所有的面板信息

在unity工程中创建一个c#脚本命名为UIPanelType,用VS打开将类关键字class改为enum枚举类型,然后删除继承的父类和里面的方法,在里面将所有的面板预制体作为枚举值并保存。

using System.Collections;using System.Collections.Generic;using UnityEngine;public enum UIPanelType {MainMenu,ItemMessage,Knapsack,Shop,Skill,Task,System}

然后在unity工程中创建一个txt文件,将之命名为UIPanelType.json,然后用VS打开并将面板名和面板路径以键值对的方式进行写入,代码如下:

UIPanelType.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"},{"panelTypeString":"Task","path":"UIPanel/TaskPanel"}]}

04-开发UIManager解析面板信息json

在unity的UIFramework中创建一个UIManager类,然后将继承信息等删除,并在UIFramework中创建一个Resources文件夹,将之前写好的UIPanelType文件拖入此文件夹中(方便在脚本中得到)。

UIManager类的代码如下所示:

using System;using System.Collections;using System.Collections.Generic;using UnityEngine;/// <summary>/// UI框架的核心管理类/// 解析并保存所有面板信息(panelPathDict)/// </summary>public class UIManager {///单例模式的核心///1.定义一个静态的对象,在外界访问,在内部构造///2.构造方法私有化private static UIManager _instance;//单例模式public static UIManager Instance{get{if (_instance == null)_instance = new UIManager();return _instance;}}private Dictionary<UIPanelType, string> panelPathDict;//存储所有面板prefab的路径/// <summary>/// 在初始化的时候解析json/// </summary>private UIManager(){ParseUIPanelTypeJson();}[Serializable]class UIPanelTypeJson{public List<UIPanelInfo> infoList;}/// <summary>/// 解析json数据/// </summary>private void ParseUIPanelTypeJson(){panelPathDict = new Dictionary<UIPanelType, string>();//初始化字典TextAsset ta = Resources.Load<TextAsset>("UIPanelType");//得到我们的UPanelType.json文件UIPanelTypeJson jsonObject= JsonUtility.FromJson<UIPanelTypeJson>(ta.text);//将json数据转换成类中的数据,并返回一个listforeach (UIPanelInfo info in jsonObject.infoList){panelPathDict.Add(info.panelType,info.path);//将list中的参数存储在字典中}}/// <summary>/// just for test/// </summary>public void Test(){string path;panelPathDict.TryGetValue(UIPanelType.Knapsack,out path);Debug.Log(path);}}

在当前的UIManager类中,将这个类使用了单例模式,并在里面使用了内部类UIPaneltypeJson,这个类的作用是将json文本 的内容序列化,因为我们的json文本整体是一个对象,而在对象里面使用了数组的形式进行数据的存储,所以在UIPanelTypeJson类中,我们定义了一个UIPanelInfo类型的List集合,这个类型是我们定义的针对json对象数组中的每一个对象进行的序列化;目前UIManager类中的核心方法是ParseUIPanelTypeJson()方法,这个方法用来解析我们的json数据,并将解析好的json数据保存在字典中。

下面是UIPanelInfo类的代码:

using System.Collections;using System.Collections.Generic;using UnityEngine;using System;[Serializable]//表示可序列化的//类中的参数要与UIPanelType.json文件中的参数保持一致public class UIPanelInfo :ISerializationCallbackReceiver{[NonSerialized]//不去序列化public UIPanelType panelType;public string panelTypeString;//{// get// {// return panelType.ToString();// }// set// {// UIPanelType type = (UIPanelType)Enum.Parse(typeof(UIPanelType), value);// panelType = type;// }//}public string path;//类在序列化之前调用此方法public void OnBeforeSerialize(){}//类在是序列化成功之后调用此方法public void OnAfterDeserialize(){UIPanelType type = (UIPanelType)Enum.Parse(typeof(UIPanelType), panelTypeString);panelType = type;}}

在UIPanelInfo这个类中,我们除了给出的几个对应的字段值外,还给了这个类一个继承类ISerializationCallbackReceiver,这个类中有两个方法需要去实现,第一个是OnBeforeSerialize()方法,这个方法是在类进行序列化操作之前执行的,第二个是OnAfterDeserialize()方法,这个方法会在序列化完成之后执行,因为我们使用了unity自带的一种json解析方法,所以,我们只能通过此种方式进行序列化,在这个方法中,我们将得到的字符串值转成UIPanelType类型,并赋给了我们在上面定义的同类型的panelType,这个类型因为我们给了一个不进行序列化的标识,所以就不会在序列化的时候使用这个字段序列化。这个就是我们序列化的类。

在我们写好相关内容之后,我们需要做下测试。就如UIManager类中的Test()方法,我们在unity的UIFramewrok文件夹中创建一个GameRoot的脚本,然后在start()方法中调用UIManaager类中的test()方法,并将脚本绑定在canvas上面,这个类也就做为了我们使用这个UI框架的启动类,代码如图所示:

GameRoot:

using System.Collections;using System.Collections.Generic;using UnityEngine;//相当于一个启动器public class GameRoot : MonoBehaviour {// Use this for initializationvoid Start () {UIManager.Instance.Test();//测试方法}}

运行unity,在控制台会有正确的输出:

05-开发BasePanel面板基类

继上一次操作内容,打开unity项目,对项目目录结构进行一些调整。首先在UIFramework文件夹下创建Base文件夹和Manager文件夹,然后创建一个BasePanel类作为所有UI面板类的基类,用来统一为面板添加相同的功能。再然后,为每个UI面板prefab创建一个UIPanel类,使之继承自BasePanel类,将自带的start方法和update方法删除,最后将所有的面板类和面板基类放入Base文件夹,将之前创建的UIManager类和GameRoot类放入Manager文件夹中。

目录结构和代码如图所示:

BasePanel:

using System.Collections;using System.Collections.Generic;using UnityEngine;public class BasePanel : MonoBehaviour {}

MainMenuPanel(只列举一个示例):

using System.Collections;using System.Collections.Generic;using UnityEngine;public class MainmenuPanel : BasePanel{}

目录结构:

面板上需要挂载相应脚本(以MainMenuPanel面板为例):

06-控制UI面板prefab的实例化创建和管理

我们主要是要针对UIManager类做一些修改,先上代码:

UIManager:

using System;using System.Collections;using System.Collections.Generic;using UnityEngine;/// <summary>/// UI框架的核心管理类/// 解析并保存所有面板信息(panelPathDict)/// </summary>public class UIManager {///单例模式的核心///1.定义一个静态的对象,在外界访问,在内部构造///2.构造方法私有化private static UIManager _instance;//单例模式public static UIManager Instance{get{if (_instance == null)_instance = new UIManager();return _instance;}}private Transform canvasTransform;//画布private Transform CanvasTransform{get{if (canvasTransform == null){canvasTransform = GameObject.Find("Canvas").transform;}return canvasTransform;}}private Dictionary<UIPanelType, string> panelPathDict;//存储所有面板prefab的路径private Dictionary<UIPanelType, BasePanel> panelDict;//保存所有实例化面板的游戏物体身上的BasePanel组件/// <summary>/// 在初始化的时候解析json/// </summary>private UIManager(){ParseUIPanelTypeJson();}/// <summary>/// 根据面板类型得到实例化的面板/// </summary>/// <param name="panelType"></param>/// <returns></returns>public BasePanel GetPanel(UIPanelType panelType){//如果panelDict==NULL,那么就新创建一个panelDictif (panelDict == null){panelDict = new Dictionary<UIPanelType, BasePanel>();}BasePanel panel;panelDict.TryGetValue(panelType,out panel);//从字典中得到我们需要的panel//如果没有得到面板,就找这个面板的prefab的路径,然后根据prefab去实例化面板if (panel == null){string path;panelPathDict.TryGetValue(panelType,out path);GameObject instPanel= GameObject.Instantiate(Resources.Load(path))as GameObject;instPanel.transform.SetParent( CanvasTransform);//设置实例化的panel的父物体为canvaspanelDict.Add(panelType,instPanel.GetComponent<BasePanel>());//将这个panel类添加到panelDict中方便下次使用return instPanel.GetComponent<BasePanel>();//返回我们新创建的物体上的basepanel}else{return panel;//如果panel已经存在panelDict中,我们直接将它返回}}[Serializable]class UIPanelTypeJson{public List<UIPanelInfo> infoList;}/// <summary>/// 解析json数据/// </summary>private void ParseUIPanelTypeJson(){panelPathDict = new Dictionary<UIPanelType, string>();//初始化字典TextAsset ta = Resources.Load<TextAsset>("UIPanelType");//得到我们的UPanelType.json文件UIPanelTypeJson jsonObject= JsonUtility.FromJson<UIPanelTypeJson>(ta.text);//将json数据转换成类中的数据,并返回一个listforeach (UIPanelInfo info in jsonObject.infoList){panelPathDict.Add(info.panelType,info.path);//将list中的参数存储在字典中}}/// <summary>/// just for test/// </summary>public void Test(){string path;panelPathDict.TryGetValue(UIPanelType.Knapsack,out path);Debug.Log(path);}}

我已经将新修改的地方给标识了出来,大体讲一下思路。首先,我们需要创建一个字典用来存储当前已经实例化的面板,然后我们创建了一个方法用来得到我们需要的面板,在方法GetPanel()中,我们首先需要判断panelDict字典是否为空,如果为空,则需要去初始化它;然后,我们需要判断我们需要的到达的面板是否存在panelDict中,如果没有,则说明我们的面板没有实例化过,我们就需要去实例化这个面板,实例化后,再将这个面板设置父物体为canvas,所以我们还需要在得到一下canvas的transform组件,得到之后,使用SetParent()方法进行父对象设置,设置好后,将实例化物体上的basePanel组件添加到panelDcit字典中,并将我们需要的basepanel类返回。而如果在判断是否在字典中时已经存在,我们就直接将面板对应basepanel类返回即可。

06-开发字典拓展类

在我们UImanager类中,我们多次使用了Dictionary类中的TryGetValue()方法,在使用过程中,我们需要用两步操作来得到我们需要的内容,这多少一点繁琐,因此我们可以在Dictionary类的基础上对它进行扩展。

首先,我们在UIFramework文件夹中创建一个名为DictionaryExtension的脚本。

打开脚本,将继承类删除,并将自带的方法删除,并将类改为静态类。

然后,定义一个方法,名为TryGet,这个方法用来对TryGetValue做些封装,方便我们的复用。

代码如下:

DictionaryExtension:

using System.Collections;using System.Collections.Generic;using UnityEngine;/// <summary>/// 字典扩展类,对Dictionary的扩展/// </summary>public static class DictionaryExtension {/// <summary>/// 尝试根据key得到value,得到了直接返回value,没有得到直接返回null/// dict表示我们要操作的字典对象/// </summary>public static Tvalue TryGet<Tkey,Tvalue>(this Dictionary<Tkey,Tvalue> dict,Tkey key){Tvalue value;dict.TryGetValue(key,out value);return value;}}

在扩展的方法中,我们使用了泛型的方式,然后通过key值得到我们需要的value值,如果有则返回值,没有则返回空。

接下来我们只需要在我们的UIManager类中将相应的代码进行更换即可,代码如下:

UIManager:

using System;using System.Collections;using System.Collections.Generic;using UnityEngine;/// <summary>/// UI框架的核心管理类/// 解析并保存所有面板信息(panelPathDict)/// </summary>public class UIManager {///单例模式的核心///1.定义一个静态的对象,在外界访问,在内部构造///2.构造方法私有化private static UIManager _instance;//单例模式public static UIManager Instance{get{if (_instance == null)_instance = new UIManager();return _instance;}}private Transform canvasTransform;//画布private Transform CanvasTransform{get{if (canvasTransform == null){canvasTransform = GameObject.Find("Canvas").transform;}return canvasTransform;}}private Dictionary<UIPanelType, string> panelPathDict;//存储所有面板prefab的路径private Dictionary<UIPanelType, BasePanel> panelDict;//保存所有实例化面板的游戏物体身上的BasePanel组件/// <summary>/// 在初始化的时候解析json/// </summary>private UIManager(){ParseUIPanelTypeJson();}/// <summary>/// 根据面板类型得到实例化的面板/// </summary>/// <param name="panelType"></param>/// <returns></returns>public BasePanel GetPanel(UIPanelType panelType){//如果panelDict==NULL,那么就新创建一个panelDictif (panelDict == null){panelDict = new Dictionary<UIPanelType, BasePanel>();}//BasePanel panel;//panelDict.TryGetValue(panelType,out panel);//从字典中得到我们需要的panelBasePanel panel = panelDict.TryGet(panelType);//如果没有得到面板,就找这个面板的prefab的路径,然后根据prefab去实例化面板if (panel == null){ //string path;//panelPathDict.TryGetValue(panelType,out path);string path = panelPathDict.TryGet(panelType);GameObject instPanel= GameObject.Instantiate(Resources.Load(path))as GameObject;instPanel.transform.SetParent( CanvasTransform,false);//设置实例化的panel的父物体为canvas,并让物体保持局部位置,而非世界位置,将第二个参数设置为falsepanelDict.Add(panelType,instPanel.GetComponent<BasePanel>());//将这个panel类添加到panelDict中方便下次使用return instPanel.GetComponent<BasePanel>();//返回我们新创建的物体上的basepanel}else{return panel;//如果panel已经存在panelDict中,我们直接将它返回}}[Serializable]class UIPanelTypeJson{public List<UIPanelInfo> infoList;}/// <summary>/// 解析json数据/// </summary>private void ParseUIPanelTypeJson(){panelPathDict = new Dictionary<UIPanelType, string>();//初始化字典TextAsset ta = Resources.Load<TextAsset>("UIPanelType");//得到我们的UPanelType.json文件UIPanelTypeJson jsonObject= JsonUtility.FromJson<UIPanelTypeJson>(ta.text);//将json数据转换成类中的数据,并返回一个listforeach (UIPanelInfo info in jsonObject.infoList){panelPathDict.Add(info.panelType,info.path);//将list中的参数存储在字典中}}/// <summary>/// just for test/// </summary>public void Test(){string path;panelPathDict.TryGetValue(UIPanelType.Knapsack,out path);Debug.Log(path);}}

这样更方便我们的使用和对方法的调用。

07-分析界面的存储栈,创建stack存储面板界面并控制面板之间的跳转

分析:在对面板进行管理时,我们可以使用栈的结构进行管理,栈的特性为先进后出,符合页面的显示方式。

操作:先上修改后的代码:

UIManager:

using System;using System.Collections;using System.Collections.Generic;using UnityEngine;/// <summary>/// UI框架的核心管理类/// 解析并保存所有面板信息(panelPathDict)/// </summary>public class UIManager {///单例模式的核心///1.定义一个静态的对象,在外界访问,在内部构造///2.构造方法私有化private static UIManager _instance;//单例模式public static UIManager Instance{get{if (_instance == null)_instance = new UIManager();return _instance;}}private Transform canvasTransform;//画布private Transform CanvasTransform{get{if (canvasTransform == null){canvasTransform = GameObject.Find("Canvas").transform;}return canvasTransform;}}private Dictionary<UIPanelType, string> panelPathDict;//存储所有面板prefab的路径private Dictionary<UIPanelType, BasePanel> panelDict;//保存所有实例化面板的游戏物体身上的BasePanel组件private Stack<BasePanel> panelStack;//面板界面的容器栈/// <summary>/// 在初始化的时候解析json/// </summary>private UIManager(){ParseUIPanelTypeJson();}/// <summary>/// 入栈,把某个页面显示在界面上/// </summary>public void PushPanel(UIPanelType panelType){if (panelStack == null){panelStack = new Stack<BasePanel>();//如何panelstack为空,就创建一个panelstack}BasePanel panel = GetPanel(panelType);//根据面板类型得到面板panelStack.Push(panel);//入栈}/// <summary>/// 出栈,把页面从界面上移除/// </summary>public void PopPanel(){}/// <summary>/// 根据面板类型得到实例化的面板/// </summary>/// <param name="panelType"></param>/// <returns></returns>private BasePanel GetPanel(UIPanelType panelType){//如果panelDict==NULL,那么就新创建一个panelDictif (panelDict == null){panelDict = new Dictionary<UIPanelType, BasePanel>();}//BasePanel panel;//panelDict.TryGetValue(panelType,out panel);//从字典中得到我们需要的panelBasePanel panel = panelDict.TryGet(panelType);//如果没有得到面板,就找这个面板的prefab的路径,然后根据prefab去实例化面板if (panel == null){//string path;//panelPathDict.TryGetValue(panelType,out path);string path = panelPathDict.TryGet(panelType);GameObject instPanel= GameObject.Instantiate(Resources.Load(path))as GameObject;instPanel.transform.SetParent( CanvasTransform,false);//设置实例化的panel的父物体为canvaspanelDict.Add(panelType,instPanel.GetComponent<BasePanel>());//将这个panel类添加到panelDict中方便下次使用return instPanel.GetComponent<BasePanel>();//返回我们新创建的物体上的basepanel}else{return panel;//如果panel已经存在panelDict中,我们直接将它返回}}[Serializable]class UIPanelTypeJson{public List<UIPanelInfo> infoList;}/// <summary>/// 解析json数据/// </summary>private void ParseUIPanelTypeJson(){panelPathDict = new Dictionary<UIPanelType, string>();//初始化字典TextAsset ta = Resources.Load<TextAsset>("UIPanelType");//得到我们的UPanelType.json文件UIPanelTypeJson jsonObject= JsonUtility.FromJson<UIPanelTypeJson>(ta.text);//将json数据转换成类中的数据,并返回一个listforeach (UIPanelInfo info in jsonObject.infoList){panelPathDict.Add(info.panelType,info.path);//将list中的参数存储在字典中}}/// <summary>/// just for test/// </summary>public void Test(){string path;panelPathDict.TryGetValue(UIPanelType.Knapsack,out path);Debug.Log(path);}}

MainMenuPanel:

using System.Collections;using System.Collections.Generic;using UnityEngine;using System;public class MainmenuPanel : BasePanel{public void PushPanel(string panelTypeString){UIPanelType panelType = (UIPanelType)Enum.Parse(typeof(UIPanelType), panelTypeString);UIManager.Instance.PushPanel(panelType);}}

GameRoot:

using System.Collections;using System.Collections.Generic;using UnityEngine;//相当于一个启动器public class GameRoot : MonoBehaviour {// Use this for initializationvoid Start () {UIManager.Instance.PushPanel(UIPanelType.MainMenu);}}

在我们已经分析并决定使用栈的方式进行页面之间的加载和跳转后,首先我们在UIManager中创建一个栈,类型为BasePanel,然后创建两个方法用来管理入栈和出栈,先处理的是入栈,在PushPanel方法中,先去判断panelstack是否为空,如果为空则创建,然后通过之前写好的用来得到basepanel的方法得到,最后将得到的面板进行入栈,表示我们已经加载过的页面。

然后我们在MainMenu类中定义一个方法用来处理点击相应按钮弹出相应页面的功能,通过传入相应的面板名称字符串参数,转成枚举类型后,将我们需要的页面显示出来。我们为MainMenu界面里的按钮添加点击事件,并附上相应的参数,然后应用一下,最后在GameRoot脚本中,调用显示MainMenu页面。

08-分析页面的状态,开发页面状态函数,控制界面的出栈和关闭

分析:界面状态可分为:界面显示、界面暂停、界面恢复(继续)、界面移除

操作:首先将过程中修改的脚本贴上来:

UIMananger:

using System;using System.Collections;using System.Collections.Generic;using UnityEngine;/// <summary>/// UI框架的核心管理类/// 解析并保存所有面板信息(panelPathDict)/// </summary>public class UIManager {///单例模式的核心///1.定义一个静态的对象,在外界访问,在内部构造///2.构造方法私有化private static UIManager _instance;//单例模式public static UIManager Instance{get{if (_instance == null)_instance = new UIManager();return _instance;}}private Transform canvasTransform;//画布private Transform CanvasTransform{get{if (canvasTransform == null){canvasTransform = GameObject.Find("Canvas").transform;}return canvasTransform;}}private Dictionary<UIPanelType, string> panelPathDict;//存储所有面板prefab的路径private Dictionary<UIPanelType, BasePanel> panelDict;//保存所有实例化面板的游戏物体身上的BasePanel组件private Stack<BasePanel> panelStack;//面板界面的容器栈/// <summary>/// 在初始化的时候解析json/// </summary>private UIManager(){ParseUIPanelTypeJson();}/// <summary>/// 入栈,把某个页面显示在界面上/// </summary>public void PushPanel(UIPanelType panelType){if (panelStack == null){panelStack = new Stack<BasePanel>();//如何panelstack为空,就创建一个panelstack}//判断一下栈里面是否有页面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>();//判断栈顶是否为空,为空创建}//如果栈顶元素数量为0,则直接返回结束方法if (panelStack.Count <= 0){return;}//关闭栈顶页面的显示BasePanel topPanel = panelStack.Pop();topPanel.OnExit();//执行退出页面的推出方法if (panelStack.Count <= 0){return;}BasePanel topPanel2 = panelStack.Peek();//得到当前的栈顶元素topPanel2.OnResume();//执行当前栈顶UI面板的继续方法}/// <summary>/// 根据面板类型得到实例化的面板/// </summary>/// <param name="panelType"></param>/// <returns></returns>private BasePanel GetPanel(UIPanelType panelType){//如果panelDict==NULL,那么就新创建一个panelDictif (panelDict == null){panelDict = new Dictionary<UIPanelType, BasePanel>();}//BasePanel panel;//panelDict.TryGetValue(panelType,out panel);//从字典中得到我们需要的panelBasePanel panel = panelDict.TryGet(panelType);//如果没有得到面板,就找这个面板的prefab的路径,然后根据prefab去实例化面板if (panel == null){//string path;//panelPathDict.TryGetValue(panelType,out path);string path = panelPathDict.TryGet(panelType);GameObject instPanel= GameObject.Instantiate(Resources.Load(path))as GameObject;instPanel.transform.SetParent( CanvasTransform,false);//设置实例化的panel的父物体为canvaspanelDict.Add(panelType,instPanel.GetComponent<BasePanel>());//将这个panel类添加到panelDict中方便下次使用return instPanel.GetComponent<BasePanel>();//返回我们新创建的物体上的basepanel}else{return panel;//如果panel已经存在panelDict中,我们直接将它返回}}[Serializable]class UIPanelTypeJson{public List<UIPanelInfo> infoList;}/// <summary>/// 解析json数据/// </summary>private void ParseUIPanelTypeJson(){panelPathDict = new Dictionary<UIPanelType, string>();//初始化字典TextAsset ta = Resources.Load<TextAsset>("UIPanelType");//得到我们的UPanelType.json文件UIPanelTypeJson jsonObject= JsonUtility.FromJson<UIPanelTypeJson>(ta.text);//将json数据转换成类中的数据,并返回一个listforeach (UIPanelInfo info in jsonObject.infoList){panelPathDict.Add(info.panelType,info.path);//将list中的参数存储在字典中}}/// <summary>/// just for test/// </summary>public void Test(){string path;panelPathDict.TryGetValue(UIPanelType.Knapsack,out path);Debug.Log(path);}}

BasePanel:

using System.Collections;using System.Collections.Generic;using UnityEngine;public class BasePanel : MonoBehaviour {/// <summary>/// 界面被显示/// </summary>public virtual void OnEnter(){}/// <summary>/// 界面暂停/// </summary>public virtual void OnPause(){}/// <summary>/// 界面继续/// </summary>public virtual void OnResume(){}/// <summary>/// 界面退出/// </summary>public virtual void OnExit(){}}

MainMenuPanel:

using System.Collections;using System.Collections.Generic;using UnityEngine;using System;public class MainmenuPanel : BasePanel{private CanvasGroup canvasGroup;private void Start(){canvasGroup = GetComponent<CanvasGroup>();}public override void OnPause(){canvasGroup.blocksRaycasts = false;//当弹出新的面板的时候,让主菜单面板不再和鼠标交互}public override void OnResume(){canvasGroup.blocksRaycasts = true;}public void PushPanel(string panelTypeString){UIPanelType panelType = (UIPanelType)Enum.Parse(typeof(UIPanelType), panelTypeString);UIManager.Instance.PushPanel(panelType);}}

KnapsackPanel:

using System.Collections;using System.Collections.Generic;using UnityEngine;public class KnapsackPanel : BasePanel{private CanvasGroup canvasGroup;private void Start(){canvasGroup = GetComponent<CanvasGroup>();}public override void OnEnter(){if(canvasGroup==null)canvasGroup = GetComponent<CanvasGroup>();canvasGroup.alpha = 1;canvasGroup.blocksRaycasts = true;}public override void OnExit(){canvasGroup.alpha = 0;canvasGroup.blocksRaycasts = false;}public void OnClose(){UIManager.Instance.PopPanel();}}

在完成了上一步的操作后,我们需要对页面的状态进行管理。

首先,我们在BasePanel类中添加四个方法,分别代表了页面的四种不同状态。

然后,我们在UIManager类中对之前的PushPanel方法做了一些修改,并完善了PopPanel方法,这两个方法在逻辑上是共通的,只要明白了其中一个方法的逻辑顺序,那么另一个也就懂了,我简单讲解一下PopPanel的逻辑:PopPanel方法是将当前位于栈容器中最上层的元素移除掉,为了实现这个目标并保证我们能够在实际运行时完成正常交互,我们需要考虑到所有需要完成的操作。首先要判断栈是否为空,为空创建;然后判断栈内元素数量是否为0,为0说明栈里已经没有元素,无法执行移除操作,直接返回;如果不为0,则将栈顶元素移除,并执行此栈顶元素(即UI面板)的basepanel组件中的onexit方法;完成操作之后,再次判断栈顶元素数量是否为0,为0返回;不为0则得到当前栈顶元素并执行它的onresume方法,此方法是页面处于继续状态时执行的,也就是页面已经存在于栈中,但在执行PopPanel方法时并不是最上层元素而是第二层元素的那个页面,会在这时被执行onresume方法,这样就完成了我们的出栈以及状态信息的更新。

完善好UIManager后,在MainMenu类中完成MainMenu页面的状态方法的内容。一是暂停状态时,而是继续状态时,暂停时则将面板的交互禁用,继续是启用。而且,还需要你将MainMenu的prefab添加一个canvasgroup组件,用来处理面板的交互管理。

既然我们需要进行页面间的跳转,那么我们还需要将其他页面的状态方法进行重写,这里以KnapsackPanel为例。我们需要完成此页面的进入状态、退出状态进行内容的填充。在方法中使用的canvasgroup.alpha是用来显示或不显示此页面的,canvasgroup.blockraycasts方法是用来启用或禁用页面交互的。close方法是点击关闭按钮后执行的,所以需要给close按钮添加上点击事件,调用的方法是close(),并且也需要给这个页面的prefab添加上canvasGroup组件才可完成正常的交互。其他页面的代码类似于KnapsackPanel脚本。

最终就实现了不同页面之间的跳转,效果如图:

09-完善面板之间的跳转并给面板添加动画

先上修改的脚本:

KnapsackPanel:

using System.Collections;using System.Collections.Generic;using UnityEngine;using DG.Tweening;//DoTween插件使用的命名空间public class KnapsackPanel : BasePanel{private CanvasGroup canvasGroup;private void Start(){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 = 1000;transform.localPosition = temp;transform.DOLocalMoveX(0, .5f);}public override void OnPause(){canvasGroup.blocksRaycasts = false;}public override void OnResume(){canvasGroup.blocksRaycasts = true;}public override void OnExit(){ //canvasGroup.alpha = 0;canvasGroup.blocksRaycasts = false;transform.DOLocalMoveX(1000, .5f).OnComplete(() => canvasGroup.alpha = 0);//OnComplete使用了lambda表达式}public void ItemClick(){UIManager.Instance.PushPanel(UIPanelType.ItemMessage);}public void OnClose(){UIManager.Instance.PopPanel();}}

SkillPanel:

using System.Collections;using System.Collections.Generic;using UnityEngine;using DG.Tweening;public class SkillPanel : BasePanel{private CanvasGroup canvasGroup;private void Start(){canvasGroup = GetComponent<CanvasGroup>();}public override void OnEnter(){if (canvasGroup == null)canvasGroup = GetComponent<CanvasGroup>();canvasGroup.alpha = 0;canvasGroup.blocksRaycasts = true;canvasGroup.DOFade(1,.5f);}public override void OnExit(){//canvasGroup.alpha = 0;canvasGroup.blocksRaycasts = false;canvasGroup.DOFade(0,.5f);}public void OnClose(){UIManager.Instance.PopPanel();}}

我们想要添加动画效果的途径有很多,可以使用内置的方式,也可以使用外部插件,在这里为了方便我直接使用了DoTween插件,如果没有听说过这个插件的同学可以自行百度一下,包括插件的下载我就不提供下载地址了。

首先我们需要导入一下DoTween插件,导入完成后,我们就可以直接修改我们的面板的相关代码了,在上面贴的代码中,修改的地方是对DoTween插件库方法的一些调用,这里只使用了其中很少的几个方法,大家可以试一下它提供的其他的一些方法。

10-UI框架的导出

在unity中,右键Project栏有一个Export package选项,点击后只选择Demigiant(DoTween插件)和UIFramework文件夹,然后点击导出即可。

附整个UI框架的结构:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。