引擎: CocosCreator 3.8.0
环境: Mac
Gitee: oops-game-kit
注: 作者
dgflash
的oops-framework框架QQ群: 628575875
回顾
在上文中主要通过oops-game-kit大家了一个新的模版项目, 主要注意项是resources目录下的两个文件夹:
- common 用于放置公共资源,在游戏启动时进行的必须加载
- game 用于放置动态非必须资源,在游戏进度条显示时进行的加载
两个文件夹必须存在,否则程序会有报错。更多内容,可参考博客:
今天的文章主要讲述下框架的界面管理,以用于创建页面、切换页面、弹窗显示等处理。
若理解不当,欢迎您的指出。
界面管理
oops-framework 的作者dgflash
针对于界面的管理主要分为了如下几个类型:
类型 | 说明 |
---|---|
Game | 游戏层,比如地图逻辑处理 |
UI | 主界面层,比如地图上方的菜单页面 |
PopUp | 弹窗层, 窗口显示后,支持非窗口区域点击,可显示多个不同配置的弹窗 |
Dialog | 模式窗口层,窗口显示后,非窗口区域不可透点 |
System | 系统窗口层,与Dialog类似,可用于显示系统信息的弹窗错误提示 |
Notify | 提示信息层, Tip信息显示,显示以后会上移消失 |
Guide | 新手引导层,用于新手的强制引导 |
注:界面类型来源于
LayerManager.ts
的枚举: LayerType
页面的显示,我们通常使用预制体Prefab
实现,然后挂载上组件脚本。在框架中编写组件脚本,不需要特意的去继承什么,依然是Component
即可。
页面构建后,需要在GameUIConfig.ts
中进行配置:
typescript
// 界面唯一标识ID
export enum UIID {
Loading = 1, // 资源加载界面
Window, // 弹窗界面
Netinstable, // 加载与延时提示界面
UI_MAIN, // UI界面
}
/*
界面配置数据 UIConfig
export interface UIConfig {
bundle?: string; // 包名
layer: LayerType; // 页面类型
prefab: string; // 预制资源相对路径
}
*/
export var UIConfigData: { [key: number]: UIConfig } = {
[UIID.Loading]: { layer: LayerType.UI, prefab: "common/prefab/loading", bundle: "resources"},
[UIID.Netinstable]: { layer: LayerType.PopUp, prefab: "common/prefab/netinstable",bundle: "resources"},
[UIID.Window]: { layer: LayerType.Dialog, prefab: "common/prefab/window", bundle: "resources"},
[UIID.UI_MAIN]: { layer: LayerType.UI, prefab: "game/prefab/uiMain", bundle: "resources"},
}
在配置完成以后,显示或隐藏页面的简单使用:
typescript
// 显示UI主页面
oops.gui.open(UIID.UI_MAIN);
// 关闭加载页面
oops.gui.remove(UIID.Loading);
LayerManager
LayerManager 管理类是框架提供的界面管理类,在框架的Oops.ts
中提供的入口:
typescript
// ../oops-plugin-framework/assets/core/Oop.ts
export class oops {
static gui: LayerManager;
}
该管理类主要负责对不同页面的管理,提供的主要接口有:
参数或接口 | 说明 |
---|---|
root | 获取界面根节点 |
camera | 获取界面摄像机 |
game | 获取游戏界面根节点 |
guide | 获取新手引导 |
uiMap | 获取界面地图 |
setUIMap() | 设置界面地图配置 |
init() | 初始化所有UI配置 |
setConfig() | 根据uiId,设置UI配置 |
toast() | Tip提示显示,支持是否显示多语言 |
open() | 根据uiId,同步打开某个页面或窗口 |
openAsync() | 根据uiId,异步打开某个页面或窗口 |
has() | 根据uiId, 检测是否存在某个页面或窗口 |
remove() | 根据uiId,移除某个页面或窗口 |
removeByNode() | 根据this框架添加的节点,移除某个页面或窗口 |
clear() | 清除所有窗口 |
看下LayerManager的构造函数实现:
typescript
constructor(root: Node) {
this.root = root;
this.camera = this.root.getComponentInChildren(Camera)!;
// 不同界面类型构建节点,然后顺序添加到根节点中
this.game = this.create_node(LayerType.Game);
this.ui = new LayerUI(LayerType.UI);
this.popup = new LayerPopUp(LayerType.PopUp);
this.dialog = new LayerDialog(LayerType.Dialog);
this.system = new LayerDialog(LayerType.System);
this.notify = new LayerNotify(LayerType.Notify);
this.guide = this.create_node(LayerType.Guide);
// 注意下层级, LayerType.Game的最低,LayerType.Guide的最高
root.addChild(this.game);
root.addChild(this.ui);
root.addChild(this.popup);
root.addChild(this.dialog);
root.addChild(this.system);
root.addChild(this.notify);
root.addChild(this.guide);
}
private create_node(name: string) {
var node = new Node(name);
node.layer = Layers.Enum.UI_2D;
// 添加widget组件,设置上下左右对齐和对齐模式
var w: Widget = node.addComponent(Widget);
w.isAlignLeft = w.isAlignRight = w.isAlignTop = w.isAlignBottom = true;
w.left = w.right = w.top = w.bottom = 0;
w.alignMode = 2;
w.enabled = true;
return node;
}
LayerManager主要管理的页面是:
- LayerUI 用于管理主页面UI层
- LayerPopUp 用于管理PopUp弹窗
- LayerDialog 用于管理Dialog模式窗口,System系统窗口
- LayerNotify 用于管理Notify提示
继承结构如下:
主界面 游戏层 引导层 PopUp弹窗 Toast消息 Dialog窗口/System窗口 LayerUI Node Game Guide LayerPopUp LayerNotify LayerDialog
然后我们看下打开窗口的逻辑处理,有助于后面示例的理解:
typescript
/*
@func: 同步打开一个窗口
@param: uiId 窗口唯一标识符ID
@param: uiArgs 页面参数,可以通过回调对象的onAdded或onRemoved回调获取
@param: callbacks 回调对象
*/
open(uiId: number, uiArgs: any = null, callbacks?: UICallbacks): void {
var config = this.configs[uiId];
if (config == null) {
warn(`打开编号为【${uiId}】的界面失败,配置信息不存在`);
return;
}
switch (config.layer) {
case LayerType.UI:
this.ui.add(config, uiArgs, callbacks);
break;
case LayerType.PopUp:
this.popup.add(config, uiArgs, callbacks);
break;
case LayerType.Dialog:
this.dialog.add(config, uiArgs, callbacks);
break;
case LayerType.System:
this.system.add(config, uiArgs, callbacks);
break;
}
}
打开的逻辑处理其实很简单,从UI配置中获取配置数据,然后通过add
设置不同窗口的显示逻辑。
示例
简单的示例,在上面有说过。在项目开发中,如果切换页面可以这样:
typescript
// 关闭当前页面
oops.gui.remove(UIID.Loading);
// 显示新的页面
oops.gui.open(UIID.UI_MAIN);
这样的处理方式主要是为了避免: 页面资源较多,切换页面时导致黑屏情况的出现。
框架针对于这种情况,通过页面回调的方式做了处理,主要定义是: UICallbacks
typescript
// ../oops-plugin-framework/assets/core/gui/layer/Defines.ts
export interface UICallbacks {
// 节点添加到层级以后的回调,参数为当前页面节点,传递参数
onAdded?: (node: Node, params: any) => void,
// 窗口节点destroy之后回调,参数为当前页面节点,传递参数
onRemoved?: (node: Node | null, params: any) => void,
// 页面在移除的时候,进行的调用,可用于隐藏动画的显示,参数为当前页面节点,回调
// 注意:如果调用`this.node.destroy()`,该回调将直接忽略
onBeforeRemove?: (node: Node, next: Function) => void
}
注:这个接口的定义在页面参数传递或动画播放中使用很频繁。
弹窗示例
在框架的弹窗中, 虽有PopUp,Dialog,System的几种类型,但他们是类似的
- PopUp 打开以后,支持非窗口区域透点,支持打开多个不同配置的弹窗
- Dialog 仅支持显示一个,非窗口区域不可透点
- System 同Dialog 类似,作者
dgflash
增加这个处理的原因主要是为了区别窗口提示的不同类型,比如客户端自身和服务器的提示,方便问题的定位。
增加一个窗口的UI预制体,如下图所示:
GameUIConfig.ts中增加配置后, 添加示例:
typescript
public openWindow(event, customData: string) {
let params = {
title: "窗口标题",
content: "这是一段描述",
}
let callBack: UICallbacks = {
onAdded: (node: Node, params: any) => {
console.log("onAdded获取传递的参数:", params)
},
onRemoved:(node: Node | null, params: any) => {
console.log("onRemoved获取传递的参数:", params)
}
}
oops.gui.open(UIID.UI_POPUP, params, callBack);
}
如果想在指定的页面中获取传入的参数,可以这样:
typescript
@ccclass('UIPop')
export class UIPop extends Component {
@property(Label) titleLabel: Label = null;
@property(Label) descLabel: Label = null;
private _okFunc = null;
// 增加onAdded的回调,获取参数
onAdded(args: any) {
if (args) {
this._okFunc = args.okFunc || null;
this.titleLabel.string = args.title || "";
this.descLabel.string = args.content || "未知错误";
}
}
onClose() {
if (this._okFunc) {
this._okFunc();
}
oops.gui.removeByNode(this.node);
}
}
一般弹窗的出现是需要有显示或隐藏动画的,我们可以通过回调方法:
- onAdded 增加显示动画
- onBeforeRemove 增加隐藏动画
动画的显示可以通过tween
缓动系统或CocosCreator的Animation
组件进行添加:
typescript
public clickSystem(event, customData: string) {
console.log(customData);
let params = {
title: `系统窗口`,
content: "数据异常",
}
oops.gui.open(UIID.UI_SYSTEM, params, this.getPopCommonEffect());
}
// 弹窗动画
private getPopCommonEffect(callbacks?: PopViewParams) {
let newCallbacks: PopViewParams = {
// 节点添加动画
onAdded: (node, params) => {
node.setScale(0.1, 0.1, 0.1);
tween(node)
.to(0.2, { scale: new Vec3(1, 1, 1) })
.start();
},
// 节点删除动画
onBeforeRemove: (node, next) => {
tween(node)
.to(0.2, { scale: new Vec3(0.1, 0.1, 0.1) })
.call(next)
.start();
},
}
if (callbacks) {
if (callbacks && callbacks.onAdded) {
let onAdded = callbacks.onAdded;
callbacks.onAdded = (node: Node, params: any) => {
onAdded(node, params);
newCallbacks.onAdded(node, params);
};
}
if (callbacks && callbacks.onBeforeRemove) {
let onBeforeRemove = callbacks.onBeforeRemove;
callbacks.onBeforeRemove = (node, params) => {
onBeforeRemove(node, params);
newCallbacks.onBeforeRemove(node, params);
};
}
return callbacks;
}
return newCallbacks;
}
注:作者在oops-framework 的
TipsManager.ts
中增加了更多的窗口示例,推荐查看学习
Toast示例
提示内容的显示就相对简单了,主要代码如下:
typescript
private _tipIndex: number = 0;
public clickTip(event, customData: string) {
this._tipIndex++;
// 参数:内容,是否使用多语言默认false
oops.gui.toast(`这是第${this._tipIndex}个提示`);
}
总结
在框架中对界面的显示和隐藏主要逻辑如下:
- 页面的构建,继承于
Component
即可,如果需要获取参数,就增加 onAdded 方法 - 在
GameUIConfig.ts
中设置 UIID 的唯一标识符ID 和 UIConfig 配置 - 页面的调用,使用
open
或openAsync
;提示的显示使用Toast
最后注意:
- 任何页面的显示,不建议重复调用
typescript
oops.gui.open(UIID.UI_LAYER, {param: "参数"});
oops.gui.open(UIID.UI_LAYER, {param: "参数"});
注:框架会告知路径为【game/prefab/uiLayer】的预制重复加载
- PopUp的弹窗支持显示多个,指的是不同配置的Pop弹窗,原因1
感谢作者dgflash
的分享,作者CSDN博客: dgflash CSDN
最后,祝大家学习和生活愉快!