使用工具:VS2017,Unity2017.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;
///
/// UI框架的核心管理类
/// 解析并保存所有面板信息(panelPathDict)
///
public class UIManager {
///单例模式的核心
///1.定义一个静态的对象,在外界访问,在内部构造
///2.构造方法私有化
private static UIManager _instance;//单例模式
public static UIManager Instance
{
get
{
if (_instance == null)
_instance = new UIManager();
return _instance;
}
}
private Dictionary panelPathDict;//存储所有面板prefab的路径
///
/// 在初始化的时候解析json
///
private UIManager()
{
ParseUIPanelTypeJson();
}
[Serializable]
class UIPanelTypeJson
{
public List infoList;
}
///
/// 解析json数据
///
private void ParseUIPanelTypeJson()
{
panelPathDict = new Dictionary();//初始化字典
TextAsset ta = Resources.Load("UIPanelType");//得到我们的UPanelType.json文件
UIPanelTypeJson jsonObject= JsonUtility.FromJson(ta.text);//将json数据转换成类中的数据,并返回一个list
foreach (UIPanelInfo info in jsonObject.infoList)
{
panelPathDict.Add(info.panelType,info.path);//将list中的参数存储在字典中
}
}
///
/// just for test
///
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 initialization
void 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;
///
/// UI框架的核心管理类
/// 解析并保存所有面板信息(panelPathDict)
///
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 panelPathDict;//存储所有面板prefab的路径
private Dictionary panelDict;//保存所有实例化面板的游戏物体身上的BasePanel组件
///
/// 在初始化的时候解析json
///
private UIManager()
{
ParseUIPanelTypeJson();
}
///
/// 根据面板类型得到实例化的面板
///
///
///
public BasePanel GetPanel(UIPanelType panelType)
{
//如果panelDict==NULL,那么就新创建一个panelDict
if (panelDict == null)
{
panelDict = new Dictionary();
}
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的父物体为canvas
panelDict.Add(panelType,instPanel.GetComponent());//将这个panel类添加到panelDict中方便下次使用
return instPanel.GetComponent();//返回我们新创建的物体上的basepanel
}
else
{
return panel;//如果panel已经存在panelDict中,我们直接将它返回
}
}
[Serializable]
class UIPanelTypeJson
{
public List infoList;
}
///
/// 解析json数据
///
private void ParseUIPanelTypeJson()
{
panelPathDict = new Dictionary();//初始化字典
TextAsset ta = Resources.Load("UIPanelType");//得到我们的UPanelType.json文件
UIPanelTypeJson jsonObject= JsonUtility.FromJson(ta.text);//将json数据转换成类中的数据,并返回一个list
foreach (UIPanelInfo info in jsonObject.infoList)
{
panelPathDict.Add(info.panelType,info.path);//将list中的参数存储在字典中
}
}
///
/// just for test
///
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;
///
/// 字典扩展类,对Dictionary的扩展
///
public static class DictionaryExtension {
///
/// 尝试根据key得到value,得到了直接返回value,没有得到直接返回null
/// dict表示我们要操作的字典对象
///
public static Tvalue TryGet(this Dictionary 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;
///
/// UI框架的核心管理类
/// 解析并保存所有面板信息(panelPathDict)
///
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 panelPathDict;//存储所有面板prefab的路径
private Dictionary panelDict;//保存所有实例化面板的游戏物体身上的BasePanel组件
///
/// 在初始化的时候解析json
///
private UIManager()
{
ParseUIPanelTypeJson();
}
///
/// 根据面板类型得到实例化的面板
///
///
///
public BasePanel GetPanel(UIPanelType panelType)
{
//如果panelDict==NULL,那么就新创建一个panelDict
if (panelDict == null)
{
panelDict = new Dictionary();
}
//BasePanel panel;
//panelDict.TryGetValue(panelType,out panel);//从字典中得到我们需要的panel
BasePanel 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,并让物体保持局部位置,而非世界位置,将第二个参数设置为false
panelDict.Add(panelType,instPanel.GetComponent());//将这个panel类添加到panelDict中方便下次使用
return instPanel.GetComponent();//返回我们新创建的物体上的basepanel
}
else
{
return panel;//如果panel已经存在panelDict中,我们直接将它返回
}
}
[Serializable]
class UIPanelTypeJson
{
public List infoList;
}
///
/// 解析json数据
///
private void ParseUIPanelTypeJson()
{
panelPathDict = new Dictionary();//初始化字典
TextAsset ta = Resources.Load("UIPanelType");//得到我们的UPanelType.json文件
UIPanelTypeJson jsonObject= JsonUtility.FromJson(ta.text);//将json数据转换成类中的数据,并返回一个list
foreach (UIPanelInfo info in jsonObject.infoList)
{
panelPathDict.Add(info.panelType,info.path);//将list中的参数存储在字典中
}
}
///
/// just for test
///
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;
///
/// UI框架的核心管理类
/// 解析并保存所有面板信息(panelPathDict)
///
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 panelPathDict;//存储所有面板prefab的路径
private Dictionary panelDict;//保存所有实例化面板的游戏物体身上的BasePanel组件
private Stack panelStack;//面板界面的容器栈
///
/// 在初始化的时候解析json
///
private UIManager()
{
ParseUIPanelTypeJson();
}
///
/// 入栈,把某个页面显示在界面上
///
public void PushPanel(UIPanelType panelType)
{
if (panelStack == null)
{
panelStack = new Stack();//如何panelstack为空,就创建一个panelstack
}
BasePanel panel = GetPanel(panelType);//根据面板类型得到面板
panelStack.Push(panel);//入栈
}
///
/// 出栈,把页面从界面上移除
///
public void PopPanel()
{
}
///
/// 根据面板类型得到实例化的面板
///
///
///
private BasePanel GetPanel(UIPanelType panelType)
{
//如果panelDict==NULL,那么就新创建一个panelDict
if (panelDict == null)
{
panelDict = new Dictionary();
}
//BasePanel panel;
//panelDict.TryGetValue(panelType,out panel);//从字典中得到我们需要的panel
BasePanel 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
panelDict.Add(panelType,instPanel.GetComponent());//将这个panel类添加到panelDict中方便下次使用
return instPanel.GetComponent();//返回我们新创建的物体上的basepanel
}
else
{
return panel;//如果panel已经存在panelDict中,我们直接将它返回
}
}
[Serializable]
class UIPanelTypeJson
{
public List infoList;
}
///
/// 解析json数据
///
private void ParseUIPanelTypeJson()
{
panelPathDict = new Dictionary();//初始化字典
TextAsset ta = Resources.Load("UIPanelType");//得到我们的UPanelType.json文件
UIPanelTypeJson jsonObject= JsonUtility.FromJson(ta.text);//将json数据转换成类中的数据,并返回一个list
foreach (UIPanelInfo info in jsonObject.infoList)
{
panelPathDict.Add(info.panelType,info.path);//将list中的参数存储在字典中
}
}
///
/// just for test
///
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 initialization
void 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;
///
/// UI框架的核心管理类
/// 解析并保存所有面板信息(panelPathDict)
///
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 panelPathDict;//存储所有面板prefab的路径
private Dictionary panelDict;//保存所有实例化面板的游戏物体身上的BasePanel组件
private Stack panelStack;//面板界面的容器栈
///
/// 在初始化的时候解析json
///
private UIManager()
{
ParseUIPanelTypeJson();
}
///
/// 入栈,把某个页面显示在界面上
///
public void PushPanel(UIPanelType panelType)
{
if (panelStack == null)
{
panelStack = new Stack();//如何panelstack为空,就创建一个panelstack
}
//判断一下栈里面是否有页面
if (panelStack.Count > 0)
{
BasePanel topPanel = panelStack.Peek();//得到栈顶元素
topPanel.OnPause();
}
BasePanel panel = GetPanel(panelType);//根据面板类型得到面板
panel.OnEnter();
panelStack.Push(panel);//入栈
}
///
/// 出栈,把页面从界面上移除
///
public void PopPanel()
{
if (panelStack == null)
{
panelStack = new Stack();//判断栈顶是否为空,为空创建
}
//如果栈顶元素数量为0,则直接返回结束方法
if (panelStack.Count <= 0)
{
return;
}
//关闭栈顶页面的显示
BasePanel topPanel = panelStack.Pop();
topPanel.OnExit();//执行退出页面的推出方法
if (panelStack.Count <= 0)
{
return;
}
BasePanel topPanel2 = panelStack.Peek();//得到当前的栈顶元素
topPanel2.OnResume();//执行当前栈顶UI面板的继续方法
}
///
/// 根据面板类型得到实例化的面板
///
///
///
private BasePanel GetPanel(UIPanelType panelType)
{
//如果panelDict==NULL,那么就新创建一个panelDict
if (panelDict == null)
{
panelDict = new Dictionary();
}
//BasePanel panel;
//panelDict.TryGetValue(panelType,out panel);//从字典中得到我们需要的panel
BasePanel 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
panelDict.Add(panelType,instPanel.GetComponent());//将这个panel类添加到panelDict中方便下次使用
return instPanel.GetComponent();//返回我们新创建的物体上的basepanel
}
else
{
return panel;//如果panel已经存在panelDict中,我们直接将它返回
}
}
[Serializable]
class UIPanelTypeJson
{
public List infoList;
}
///
/// 解析json数据
///
private void ParseUIPanelTypeJson()
{
panelPathDict = new Dictionary();//初始化字典
TextAsset ta = Resources.Load("UIPanelType");//得到我们的UPanelType.json文件
UIPanelTypeJson jsonObject= JsonUtility.FromJson(ta.text);//将json数据转换成类中的数据,并返回一个list
foreach (UIPanelInfo info in jsonObject.infoList)
{
panelPathDict.Add(info.panelType,info.path);//将list中的参数存储在字典中
}
}
///
/// just for test
///
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 {
///
/// 界面被显示
///
public virtual void OnEnter()
{
}
///
/// 界面暂停
///
public virtual void OnPause()
{
}
///
/// 界面继续
///
public virtual void OnResume()
{
}
///
/// 界面退出
///
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();
}
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();
}
public override void OnEnter()
{
if(canvasGroup==null)
canvasGroup = GetComponent();
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();
}
public override void OnEnter()
{
if(canvasGroup==null)
canvasGroup = GetComponent();
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();
}
public override void OnEnter()
{
if (canvasGroup == null)
canvasGroup = GetComponent();
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框架的结构: