含义:UI框架用于管理场景中所有的面板,负责控制面板之间的跳转
1、随着游戏系统的复杂化,UI控件越来越多,各个UI之间的直接通讯,已经UI与GameObject之间的关系会越来越复杂
2、代码耦合性会很强
MainMenuPanel:主菜单面板
BagPanel:背包面板
ItemMessagePanel:物品信息面板
ShopPanel:商城面板
SkillPanel:技能面板
SystemPanel:系统面板
TaskPanel:技能面板
我们将设计好的面板做成预制体,放在Resources的目录下。
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
{
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,并其字段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对象的数组
第二,遍历数组,并加入字典中,方便以后对其数据进行各种操作
public class BasePanel : MonoBehaviour
{
///
/// 界面被显示出来
///
public virtual void OnEnter()
{ }
///
/// 界面暂停
///
public virtual void OnPause()
{ }
///
/// 界面继续
///
public virtual void OnResume()
{ }
///
/// 界面不显示,退出这个界面,界面被关闭
///
public virtual void OnExit()
{ }
}
BagPanel继承BasePanel
public class BagPanel:BasePanel
{
private CanvasGroup canvasGroup;
private void Start()
{
if (canvasGroup == null)
{
canvasGroup = GetComponent<CanvasGroup>();
}
}
///
/// 处理页面的关闭
///
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;
///
/// 把某个页面入栈,触发相应方法,并显示在界面上
///
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);
}
///
/// 出栈,把页面从界面上移除
///
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();
}
///
/// 根据面板类型,得到实例化的面板
///
///
public BasePanel GetPanel(UIPanelType panelType)
{
if (panelDict == null)
{
panelDict = new Dictionary<UIPanelType, BasePanel>();
}
//BasePanel panel;
//panelDict.TryGetValue(panelType, out panel);//TODO
BasePanel 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
{
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,并其字段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则负责使用“栈”来对不同页面进行管理