列记录下自己经常使用的这个UI框架。首先说下这个UI框架整体吧,该框架主要实现了UI的的显示、隐藏、按钮点击、UI数值更新,这也是大多数游戏UI的功能。
该框架主要分三个部分来理解,分别是窗口(window)、视图(view)、控制(control),看起来有点像mvc框架是吧,但这里并没有实现model数据这块,现在只是实现UI的一些显示功能,并不需要做Model数据处理内容,所以暂时不管。如果加上model模块,那么窗口和视图可以合成View的。
细说三部分
1、窗口(window):为了将UI分类,将其分成相应的窗口理解,如:商店窗口、设置窗口等;
2、视图(view):每个窗口内的具体UI视图,一个窗口可有多个UI视图,如:商店窗口有显示所有商品的视图、然后选择单个商品时又可以弹出一个商品详情视图,点击购买又可以弹出一个购买视图。当然,把这些都放在同一视图也行,但是就是显得脚本程序臃肿了,毕竟分类管理能让程序更直观清晰。
3、控制(control):用来实现UI的创建、记录、显示和隐藏等功能的具体逻辑。
代码实现
根据上面三部分可以创建三个类UIBaseWindow(窗口)、UIBaseView(视图)、UIWindowCtrl(控制);
详细如下图:
1、首先我们看看UIBaseWindow类下包含四个主要方法,由命名可以看出其功能分别是窗口的创建、显示、隐藏、重现;下面几个子类则可以直接继承这些功能并使用,这几个子类分别是主界面、商城、设置等窗口。子类的作用主要是用来初始化对应显示的视图。
2、下面看看UIBaseView类,该类的作用则是包含了具体的UI视图了,该基类也自带了几个非常重要的方法,包括Veiw的初始化、获取当前View所属窗口、设置View所属窗口、View的显示和隐藏,而继承该基类的子类除了显示具体的UI外,还扩展了按钮的点击事件;从上图看到多了一个叫CommonView的通用视图类,因为我们的View UI通常都是共用一个父窗口的,就是外部轮廓一致的,只有标题不同而已,而且窗口都会带有一个关闭按钮的,那么我们就可以把这些提取出来做成一个通用的CommonView视图了。
3、最后一个UIWindowCtrl类,顾名思义就是UI总控制类,包括窗口的具体创建逻辑、已有窗口的存储、已有窗口重现的判断逻辑等功能都是写在这个类。该类是个管理类,不允许继承的。
首先是窗口类UIBaseWindow:
public class UIBaseWindow {
public GameObject winParent = null;
public virtual void OnCreate(string winName)
{
if (winParent == null){
// GameSettings.GetInstance().UIWin是一个对象,可自定义,也可以直接使用CanVas,反正是用来存放UI的
winParent = ResMgr.CreateEmptyGameObject(GameSettings.GetInstance().UIWin, winName);
}
}
// 在当前窗口切换其他窗口
public void StartWindow(System.Type windowName)
{
UIWindowCtrl.GetInstance().ShowWindow(windowName);
}
// 重现当前窗口
public virtual void OnResume()
{
winParent.SetActive(true);
}
// 隐藏当前窗口
public virtual void OnStop()
{
winParent.SetActive(false);
}
}
该基类主要封装了几个方法,都很简单,主要说下几点。
(1)OnCreate(string winName)方法创建一个空对象,是用来存放该窗口的后续所有子对象,其中用到调用的方法是我封装的一个方法。
public static GameObject CreateEmptyGameObject(GameObject parent, string name)
{
GameObject obj = new GameObject(name);
obj.transform.SetParent(parent.transform);
obj.transform.localPosition = Vector3.zero;
obj.transform.localEulerAngles = Vector3.zero;
obj.transform.localScale = Vector3.one;
return obj;
}
2)StartWindow(System.Type windowName)方法是启动一个窗口,参数是一个窗口类名的类型,如:typeof(Win_Shop);至于调用的UIWindowCtrl类方法将在后面说明。
下面我们实现一个继承于基类的具体窗口类Win_Main。
public class Win_Main : UIBaseWindow {
private MainView mMainView;
public override void OnCreate(string winName)
{
base.OnCreate(winName);
mMainView = new MainView(this, "Prefas/UI/UIMain/MainView");
}
public override void OnResume()
{
base.OnResume();
}
}
好了,现在我们再来梳理下这个UI框架,以上面代码为例;比如我要显示一个主界面Main,这个界面主要包含一些背景图和、商城、设置、帮助、等按钮,这些都做成一个预设MainView,然后从上面代码看到一个同名类MainView,该类实现了预设的各种功能。所以当我要OnCreate()一个窗口时就会调用这个MainVeiw预设界面了。
下面继续实现第二个基类UIBaseView。
abstract public class UIBaseView {
public GameObject view;
private UIBaseWindow _window;
public UIBaseView(UIBaseWindow parent,string path)
{
_window = parent;
view = ResMgr.InitLoadPrefabsInParent(parent.winParent, path);
Start();
}
public abstract void Start();
// 获取当前View所属Window
public virtual UIBaseWindow GetWindow()
{
return _window;
}
// 设置当前View所属Window
public virtual void SetWindow(UIBaseWindow window)
{
_window = window;
}
public virtual void Hide()
{
view.SetActive(false);
}
public virtual void Show()
{
view.SetActive(true);
}
}
该类的一个实例MainView
public class MainView : UIBaseView {
public MainView(UIBaseWindow window,string path)
:base(window,path)
{
}
public override void Start()
{
view.SetOnClickListener(OnButtonClick);
}
private void OnButtonClick(GameObject btn)
{
switch(btn.name)
{
case BUTTON.Btn_Set:
// 调用当前View所属Window的StartWindow方法切换来窗口
GetWindow().StartWindow(typeof(Win_Set));
break;
case BUTTON.Btn_Shop:
GetWindow().StartWindow(typeof(Win_Shop));
break;
}
}
private struct BUTTON{
public const string Btn_Set = "Btn_Set";
public const string Btn_Shop = "Btn_Shop";
}
}
上面有段按钮点击回调方法绑定的代码view.SetOnClickListener(OnButtonClick);方法SetOnClickListener其实是在其他脚本封装的一个点击事件绑定方法,还是个扩展方法。
扩展方法
(1)给某个类扩展一个方法出来;
(2)该方法必须为static,参数一为类本身,必须带this关键字(this GameObject obj)
public static void SetOnClickListener(this GameObject obj,EventTriggerListener.methodDelegate method)
{
foreach (Button btn in obj.GetComponentsInChildren<Button>()) // 获取obj所有子物体的Button组件
{
EventTriggerListener.getListener(btn.gameObject).onClick = method;
}
}
上面的MainView实现了点击按钮显示Set、Shop窗口。
最后一个类,也是最重要的一个控制类,功能包括窗口的创建、显示、
隐藏、重现、删除等。
public class UIWindowCtrl {
private UIBaseWindow currentWindow;
private List<UIBaseWindow> mWindowList;
private static UIWindowCtrl _instance;
public static UIWindowCtrl GetInstance(){
if (null == _instance)
{
_instance = new UIWindowCtrl();
}
return _instance;
}
private UIWindowCtrl()
{
mWindowList = new List<UIBaseWindow>();
}
// 当前显示的窗口
public UIBaseWindow GetCurrentWindow()
{
return currentWindow;
}
// 显示一个窗口
public void ShowWindow(System.Type type)
{
if (null != currentWindow && !(currentWindow.GetType().Equals(typeof(Win_Main))) )
{
// 如果是Win_Main主窗口不关闭,可以在其上加显示其他窗口
// 否则先关闭当前窗口,再显示其他窗口
if (!currentWindow.GetType().Equals(type))
{
WindowStop(currentWindow);
currentWindow = null;
}
}
UIBaseWindow window = WindowContains(type);
if(null!=window){
WindowResume(window);
}else{
CreateWindow(type);
}
}
// 创建一个窗口实例存于窗口列表
private void CreateWindow(Type type)
{
UIBaseWindow window;
window = Activator.CreateInstance(type) as UIBaseWindow;
mWindowList.Add(window);
currentWindow = window;
window.OnCreate(window.ToString());
WindowResume(window);
}
// 窗口的重现
public void WindowResume(UIBaseWindow window)
{
currentWindow = window;
window.OnResume();
}
// 判断窗口是否已存在,避免重复创建
private UIBaseWindow WindowContains(Type type)
{
foreach(UIBaseWindow window in mWindowList){
if(type.Equals(window.GetType())){
return window;
}
}
return null;
}
public void WindowStop(UIBaseWindow window)
{
window.OnStop();
}
public void ClearWindowData()
{
if(mWindowList.Count>0){
mWindowList.Clear();
}
currentWindow = null;
}
}