|
图源/网络
接下来实现弹窗功能。
首先,需要明确的是,这边所说的弹窗到底是什么?
此处所说的弹窗,指的是非主界面的所有弹出窗口,里面包括一些界面,比如系统设置、科技界面等。
这些窗口打开的时候,主界面依然是存在的,并且可能作为UI的背景之一,因为这些UI会比主界面要小一些。
并且,很可能同一时间会打开多个弹窗,比如最典型的的,我在科技树弹窗上,选择提升某个科技,这时候会弹出一个确认的新弹窗,并且前面那个科技树弹窗还存在。
为了便于操作,可以定这样的规则:
弹窗上操作可以弹出新的弹窗,但是玩家只能操作最新出现的那个弹窗。
继续回到科技树那个例子上,我在科技树上选择了科技A,这时弹出了一个窗口,让我确认是否花费xxx升级科技A。
这时候,有些游戏是可以不点那个确认窗口,继续点科技树上的科技B的。点完后,那个确认弹窗的文字会变成“是否花费xxx升级科技B”。
而我简化了一下这个操作并规定,这时候玩家只能操作那个确认弹窗界面,要么点取消,要么点确认。玩家想要改升级科技B,需要先点取消把弹窗关闭,然后再选择科技B。
当然,如果以后觉得可以优化体验,可以改成前面说的这种,多个窗口并存的模式,但不是现在。这么一来,逻辑就简单多了,方面快速把游戏原型给做出来。
接下来是具体功能的实现,首先是修改游戏的参数名:
- public class UIRoot: MonoBehaviour {
- ……
- private readonly Dictionary<UIType, BaseUI> uiDic = new Dictionary<UIType, BaseUI>();
- private readonly Stack<BaseUI> CurrentAboveUI = new Stack<BaseUI>();
- ……
- }
复制代码
将原来的NormalUIDic重命名为UIDic。因为接下来,弹窗的UI也将存放到这个字典中。
同时新建一个Stack用于存放正在使用的UI,类似于上一篇文章中的CurrentUI。
Stack类型即堆栈,有点类似于List,用于存放多个相同类型的对象。和List不同的是,他的存储是先进先出的,即我按A-B-C的顺序依次存入一个Stack之后,我要取出,必然只能是C-B-A。这一特点和弹窗的设计是一致的。
Stack的用法很简单,这里主要用到了下面几个方法:
- Stack<a> aStack = new Stack<a>();//创建一个a类型的堆栈
- int count = aStack.Count;//获取堆栈中元素的个数
- a newA = new a();
- aStack.Push(newA);//将一个元素存放进堆栈
- a theA = aStack.Pop();//获得堆栈中最新存进去的元素,并在堆栈中删除它
复制代码
于是,我们可以模仿上一篇文章中如何打开一个新界面的方法写如何打开一个弹窗:
- private readonly Stack<BaseUI> CurrentAboveUI = new Stack<BaseUI>();
- public void OpenKeepAboveUI(UIType uiType)
- {
- BaseUI theUI;
- if (UIDic.ContainsKey(uiType))
- {
- theUI = UIDic[uiType];
- }
- else
- {
- theUI = Instantiate(Resources.Load<BaseUI>(UIConfig.UIPath[uiType])) as BaseUI;
- UITool.AddChild(theUI.transform, KeepAboveUI);
- UIDic.Add(uiType, theUI);
- }
- CurrentAboveUI.Push(theUI);//将theUI放入堆栈
- theUI.transform.SetAsLastSibling();//将该界面放在最上面那一层
- theUI.OpenUI();
- }
复制代码
和主界面不同的是,由于主界面是互斥的,所以主界面没有单独必要写一个关闭主界面的函数。但弹窗需要:
- public void CloseKeepAboveUI()
- {
- if (CurrentAboveUI.Count == 0)
- {
- return;
- }
- BaseUI theUI = CurrentAboveUI.Pop();//获得最上一层的UI,并在堆栈中删除它
- theUI.CloseUI();
- }
复制代码
这个方法用来删除最上层的弹窗,毕竟在设计中,弹窗只能操作最上面的那个。
好了,现在可以做几个界面来测试一下:
先右键UIRoot下Keep Above UI这个gameobject,然后选择创建一个新的Panel,并重命名为Test Above UI
然后再在这个Panel上,放2个image和2个button,组成一个最简单的UI。这个UI包括背景(用于填充整个窗口以防止点击到主界面上的按钮上去),窗口本体,关闭按钮,打开新弹窗的按钮。
其中NewUIButton是中间蓝色的那个,用于打开另一个新的弹窗(这个弹窗还没做),黄色按钮为关闭按钮即CloseButton。
建立一个新的脚本,叫TestAboveUI,并让它继承自BaseUI,并加入以下代码,以实现关闭窗口的功能:
- public class TestAboveUI : BaseUI
- { private Button CloseButton;
- private void Awake()
- {
- CloseButton = UITool.FindChildByName(gameObject, "CloseButton").GetComponent<Button>();
- CloseButton.onClick.AddListener(CloseThisUI);
- }
- public void CloseThisUI()
- {
- UIRoot.Instance.CloseKeepAboveUI();
- }
- }
复制代码
挂在刚刚我们建的那个窗口上。
将该窗口做成prefab放在其他的窗口prefab一起。
在UIConfig文件中添加对应的索引:
- public class UIConfig
- {
- public static Dictionary<UIType, string> UIPath = new Dictionary<UIType, string>
- {
- { UIType.StartUI,"UIPrefabs/StartUI"},
- { UIType.GameSettingUI,"UIPrefabs/GameSettingUI"},
- { UIType.TestAboveUI,"UIPrefabs/TestAboveUI"},
- };
- }
复制代码
我们再打开上一篇文章中做的GameSettingUI,并添加一个新的Button,名叫OpenTestUI。
打开GameSettingUI的脚本,添加该按钮相关的代码:
- public class GameSettingUI : BaseUI {
- ……
- private Button openTestUI;
- private void Awake()
- {
- ……
- openTestUI = UITool.FindChildByName(gameObject, "OpenTestUI").GetComponent<Button>();
- openTestUI.onClick.AddListener(OpenTestUI);
- }
- ……
- public void OpenTestUI()
- {
- UIRoot.Instance.OpenKeepAboveUI(UIType.TestAboveUI);
- }
- }
复制代码
原理上一篇文章中已经讲过了,就不展开说了。
运行程序,可以在GameSettingUI窗口中,打开弹窗,然后点击黄色按钮关闭。
相关阅读:
从零开始做一个SLG游戏(一):六边形网格
从零开始做一个SLG游戏(二):用mesh实现简单的地形
从零开始做一个SLG游戏(三):用unity绘制图形
从零开始做一个SLG游戏(四):UI系统之主界面搭建
作者:观复
专栏地址:https://zhuanlan.zhihu.com/p/60004604
|
|