UI框架
UI框架的含义
含义:UI框架用于管理场景中所有的面板,负责控制面板之间的跳转
UI框架的意义
1、随着游戏系统的复杂化,UI控件越来越多,各个UI之间的直接通讯,已经UI与GameObject之间的关系会越来越复杂
2、代码耦合性会很强
UI框架的实例
1、框架面板的设计
MainMenuPanel:主菜单面板
BagPanel:背包面板
ItemMessagePanel:物品信息面板
ShopPanel:商城面板
SkillPanel:技能面板
SystemPanel:系统面板
TaskPanel:技能面板
我们将设计好的面板做成预制体,放在Resources的目录下。
2、Json数据的读取
UIPanelInfo.json
{"infoList":[{"panelTypeString": "ItemMessage","path": "UIPanel/ItemMessagePanel"},{"panelTypeString": "BagPanel","path": "UIPanel/BagPanel"},{"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"}]
}
将所有信息作为一个infolist类的对象
UIPanelType
public enum UIPanelType
{BagPanel,ItemMessage,MainMenu,Shop,Skill,System,Task,
}
UIManager
public class UIManager
{private Dictionary<UIPanelType, string> panelPathDicr;//存储所有面板Prefab的路径,UIPanelType类型,string用于存储路径private UIManager(){ParseUIPanelTypeJson();//调用ParseUIPanelTypeJson方法解析Json文件}//单例化private static UIManager instance;public static UIManager GetInstance(){if (instance == null){instance = new UIManager();}return instance;}[Serializable]class UIPanelTypeJson//将Json文件视作一个整体,直接将其转换为List<UIPanelInfo>{public List<UIPanelInfo> infoList;}//解析Json文件private void ParseUIPanelTypeJson(){panelPathDicr = new Dictionary<UIPanelType, string>();//生成一个字典TextAsset ta = Resources.Load<TextAsset>("UIPanelType");//加载Json文件中的UIPanelType信息UIPanelTypeJson jsonObject = JsonUtility.FromJson<UIPanelTypeJson>(ta.text);//JsonUtility——工具类,将Json中的文件解析为对象,ta.text就是JSON信息,将JSON信息转换为UIPanelTypeJson类//总的意思就是将JSON文件转换为UIPanelTypeJson类,而UIPanelTypeJson类中则是List<UIPanelInfo>,并其字段infoList对应JSON文件的类名foreach (UIPanelInfo info in jsonObject.infoList)//遍历UIPanelInfo的数组{//Debug.Log(info.panelType);panelPathDicr.Add(info.panelType, info.path);//这里的info相对于生成一个jsonObject.infoList,因此info就是数组中的一个元素。//将对象加入到字典当中去}}public void Test()//测试{string path;panelPathDicr.TryGetValue(UIPanelType.Shop, out path);Debug.Log(path);}
}
UIPanelInfo
[Serializable]
public class UIPanelInfo:ISerializationCallbackReceiver//将UIPanelInfo进行序列化,[Serializable]标签就是运行下面字段可进行读盘和写盘,负责与Json文件向对应,读取Json的文件,传递数据,可以避免直接修改Json文件
{[NonSerialized]public UIPanelType panelType;//记录Json文件中的panelType对象//由于无法直接解析UIPanelType,所有使用string来接受Json数据public string panelTypeString;//{第一种方式转换方式。// get// {// return panelType.ToString();// }// set // {// UIPanelType type = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), value);// panelType = type;// }//}public string path;//记录path的路径字段//反序列化 从文本信息到对象public void OnAfterDeserialize()//每次反序列化后就会调用{UIPanelType type = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);panelType = type;//将文本中的panelTypeString强制转换成UIPanelType}public void OnBeforeSerialize()//每次序列化之前调用这个方法{}
}
分析:
首先
1、UIPanelType类
作为一个枚举类,用于记录各个面板,保存面板的类型
2、UIPanelType.json
这是一个Json文件,用于存储各个面板的具体信息
例如:"panelTypeString": "MainMenu","path": "UIPanel/MainMenuPanel"
panelTypeString保存对象
path保存路径
3、UIPanelInfo
负责与Json文件向对应public UIPanelType panelType;//记录Json文件中的panelType对象public string path;//记录path的路径字段这里要将其设置为可序列化的状态[Serializable],让这个类可以读盘与写盘,方便后期对信息进行修改
,也可以避免直接修改Json文件
4、UIManger
是UI框架的核心,负责管理各种框架
第一步,将其设置为单例模式
第二步,将Json文件解析
第三步,使用UIPanelInfo的数组来接受Json文件的信息
第四步,遍历UIPanelInfo数组,并将其中的数据加入字典当中其中有两个关键点。
第一,加载Json文件,并将文件解析为UIPanelInfo对象的数组
第二,遍历数组,并加入字典中,方便以后对其数据进行各种操作
3、框架逻辑设计
BasePanel的设计
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(){ }}
BagPanel继承BasePanel
public class BagPanel:BasePanel
{private CanvasGroup canvasGroup;private void Start(){if (canvasGroup == null){canvasGroup = GetComponent<CanvasGroup>();}}/// <summary>/// 处理页面的关闭/// </summary>public override void OnExit(){// canvasGroup.alpha = 0;canvasGroup.blocksRaycasts = false;transform.DOLocalMoveX(600, 0.5f).OnComplete(() => canvasGroup.alpha = 0);}public void OnClosePanel(){UIManager.GetInstance().PopPanel();}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 OnPause(){canvasGroup.blocksRaycasts = false;}public override void OnResume(){canvasGroup.blocksRaycasts = true;}public void OnItemButtonClick(){UIManager.GetInstance().PushPanel(UIPanelType.ItemMessage);}
}
完善后的UIManager
public class UIManager
{private Transform canvasTransform;private Transform CanvasTransform{get {if (canvasTransform == null){canvasTransform = GameObject.Find("Canvas").transform;}return canvasTransform;}}private Dictionary<UIPanelType, string> panelPathDicr;//存储所有面板Prefab的路径,UIPanelType类型,string用于存储路径private Dictionary<UIPanelType, BasePanel> panelDict;//保存所有面板的游戏物体身上的BasePanel组件private Stack<BasePanel> panelStacks;/// <summary>/// 把某个页面入栈,触发相应方法,并显示在界面上/// </summary>public void PushPanel(UIPanelType panelType){if (panelStacks == null){panelStacks = new Stack<BasePanel>();}//判断一下栈里面是否有页面if (panelStacks.Count > 0){BasePanel topPanel = panelStacks.Peek();topPanel.OnPause();}BasePanel panel = GetPanel(panelType);panel.OnEnter();panelStacks.Push(panel);}/// <summary>/// 出栈,把页面从界面上移除/// </summary>public void PopPanel(){if (panelStacks == null){panelStacks = new Stack<BasePanel>();}//BasePanel panel = GetPanel(panelType);if (panelStacks.Count <= 0){return;}BasePanel topPanel = panelStacks.Pop();topPanel.OnExit();if (panelStacks.Count <= 0) return;BasePanel topPanel2 = panelStacks.Peek();topPanel2.OnResume();}/// <summary>/// 根据面板类型,得到实例化的面板/// </summary>/// <returns></returns>public BasePanel GetPanel(UIPanelType panelType){if (panelDict == null){panelDict = new Dictionary<UIPanelType, BasePanel>();}//BasePanel panel;//panelDict.TryGetValue(panelType, out panel);//TODOBasePanel panel = panelDict.TryGet(panelType);if (panel == null){//如果找不到,就找这个面板的Prefab的路径,然后去根据Prefab去实例化面板//string path;//panelPathDicr.TryGetValue(panelType, out path);string path = panelPathDicr.TryGet(panelType);//字典对象的值是已经决定了的,所以不需要使用泛型而dict对象就是panelPathDicr,不需要再赋值字典var pan = Resources.Load(path);GameObject instPanel = GameObject.Instantiate(pan) as GameObject;//实例化instPanel.transform.SetParent(CanvasTransform, false);//TODO false表示局部位置,而非全局位置panelDict.Add(panelType, instPanel.GetComponent<BasePanel>());return instPanel.GetComponent<BasePanel>();}else{return panel;}}private UIManager(){ParseUIPanelTypeJson();//调用ParseUIPanelTypeJson方法解析Json文件}//单例化private static UIManager instance;public static UIManager GetInstance(){if (instance == null){instance = new UIManager();}return instance;}[Serializable]class UIPanelTypeJson//将Json文件视作一个整体,直接将其转换为List<UIPanelInfo>{public List<UIPanelInfo> infoList;}//解析Json文件private void ParseUIPanelTypeJson(){panelPathDicr = new Dictionary<UIPanelType, string>();//生成一个字典TextAsset ta = Resources.Load<TextAsset>("UIPanelType");//加载Json文件中的UIPanelType信息UIPanelTypeJson jsonObject = JsonUtility.FromJson<UIPanelTypeJson>(ta.text);//JsonUtility——工具类,将Json中的文件解析为对象,ta.text就是JSON信息,将JSON信息转换为UIPanelTypeJson类//总的意思就是将JSON文件转换为UIPanelTypeJson类,而UIPanelTypeJson类中则是List<UIPanelInfo>,并其字段infoList对应JSON文件的类名foreach (UIPanelInfo info in jsonObject.infoList)//遍历UIPanelInfo的数组{//Debug.Log(info.panelType);panelPathDicr.Add(info.panelType, info.path);//这里的info相对于生成一个jsonObject.infoList,因此info就是数组中的一个元素。//将对象加入到字典当中去}}public void Test()//测试{string path;panelPathDicr.TryGetValue(UIPanelType.Shop, out path);Debug.Log(path);}
}
分析
1、BasePanel作为基类被其他的具体类继承
而BasePanel中的方法为OnEnter()——进入OnPause()——暂停OnResume()——恢复OnExit()——退出2、具体的类在继承的基础上,进行具体的修改,添加事件3、UIManager则负责使用“栈”来对不同页面进行管理