ZMUIL5——堆栈系统

堆栈系统要做的工作

  1. 堆栈系统可以设置窗口弹出队列,按照设定好的顺序在特定的时机顺序弹出多个窗口
  2. 堆栈系统依次弹出窗口时,打开或关闭弹出队列外的窗口,不会影响堆栈系统的窗口弹出
  3. 堆栈系统可以在弹出窗口时,可以随时向队列内添加新的需要弹出的窗口,或者清除弹出队列,取消堆栈系统的窗口弹出

堆栈系统是任何游戏必不可少的一项功能,它可以用作诸如首次进入大厅时一些特殊或活动面板的有序自动弹出,
从而让玩家能够更好的去了解到游戏内容和新增功能。

UIModule的堆栈系统相关

堆栈系统会控制UI窗口的显示隐藏,而UIModule​负责窗口显隐的执行,因此堆栈系统主要在UIModule​内实现

堆栈系统使用队列的原因

堆栈系统实际是使用队列容器来装载要显示的窗口的,而不是栈,
因为栈是后进先出,而向堆栈系统添加要显示的面板的时间是不确定的,例如服务器发送消息的时间就不确定
如果使用后进先出的栈,假设在弹出窗口1后,窗口2和窗口3先后入栈,这会导致下一个弹出的窗口是窗口3,造成窗口弹出顺序的混乱
而队列的先进先出的顺序就很符合直觉,也不会造成弹出顺序的混乱

1
2
3
//UIModule内
private Queue<WindowBase> mWindowStack = new Queue<WindowBase>(); //堆栈系统的队列,用来管理弹窗的循环弹出
private bool mStartPopStackWindowStatus = false; //开始弹出堆栈的标志,用于处理多种情况,例如:正在出栈中有其他界面弹出,可以直接放到栈内进行弹出等

WindowBehaviour的堆栈系统相关

这里的PopStack​表示是否是从堆栈系统弹出的窗口,它决定隐藏该窗口时是否让堆栈系统弹出队列里的下一个窗口
PopStackListener​是窗口加入堆栈系统队列时的弹出窗口委托,当堆栈系统弹出该窗口时会执行该函数

1
2
3
//WindowBehaviour内
public bool PopStack { get; set; } //是否是通过堆栈系统弹出的弹窗,若是,则处于堆栈弹出的流程时,隐藏该窗口将弹出下一个窗口
public Action<WindowBase> PopStackListener { get; set; } //从堆栈系统出栈时的监听函数

堆栈系统执行流程

  1. 使用PushWindowToStack​方法使用泛型传入要加入队列的窗口,同时可以传入弹出窗口时执行的委托
    无论界面是否已加载都先new出来,将委托赋值给这个new出来的对象的PopStackListener​,然后将它加入到队列内
  2. 使用StartPopFirstStackWindow​将开启堆栈系统的窗口顺序弹出,将UIModule​的mStartPopStackWindowStatus​赋值为true​标记弹出的开始
    然后调用PopStackWindow​方法
  3. PopStackWindow​方法会检查队列是否拥有窗口,
    如果不存在就终止堆栈系统的弹出,将mStartPopStackWindowStatus​赋值为false
    如果存在,则队列弹出在PushWindowToStack​new出来的窗口,将该窗口传入堆栈系统专用PopUpWindow​方法内
    之前new出来的窗口会将PopStackListener​赋值给PopUpWindow​方法弹出的窗口,将其PopStack​赋值为true​,并执行PopStackListener​的委托
  4. 当有窗口隐藏或销毁时,会执行PopNextStackWindow​方法,如果关闭的这个窗口PopStack​为true​,就再次执行PopStackWindow
  5. PushAndPopStackWindow​方法会在加入队列时就直接开始弹出窗口
  6. ClearStackWindows​会清除队列,可以用于打断弹出队列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//堆栈系统专用弹出窗口方法,传入从堆栈系统弹出的WindowBase
private WindowBase PopUpWindow(WindowBase QueuePopWindow)
{
System.Type type = QueuePopWindow.GetType();
string windowName = type.Name;
//先确认是否加载过Window,若加载过,直接返回加载过的Window
WindowBase LoadedWindow = GetWindow(windowName);
if (LoadedWindow != null)
{
return ShowWindow(windowName);
}
//若未加载,则将弹出的WindowBase用于初始化
return InitializeWindow(QueuePopWindow, windowName);
}

#region 堆栈系统相关
/// <summary>
/// 向堆栈压入一个界面
/// </summary>
/// <typeparam name="T">要压入的窗口</typeparam>
/// <param name="popCallBack">出栈时要执行的监听函数</param>
public void PushWindowToStack<T>(Action<WindowBase> popCallBack = null) where T : WindowBase, new()
{
//这里的new出来的T暂时用于在队列内记录监听函数,
//后续出栈显示窗口时,若T已经加载过,则这里的监听函数将赋值给已经加载过的T,若未加载过再使用该T用于初始化
T windowBase = new T();
windowBase.PopStackListener = popCallBack;
mWindowStack.Enqueue(windowBase);
}

/// <summary>
/// 弹出堆栈中第一个弹窗
/// </summary>
public void StartPopFirstStackWindow()
{
if (mStartPopStackWindowStatus) return;
mStartPopStackWindowStatus = true; //表示已经开始进行堆栈弹出的流程
PopStackWindow();
}

/// <summary>
/// 压入并直接开始弹出堆栈弹窗
/// </summary>
/// <typeparam name="T">要压入的窗口</typeparam>
/// <param name="popCallBack">出栈时要执行的监听函数</param>
public void PushAndPopStackWindow<T>(Action<WindowBase> popCallBack = null) where T : WindowBase, new()
{
PushWindowToStack<T>(popCallBack);
StartPopFirstStackWindow();
}

/// <summary>
/// 弹出下一个窗口
/// </summary>
/// <param name="windowBase"></param>
private void PopNextStackWindow(WindowBase windowBase)
{
if (windowBase != null && mStartPopStackWindowStatus && windowBase.PopStack)
{
windowBase.PopStack = false;
PopStackWindow();
}
}

/// <summary>
/// 弹出堆栈弹窗
/// </summary>
/// <returns>是否从堆栈里弹出窗口</returns>
public bool PopStackWindow()
{
if (mWindowStack.Count > 0)
{
WindowBase recordWindow = mWindowStack.Dequeue();
WindowBase popWindow = PopUpWindow(recordWindow);
popWindow.PopStackListener = recordWindow.PopStackListener;
popWindow.PopStack = true; //表示是从堆栈系统里打开的窗口,关闭该窗口将重新执行这里的方法
popWindow.PopStackListener?.Invoke(popWindow);
popWindow.PopStackListener = null;
return true;
}
else
{
mStartPopStackWindowStatus = false; //表示堆栈弹出的流程结束
return false;
}
}

/// <summary>
/// 清空窗口堆栈,可用于中途取消堆栈弹出流程
/// </summary>
public void ClearStackWindows()
{
mWindowStack.Clear();
}
#endregion