Unity3D之搭建简易有效的UI框架_胖胖的橘猫君-CSDN博客_u3d ui框架
UI框架用于管理场景中的所有面板,控制面板之间的切换,可以加快开发进度、提高代码质量。
根据用户界面调用情况,分析有如下四种状态:
进入状态:界面第一次被动态加载使用的时候
暂停状态:切换到其他界面的时候
继续状态:重新回到界面的时候
退出状态:界面不显示的时候
实现步骤:
1、使用JSON保存面板路径,枚举保存面板类型
2、根据界面共有的四种状态,创建UI基类BasePanel,场景中的界面继承该基类,并将四种状态写成虚方法,依次分别为OnEnter(),OnPause(),OnResume(),OnExit(),提供给子类重写。
3、通过管理类UIManager,解析JSON,管理UI界面的加载和切换。为了方便调用,做成单例模式。分别用两个字典保存从JSON读取的面板信息和已动态加载实例化的面板。通过栈来管理场景中所有面板之间的切换。
缺点也比较明显,因为用栈来存储场景中依次打开的界面,也只能依次从栈顶界面开始关闭。
页面状态流程图如下所示:
UI框架类图如下:
UI界面基类
BasePanel.cs
public class BasePanel : MonoBehaviour
{
///
/// 界面显示出来
///
public virtual void OnEnter() { }
///
/// 界面暂停(弹出了其他界面)
///
public virtual void OnPause() { }
///
/// 界面继续(其他界面移除,回复本来的界面交互)
///
public virtual void OnResume() { }
///
/// 界面不显示,退出这个界面,界面被关闭
///
public virtual void OnExit() { }
}
UIManager.cs
public class UIManager
{
// 单例模式:定义一个静态的对象,构造方法私有化,内部构造,用于外部访问
private static UIManager _instance;
public static UIManager Instance
{
get
{
if (_instance == null)
{
_instance = new UIManager();
}
return _instance;
}
}
//字典存储所有面板的Prefabs路径
private Dictionary
//保存所有已实例化面板的游戏物体身上的BasePanel组件
private Dictionary
//存储当前场景中的界面
private Stack
private Transform canvasTransform;
private Transform CanvasTransform
{
get
{
if (canvasTransform == null)
{
canvasTransform = GameObject.Find("Canvas").transform;
}
return canvasTransform;
}
}
private UIManager()
{
ParseUIPanelTypeJson();
}
///
/// 解析JSON,获取所有面板的路径信息
///
private void ParseUIPanelTypeJson()
{
TextAsset ta = Resources.Load
JsonData jsonDataArray = JsonMapper.ToObject(ta.text);
foreach (JsonData item in jsonDataArray)
{
UIPanelType panelType = (UIPanelType)Enum.Parse(typeof(UIPanelType), item["panelType"].ToString());
string path = item["path"].ToString();
panelPathDict.Add(panelType, path);
}
}
///
/// 根据面板类型,返回对应的BasePanel组件
///
/// 需要返回的面板类型
///
private BasePanel GetPanel(UIPanelType panelType)
{
BasePanel basePanel = panelDict.GetValue(panelType);
//如果panel为空,根据该面板prefab的路径,实例化该面板
if (basePanel == null)
{
string path = panelPathDict.GetValue(panelType);
GameObject newPanel = GameObject.Instantiate(Resources.Load
newPanel.transform.SetParent(CanvasTransform, false);
//第一次实例化的面板需要保存在字典中
panelDict.Add(panelType, newPanel.GetComponent
return newPanel.GetComponent
}
else
{
return basePanel;
}
}
///
/// 设置默认的栈顶元素
///
/// 界面类型
/// 组件
public void SetDefaultPopPanel(UIPanelType panelType,BasePanel basePanel)
{
panelDict.Add(panelType, basePanel);
panelStack.Push(basePanel);
}
///
/// 把该页面显示在场景中
///
/// 需要显示界面的类型
public void PushPanel(UIPanelType panelType)
{
//判断一下栈里面是否有页面
if (panelStack.Count > 0)
{
panelStack.Peek().OnPause();//原栈顶界面暂停
}
BasePanel panel = GetPanel(panelType);
panel.OnEnter();//调用进入动作
panelStack.Push(panel);//页面入栈
}
///
/// 关闭栈顶界面显示
///
public void PopPanel()
{
//当前栈内为空,则直接返回
if (panelStack.Count <= 0) return;
panelStack.Pop().OnExit();//Pop删除栈顶元素,并关闭栈顶界面的显示,
if (panelStack.Count <= 0) return;
panelStack.Peek().OnResume();//获取现在栈顶界面,并调用界面恢复动作
}
}
点击主菜单按钮,实现各个面板之间的切换效果,源文件在末尾。
效果展示如下:
将制作好的面板做成预制体放在Resources文件中,如下所示:
将状态面板类型记录在枚举中,如下所示:
public enum UIPanelType
{
ItemMessagePanel,
CharacterPanel,
KnapsackPanel,
MainMenuPanel,
ShopPanel,
SkillPanel,
SystemPanel,
TaskPanel
}
根据面板和其路径,记录在JSON中,如下所示:
[
{
"panelType": "ItemMessagePanel",
"path": "UIPanel/ItemMessagePanel"
},
{
"panelType": "CharacterPanel",
"path": "UIPanel/CharacterPanel"
},
{
"panelType": "KnapsackPanel",
"path": "UIPanel/KnapsackPanel"
},
{
"panelType": "ShopPanel",
"path": "UIPanel/ShopPanel"
},
{
"panelType": "SkillPanel",
"path": "UIPanel/SkillPanel"
},
{
"panelType": "SystemPanel",
"path": "UIPanel/SystemPanel"
},
{
"panelType": "TaskPanel",
"path": "UIPanel/TaskPanel"
}
]
各个界面分别创建BasePanel的子类,用于实现界面的四种状态。为了方便实现界面的动画,使用DOTween插件,如果不会的话,可以看我之前的学习博客。 https://blog.csdn.net/qq_35361471/article/details/79353071
为各个界面添加CanvasGroup组件,如下:
Alpha可以方便实现隐藏和显示,取消勾选Blocks Raycasts,可以实现屏蔽UI射线检测
主菜单界面需要实现一个提供给各个界面按钮的方法接口,如下:
public void OnPushPanel(string panelTypeString)
{
UIPanelType panelType = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);
UIManager.Instance.PushPanel(panelType);
}
给对应按钮添加监听方法的时候,需要注意,这里的字符串要和枚举UIPanelType里的值写的一致。
以CharacterPanel.cs为例,继承BasePanel,并重写四种方法,使用DOTween实现动画,代码如下:
using UnityEngine;
using DG.Tweening;
public class CharacterPanel : BasePanel
{
private CanvasGroup canvasGroup;
void Start()
{
if (canvasGroup == null) canvasGroup = GetComponent
}
public override void OnEnter()
{
if (canvasGroup == null) canvasGroup = GetComponent
canvasGroup.alpha = 1;
canvasGroup.blocksRaycasts = true;
Vector3 temp = transform.localPosition;
temp.x = -800;
transform.localPosition = temp;
transform.DOLocalMoveX(0, 0.5f);
}
public override void OnPause()
{
canvasGroup.blocksRaycasts = false;
}
public override void OnResume()
{
canvasGroup.blocksRaycasts = true;
}
public override void OnExit()
{
transform.DOLocalMoveX(-800, .5f).OnComplete(() => canvasGroup.alpha = 0);
}
public void OnClosePanel()
{
UIManager.Instance.PopPanel();
}
}