版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Say__Yes/article/details/74086620
接着上一篇的介绍,这一篇就该干点实事了—上代码。但是在这之前,我先给出UI框架在Unity中的资源文件的截图,方便大家理解(其中的预制体和代码是根据上一篇博客的讲解给出):
1.存储面板类型和面板预设路径的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"}
]
}
2.每个面板对应的枚举如下:
using UnityEngine;
using System.Collections;
public enum UIPanelType
{
MainMenu,
Knapsack,
Shop,
Skill,
Task,
System,
ItemMessage
}
3.每个UI界面信息对应的类(也就是解析出来的Json对应的类):
using UnityEngine;
using System.Collections;
using System;
[Serializable]
public class UIPanelInfo : ISerializationCallbackReceiver{//和json文件信息对应的一个类
//不可序列化的,因为这里unity解析json文件时没办法解析枚举类型,所以下面定义了一个string类型的字段panelTypeString,用于两者的转化
[NonSerialized]
public UIPanelType panelType;//面板类型
public string panelTypeString;
public string path;//面板所在路径
//实现ISerializationCallbackReceiver的接口, 反序列化方法,从文本信息到对象
public void OnAfterDeserialize()
{
UIPanelType type = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);//把一个字符串转化为一个枚举
panelType = type;
}
//实现接口, 序列化方法,从对象到文本信息
public void OnBeforeSerialize(){}
}
4.(重点)UIManager管理各个UI面板的核心类:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// 单例模式的核心:
/// 1.只在该类内定义一个静态的对象,该对象在外界访问,在内部构造
/// 2.构造函数私有化
/// </summary>
public class UIManager{//此类作为一个单例模式,即只有一个实例的模式
private static UIManager _instance;
public static UIManager Instance
{
get
{
if (_instance == null)
{
_instance = new UIManager();
}
return _instance;
}
}
private UIManager() //构造函数私有化(单例模式)
{
ParseUIPanelTypeJson();//构造该类时会解析Json
}
private Dictionary<UIPanelType, string> panelPathDict;//存储所有Perfab面板的路径
private Dictionary<UIPanelType, BasePanel> panelDict;//借助BasePanel脚本保存所有实例化出来的面板物体(因为BasePanel脚本被所有面板预设物体的自己的脚本所继承,所以需要的时候可以根据BasePanel脚本来实例化每一个面板对象)
private Stack<BasePanel> panelStack;//这是一个栈,用来保存实例化出来(显示出来)的面板
private Transform canvasTransform;//用来使实例化的面板归为它的子物体
private Transform CanvasTransform
{
get {
if (canvasTransform == null)
{
canvasTransform = GameObject.Find("Canvas").transform;
}
return canvasTransform;
}
}
//页面入栈,即把页面显示在界面上
public void PushPanel(UIPanelType panelType)
{
if (panelStack == null)//如果栈不存在,就实例化一个空栈
{
panelStack = new Stack<BasePanel>();
}
if (panelStack.Count > 0)
{
BasePanel topPanel = panelStack.Peek();//取出栈顶元素保存起来,但是不移除
topPanel.OnPause();//使该页面暂停,不可交互
}
BasePanel panelTemp = GetPanel(panelType);
panelTemp.OnEnter();//页面进入显示,可交互
panelStack.Push(panelTemp);
}
//页面出栈,即把页面从界面上移除
public void PopPanel()
{
if (panelStack == null)
{
panelStack = new Stack<BasePanel>();
}
if (panelStack.Count <= 0) return;
//关闭栈顶页面的显示
BasePanel topPanel1 = panelStack.Pop();
topPanel1.OnExit();
if (panelStack.Count <= 0) return;
BasePanel topPanel2 = panelStack.Peek();
topPanel2.OnResume();//使第二个栈里的页面显示出来,并且可交互
}
[System.Serializable]
class UIPanelTypeJson //内部类里面就一个链表容器,用来配合解析
{
public List<UIPanelInfo> infoList;
}
//根据面板类型UIPanelType得到实例化的面板
private BasePanel GetPanel(UIPanelType panelType)
{
if (panelDict == null)//如果panelDict字典为空,就实例化一个空字典
{
panelDict = new Dictionary<UIPanelType, BasePanel>();
}
//BasePanel panel;
//panelDict.TryGetValue(panelType, out panel);//不为空就根据类型得到Basepanel
BasePanel panel = panelDict.TayGet(panelType);//我们扩展的Dictionary的方法,代码作用同上两行
if (panel == null)//如果得到的panel为空,那就去panelPathDict字典里面根据路径path找到,然后加载,接着实例化
{
string path = panelPathDict.TayGet(panelType);//我们扩展的Dictionary的方法
//panelPathDict.TryGetValue(panelType, out path);
GameObject instPanel = GameObject.Instantiate(Resources.Load(path)) as GameObject;//根据路径加载并实例化面板
instPanel.transform.SetParent(this.CanvasTransform,false);//设置为Canvas的子物体,false表示实例化的子物体坐标以Canvas为准
//TODO
panelDict.Add(panelType,instPanel.GetComponent<BasePanel>());
return instPanel.GetComponent<BasePanel>();
}
else
{
return panel;
}
}
//解析UIPanelType.json的信息
private void ParseUIPanelTypeJson()
{
panelPathDict = new Dictionary<UIPanelType, string>();//实例化一个字典对象
TextAsset ta = Resources.Load<TextAsset>("UIPanelType"); //获取UIPanelType.json文件的文本信息
UIPanelTypeJson jsonObject = JsonUtility.FromJson<UIPanelTypeJson>(ta.text);//把UIPanel.json文本信息转化为一个内部类的对象,对象里面的链表里面对应的是每个Json信息对应的类
foreach (UIPanelInfo info in jsonObject.infoList)
{
//Debug.Log(info.panelType);
panelPathDict.Add(info.panelType, info.path);//把每一个进过json文件转化过来的类存入字典里面(键值对的形式)
}
}
}
5.启动整个UI框架的类(这里只需要再启动游戏时加载主界面就好,其他界面是需要点击按钮时才会实例出来并显示):
using UnityEngine;
using System.Collections;
public class GameRoot : MonoBehaviour
{
// Use this for initialization
void Start () {
UIManager.Instance.PushPanel(UIPanelType.MainMenu);
}
}
6.BasePanel各个面板的基类(定义各个面板共有的四个状态):
using UnityEngine;
using System.Collections;
//所有面板的公共基类
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(){}
}
7.主界面:
using UnityEngine;
using System.Collections;
public class MainMenuPanel : BasePanel{
private CanvasGroup canvasGroup;//获取CanvasGroup组件,用于控制该面板的交互功能
void Start()
{
canvasGroup = this.GetComponent<CanvasGroup>();
}
public override void OnPause()//重写父类BasePanel的OnPause方法
{
canvasGroup.blocksRaycasts = false;//使该面板失去交互功能
}
//重写父类BasePanel的OnResume方法
public override void OnResume()
{
canvasGroup.blocksRaycasts = true;//使该面板继续交互功能
}
//点击功能模块按钮加载面板,并将其入栈
public void OnPushPanel(string panelTypeString)
{
//把一个字符串转化为对应的枚举类型
UIPanelType panelType =(UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);
UIManager.Instance.PushPanel(panelType);
}
}
8.任务面板:
using UnityEngine;
using System.Collections;
using DG.Tweening;
public class TaskPanel : BasePanel {
private CanvasGroup canvasGroup;//对该页面CanvasGroup组件的引用
void Start() {
if(canvasGroup == null) canvasGroup = this.GetComponent<CanvasGroup>();
}
//重写父类的OnEnter方法
public override void OnEnter()
{
if (canvasGroup == null) canvasGroup = this.GetComponent<CanvasGroup>();
this.canvasGroup.alpha = 0;
this.canvasGroup.blocksRaycasts = true;//使该页面可交互
canvasGroup.DOFade(1, 0.6f);//使该页面渐渐显示,1是Alpha值
}
//重写父类的OnExit方法
public override void OnExit()
{
this.canvasGroup.blocksRaycasts = false;//使该页面不可交互
canvasGroup.DOFade(0, 0.6f);//使该页面渐渐隐藏,0是Alpha值
}
//点击关闭按钮事件
public void OnClose()
{
UIManager.Instance.PopPanel();
}
}
9.背包面板:
using UnityEngine;
using System.Collections;
using DG.Tweening;
public class KnapsackPanel : BasePanel {
private CanvasGroup canvasGroup;
void Awake()
{
this.canvasGroup = this.GetComponent<CanvasGroup>();
}
public override void OnEnter()
{
this.canvasGroup.alpha = 1;
this.canvasGroup.blocksRaycasts = true;
Vector3 temp = this.transform.localPosition;
temp.x = 600;
this.transform.localPosition = temp;
this.transform.DOLocalMoveX(0, 0.6f);
}
public override void OnExit()
{
//this.canvasGroup.alpha = 0;
this.canvasGroup.blocksRaycasts = false;
this.transform.DOLocalMoveX(-600, 0.6f).OnComplete(()=>canvasGroup.alpha =0);
}
public override void OnPause()
{
this.canvasGroup.blocksRaycasts = false;
}
public override void OnResume()
{
this.canvasGroup.blocksRaycasts = true;
}
//点击关闭按钮
public void OnClose()
{
UIManager.Instance.PopPanel();
}
//点击装备,显示装备信息
public void OnItemButtonDown()
{
UIManager.Instance.PushPanel(UIPanelType.ItemMessage);
}
}
10.技能面板
using UnityEngine;
using System.Collections;
public class SkillPanel : BasePanel {
private CanvasGroup canvasGroup;
void Awake()
{
this.canvasGroup = this.GetComponent<CanvasGroup>();
}
public override void OnEnter()
{
this.canvasGroup.alpha = 1;
this.canvasGroup.blocksRaycasts = true;
}
public override void OnExit()
{
this.canvasGroup.alpha = 0;
this.canvasGroup.blocksRaycasts = false;
}
public void OnClose()
{
UIManager.Instance.PopPanel();
}
}
11.商城面板:
using UnityEngine;
using System.Collections;
public class ShopPanel : BasePanel {
private CanvasGroup canvasGroup;
void Awake()
{
this.canvasGroup = this.GetComponent<CanvasGroup>();
}
public override void OnEnter()
{
this.canvasGroup.alpha = 1;
this.canvasGroup.blocksRaycasts = true;
}
public override void OnExit()
{
this.canvasGroup.alpha = 0;
this.canvasGroup.blocksRaycasts = false;
}
public void OnClose()
{
UIManager.Instance.PopPanel();
}
}
12.系统设置面板:
using UnityEngine;
using System.Collections;
public class SystemPanel : BasePanel {
private CanvasGroup canvasGroup;
void Awake()
{
this.canvasGroup = this.GetComponent<CanvasGroup>();
}
public override void OnEnter()
{
this.canvasGroup.alpha = 1;
this.canvasGroup.blocksRaycasts = true;
}
public override void OnExit()
{
this.canvasGroup.alpha = 0;
this.canvasGroup.blocksRaycasts = false;
}
public void OnClose()
{
UIManager.Instance.PopPanel();
}
}
13.背包里的装备信息提示面板:
using UnityEngine;
using System.Collections;
using DG.Tweening;
public class ItemMessagePanel : BasePanel{
private CanvasGroup canvasGroup;
void Awake()
{
this.canvasGroup = this.GetComponent<CanvasGroup>();
}
public override void OnEnter()
{
this.canvasGroup.alpha = 1;
this.canvasGroup.blocksRaycasts = true;
this.transform.localScale = Vector3.zero;
transform.DOScale(1, 0.6f);//缩放动画,1代表结束时的缩放
}
public override void OnExit()
{
//this.canvasGroup.alpha = 0;
this.canvasGroup.blocksRaycasts = false;
transform.DOScale(0, 0.6f).OnComplete(() => canvasGroup.alpha = 0);
}
public void OnClose()
{
UIManager.Instance.PopPanel();
}
}
14.字典拓展类(用于拓展Dictionary类的TryGetValue( )方法,只是为了简化代码方便使用,UI框架里可有可无):
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
//对字典类的TayGetValue( )方法的扩展
/// <summary>
/// 尝试根据key得到value,得到返回value,否则返回null
/// this Dictionary<Tkey,Tvalue> dict 这个字典表示我们要获取值value的字典
/// 扩展方法规定类必须是一个静态类.里面包含的所有方法都必须是静态方法。扩展方法被定义为静态方法,但它们是通过实例方法语法进行调用的。 它们的第一个参数指定该方法作用于哪个类型,并且该参数以 this 修饰符为前缀。
/// </summary>
public static class DictionaryExtension {
public static Tvalue TayGet<Tkey,Tvalue>( this Dictionary<Tkey,Tvalue> dict,Tkey key)
{
Tvalue value;
dict.TryGetValue(key,out value);
return value;
}
}
总结:该UI框架重点在于UI核心管理类(UIManager)的实现,其中使用栈先进后出,后进先出的思想实现了页面的逻辑管理。难点呢在于实现Json文本的解析。