引擎: CocosCreator 3.8.0
环境: Mac
Gitee: oops-game-kit
引言
oops-framework是由作者dgflash
编写,基于CocosCreator 3.x而实现的开源框架。
该框架以插件形式存在,主要目的是为了降低与项目的耦合,并且通过插件内部的命令快速的获取最新版本。
该框架的特性有:
- 提供游戏常用的功能库,提高开发效率
- 提供业务模块代码模版,降低程序设计难度
- 内置模块低耦合,可根据需要自行删减,以适应不同的类型
- 提供了常用的插件工具,支持Excel表转Json、支持热更新、AB包
- 增加了ECS、MVVM框架相关,以及常用的屏幕适配,UI管理,多语言等等
为了方便大家更好的学习和使用该框架,作者很贴心的准备了一些学习资料:
注:oops-framework框架QQ群: 628575875
在CocosCreator官方商店,可以通过 oops 搜索更多的框架项目Demo进行学习。
界面管理
框架的UI界面主要在场景的gui节点中,该节点被LayerManager
模块管理。框架初始化的主要逻辑有:
// ../extensions/oops-plugin-framework/assets/core/Roots.ts
export class Root extends Component {
@property({type: Node, tooltip: "界面层"})
gui: Node = null!;
protected init() {
oops.gui = new LayerManager(this.gui);
this.initGui();
}
}
// assets/script/Main.ts
import { UIConfigData } from './game/common/config/GameUIConfig';
export class Main extends Root {
protected initGui() {
// 初始化UI界面配置数据进行初始化
oops.gui.init(UIConfigData);
}
}
在实际的应用中,对于页面的打开、隐藏我们只要编写:
// 打开新页面
oops.gui.open(UIID.GAME_LOGIN);
// 关闭指定页面
oops.gui.remove(UIID.Loading);
oops.gui的调用入口在 Oop.ts
中
export class oops {
static gui: LayerManager;
}
UIID配置信息在GameUIConfig.ts
中,该文件主要用于配置UI页面的数据相关
// GameUIConfig.ts
export enum UIID {
Loading = 1,
Window,
Netinstable,
}
export var UIConfigData: { [key: number]: UIConfig } = {
[UIID.Loading]: { layer: LayerType.UI, prefab: "common/prefab/loading"},
[UIID.Netinstable]: { layer: LayerType.PopUp, prefab: "common/prefab/netinstable" },
[UIID.Window]: { layer: LayerType.Dialog, prefab: "common/prefab/window" },
}
// LayerManager.ts
export interface UIConfig {
bundle?: string; /** 远程包名 */
layer: LayerType; /** 窗口层级 */
prefab: string; /** 预制资源相对路径 */
}
简单的理解页面的构建就是:
- 构建预制体页面UI
- 打开
GameUIConfig.ts
文件,配置页面枚举类型,配置页面类型,资源路径 - 调用
oops.gui.open(UIID)
LayerManager
LayerManager
主要用于管理不同的UI页面,构造函数实现如下:
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;
}
主要页面的类型有:
类型 | 说明 |
---|---|
Game | 游戏层,比如地图逻辑处理 |
UI | 主界面层,比如地图上方的菜单页面 |
PopUp | 弹窗层, 窗口显示后,支持非窗口区域点击,可显示多个不同配置的弹窗 |
Dialog | 模式窗口层,窗口显示后,非窗口区域不可透点 |
System | 系统窗口层,与Dialog类似,可用于显示系统信息的弹窗错误提示 |
Notify | 提示信息层, Tip信息显示,显示以后会上移消失 |
Guide | 新手引导层,用于新手的强制引导 |
页面的继承结构:
常用的参数或接口:
参数或接口 | 说明 |
---|---|
root | 获取界面根节点 |
camera | 获取界面摄像机 |
game | 获取游戏界面根节点 |
guide | 获取新手引导 |
uiMap | 获取界面地图 |
setUIMap() | 界面地图配置数据 |
toast() | Tip提示显示,支持是否显示多语言 |
open() | 根据uiId,同步打开某个页面 |
openAsync() | 根据uiId,异步打开某个页面 |
has() | 根据uiId, 检测是否存在某个页面 |
remove() | 根据uiId,移除某个页面或窗口 |
removeByNode() | 根据this框架添加的节点,移除某个页面或窗口 |
clear() | 清除所有窗口 |
通过 oops.gui.open 打开某个窗口,看下框架的逻辑实现:
/*
@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;
}
}
页面的打开是支持页面参数传递的,新页面可以通过 UICallbacks 的回调调用
// ../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
}
注:新页面的打开传递参数可调用onAdded方法
常用示例
打开关闭页面
oops.gui.open(UIID.UI_MAIN);
// 关闭方式1: 通过UIID移除窗口,默认释放
oops.gui.remove(UIID.Loading);
// 方式2: 通过this.node释放窗口,默认保留对象
oops.gui.open(UIID.UI_MAIN);
oops.gui.removeByNode(this.node, true);
打开新页面传递参数
let params = {
data: "oops"
}
oops.gui.open(UIID.MAIN, param);
// MainLayer.ts
export class MainLayer extends Component {
onAdded(params?: any) {
if (!params) {
return;
}
let data = params.data;
// todo
}
}
弹窗页面显示
弹窗的类型虽有PopUp,Dialog,System的几种类型,但他们是类似的:
- PopUp 打开以后,支持非窗口区域透点,支持打开多个不同配置的弹窗
- Dialog 仅支持显示一个,非窗口区域不可透点
-
System 同Dialog类似,作者
dgflash
增加这个处理的原因主要是为了区别窗口提示的不同类型,比如客户端自身和服务器的提示,方便问题的定位。
增加一个窗口的UI预制体,如下图所示:
GameUIConfig.ts中增加配置后, 添加示例:
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);
}
一般弹窗的出现是需要有显示或隐藏动画的,我们可以通过回调方法:
- onAdded 增加显示动画
- onBeforeRemove 增加隐藏动画
动画的显示可以通过tween
缓动系统或CocosCreator的Animation
组件进行添加:
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示例
提示内容的显示就相对简单了,主要代码如下:
private _tipIndex: number = 0;
public clickTip(event, customData: string) {
this._tipIndex++;
// 参数:内容,是否使用多语言默认false
oops.gui.toast(`这是第${this._tipIndex}个提示`);
}
最后,祝大家学习生活工作愉快!