四邑隐侠, 本名关健昌, 10 年游戏生涯, 现隐居四邑. 本系列文章以 TypeScript 为介绍语言.
我们已经知道怎样制作, 加载, 显示界面. 但 cocos 没有提供一个弹窗管理模块, 对于一个多人合作的项目, 没有统一的管理, 界面层级容易混乱. 作为主程, 在项目开始就应该处理好这些问题, 将弹窗划分为不同的层次, 不同类型的信息显示在不同的层中. 下面将讲解怎样设计弹窗堆栈.
一般地, 从下向上, 我会将弹窗划分为以下层:
1) 内容层, 展示游戏相关的信息界面.
2)tips 层, 显示提示性信息界面, 例如获得物品的浮窗, 网络异常的提示.
3) 新手引导层, 主要显示新手引导的手指, 新手提示文本框等.
4)alert 层, 主要显示系统级的信息, 错误, 例如断网, 被踢下线.
5)loading 层, 显示加载动画.
具体怎样实现? 弹窗通常有统一的动画, 所以定义一个界面逻辑的基类 ViewCtrl, 定义一些通用的属性和方法. 每个层中可能同时存在多个界面, 我把每个层做成组 ViewGroup. 再写一个类 PopupCtrl, 对界面分层, 并对外提供编程接口.
先来说说 ViewCtrl 这个类, 继承自 cc.Component, 这样我们写的 ViewCtrl 子类就可以挂载到 prefab 的节点上. 定义两个 boolean 属性: hasMask,touchOutClose, 用于控制是否有灰色半透明遮罩, 和是否点击弹窗外关闭. 后面在介绍 ViewGroup 时会介绍怎样实现这两个功能.
cc.Component 本身有 onEnable,onDisable 两个生命周期方法, 但我们希望同屏不出现太多弹窗, 弹出新弹窗时隐藏上一个弹窗, 这会导致这两个方法频繁调用. 对于打开界面时的刷新, 在界面放到堆栈时回调更合适. 同理对重用的界面的重置, 在界面移出堆栈时回调重置. 可以在 ViewCtrl 定义 onAddToStack 和 onRemoveFromStack 两个生命周期方法, 在 ViewGroup 里在合适的时机触发, 后面会介绍. 再添加两个方法, onPlayShowAni,onPlayHideAni, 在这里可以实现统一的弹窗动画. 子类的特殊显示和关闭动画由子类重写这两个方法实现. 这两个方法也由 ViewGroup 在合适的时机触发.
- /**
- * auth: 关健昌
- * date: 2018-11-17
- * desc: 界面基类
- * modify:
- */
- const {ccclass, property} = cc._decorator;
- @ccclass
- export default class ViewCtrl extends cc.Component {
- @property
- hasMask: boolean = true;
- @property
- touchOutClose: boolean = false;
- public onAddToStack(): void {
- }
- public onRemoveFromStack(): void {
- }
- public onPlayShowAni(): void {
- }
- public onPlayHideAni(): void {
- }
- }
接下来是 ViewGroup 这个类, 它代表一个弹窗层, 也继承自 cc.Component, 它的 node 可以添加到 PopupCtrl 的 node 上从而展示这个弹窗层. 它应该有自己的 maskLayer,blockLayer, 用于显示灰色半透明遮罩, 和屏蔽下层界面的触屏事件. 当然, 像 tips 层, 我们不希望屏蔽下层界面的触屏事件, 可以提供接口来控制这两个 layer 是否生效.
ViewGroup 最重要的作用是管理一层的弹窗. 所以它应该有一个 Array<ViewCtrl > 类型的 viewArr 属性. 基本的操作包括:
- /**
- * auth: 关健昌
- * date: 2018-11-17
- * desc: 界面组
- * modify:
- */
- export default class ViewGroup extends cc.Component {
- private maskLayer: cc.Node = null;
- private blockLayer: cc.Node = null;
- private viewArr: Array<ViewCtrl> = [];
- public pushView(ctrl: ViewCtrl, hideOld: boolean): void {
- }
- public popView(cleanup: boolean): void {
- }
- public insertView(ctrl: ViewCtrl): void {
- }
- public removeView(ctrl: ViewCtrl): void {
- }
- public pushViews(ctrls: Array<ViewCtrl>, hideOld: boolean): void {
- }
- public removeAllViews(cleanup: boolean): void {
- }
- public lastView(): ViewCtrl {
- }
- public getViewByName(name: string): ViewCtrl {
- }
- public getViewCount(): number {
- }
- public isEmpty(): boolean {
- }
- }
其中 pushView,insertView,pushViews 会将对应 ViewCtrl 放入 viewArr, 并触发 ViewCtrl.onAddToStack. 通过 ViewGroup.node.addChild 将 ViewCtrl.node 添加为子节点将对应界面显示出来. 具体是否要添加到界面要看是否在 hideOld 的界面下面. pushViews 主要是为自动弹窗设计的, 例如登录后自动显示公告, 签到等, 关闭一个自动打开下一个.
popView,removeView,removeAllViews 通过 ViewCtrl.node.removeFromParent 将 ViewCtrl.node 移出界面, 同时将 ViewCtrl 移出 viewArr, 并触发 ViewCtrl.onRemoveFromStack.pushView,popView 还会触发 onPlayShowAni,onPlayHideAni 这两个生命周期方法.
现在来说说 PopupCtrl 这个类, 它也继承自 cc.Component, 可以直接挂载到 prefab 的节点上, 在介绍游戏入口时我们给 Canvas 添加了 popupLayer 子节点, 通过添加组件将 PopupCtrl 挂上去. PopupCtrl 要实现分层, 它有 Array<ViewGroup > 类型的 groupArr 属性. 在 onLoaded 生命周期方法里按弹窗层顺序, 通过 PopupCtrl.addChild 添加对应的弹窗层. PopupCtrl 提供跟 ViewGroup 类似的接口, 不同的是 PopupCtrl 的 pushView,insertView,pushViews 提供 layer 参数, 用于指定弹窗添加的层.
- enum PopupLayer {
- CONTENT = 0,
- TIPS,
- GUIDE,
- ALERT,
- LOADING
- }
弹窗管理先说到这里, 下一篇我们将介绍下怎样做资源加载管理.
来源: https://www.cnblogs.com/niudanshui/p/10224472.html