关于界面栈系统
概述
游戏内,关于界面的跳转逻辑,一般是有一定规则的,例如
- 从主界面
- 打开全屏界面A
- 打开弹出框B
- 关闭A和B后,打开C
- 关闭C,弹出A,再弹出B
- 关闭界面B
- 关闭界面A
- 打开主界面
- 打开副本界面中,点击某副本
- 弹出副本详情,点开战
- 关闭所有界面,进入战斗场景
- 等战斗结束,弹结算界面,清理战场
- 弹回之前的副本和详情界面,并恢复当时的状态
很啰嗦,但是这比较常见的流程,这里面有什么可以抽象出来的?可以达成什么目标?
目标
- 开发者不需要特别关注打开一个界面时,后面的界面是个什么状态
- 开发者不需要特别关注在关闭一个界面的时候,该打开什么界面(除非有特殊跳转)
- 进战斗前,开发者不必每个人都记录当前打开的界面
- 出战斗后,开发者并不需要自己打开进战斗前的界面
- 但是,界面内的还原工作,还是需要每个人自己做
抽象
1.界面类别:
- A级全屏界面
- B级半屏界面
- C级不重要的弹出界面(确认框,飘字等)
2.记录一个当前已经打开的Main层的界面queue(或list) standBy
3.记录一个当前已关闭的所有界面,用数据结构-栈,pageStack
4.根(Root),定义HomePage为根界面,每次打开HomePage,清理掉pageStack和standBy,降低复杂度与栈的深度
5.正向打开逻辑
- 如果是打开半屏界面,别的事什么也不做
- 如果是打开全屏界面,那么之前打开的所有界面都可以回对象池了,同时记录一个当时的数据包,push到pageStack中,并将当前界面放入standBy中
6.反向后退逻辑
- 如果是关闭弹出界面,只关闭,别的事什么也不做
- 如果是关闭全屏界面,那要从记录的数据包合集里从后往前查找,逻辑如下:
- 先创建一个local的tempStack的栈
- 如果pageStack是空的,直接弹出根界面
- pageStack:pop() ,如果是半屏界面,压入tempStack,还需要继续往前弹
- pageStack:pop() ,如果是全屏界面,压入tempStack,不需要再弹了
- 一次把tempStack再pop完,就是我们想要的先全屏,再半屏界面了
7. 避免循环弹栈
- Root->A->B 这时候栈内为A,standBy里为B
- 关闭B,这时候把A弹出来,没问题,但是回把B再入栈
- 这时候关闭A,我们期待回到HomePage并且清空PageStack,但是实际会把B弹出来,同时A又入栈里造成循环弹AB
- 处理办法:如果是单纯Close界面,先把自己从standBy干掉
解释
1. 为什么存储已关闭界面要用stack
这里是后进先出的原则,最后压进去的数据,最先弹出来,来判断是否为全屏界面
2. stash 和pop的原理是什么
stash和pop的灵感来自git,我们在工作中,经常会被插入一些临时的活,但是我们手里还有着原来的工作,那怎么办?使用git的处理流程如下:
- 我们可以执行命令:git stash 就把我们的本地修改扔到缓存中了
- 等我们这边处理完插入的事物并提交后,这时现在的工作区又是一个干净的了
- 我们再执行命令:git stash pop
- 把之前暂存的本地修改弹出来,再继续我们原来的工作
同理,对比我们游戏内的流程
- 我们再要插入一个事务之前(比方说进战斗),我们执行PageStack:StashAll(),把当前状态catch住,
- 然后完成这个事务
- 执行PageStack:StashPop(),这个方法会把界面清理干净,然后把之前暂存的界面分别弹出来,达到了还原的目的
不过,我们的stash不支持多次执行,也没必要多次执行