oops-framework框架 之 界面管理(三)

引擎: CocosCreator 3.8.0

环境: Mac

Gitee: oops-game-kit

注: 作者dgflashoops-framework框架QQ群: 628575875


回顾


在上文中主要通过oops-game-kit大家了一个新的模版项目, 主要注意项是resources目录下的两个文件夹:

  • common 用于放置公共资源,在游戏启动时进行的必须加载
  • game 用于放置动态非必须资源,在游戏进度条显示时进行的加载

两个文件夹必须存在,否则程序会有报错。更多内容,可参考博客:

oops-framework框架 之 创建项目(二)

今天的文章主要讲述下框架的界面管理,以用于创建页面、切换页面、弹窗显示等处理。

若理解不当,欢迎您的指出。


界面管理


oops-framework的作者dgflash 针对于界面的管理主要分为了如下几个类型:

类型 说明
Game 游戏层,比如地图逻辑处理
UI 主界面层,比如地图上方的菜单页面
PopUp 弹窗层, 窗口显示后,支持非窗口区域点击,可显示多个不同配置的弹窗
Dialog 模式窗口层,窗口显示后,非窗口区域不可透点
System 系统窗口层,与Dialog类似,可用于显示系统信息的弹窗错误提示
Notify 提示信息层, Tip信息显示,显示以后会上移消失
Guide 新手引导层,用于新手的强制引导

注:界面类型来源于LayerManager.ts的枚举: LayerType

页面的显示,我们通常使用预制体Prefab实现,然后挂载上组件脚本。在框架中编写组件脚本,不需要特意的去继承什么,依然是Component即可。

页面构建后,需要在GameUIConfig.ts中进行配置:

// 界面唯一标识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"},
}

在配置完成以后,显示或隐藏页面的简单使用:

// 显示UI主页面
oops.gui.open(UIID.UI_MAIN);
// 关闭加载页面
oops.gui.remove(UIID.Loading);

LayerManager


LayerManager管理类是框架提供的界面管理类,在框架的Oops.ts中提供的入口:

// ../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的构造函数实现:

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

然后我们看下打开窗口的逻辑处理,有助于后面示例的理解:

/*
@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设置不同窗口的显示逻辑。


示例

简单的示例,在上面有说过。在项目开发中,如果切换页面可以这样:

// 关闭当前页面
oops.gui.remove(UIID.Loading);
// 显示新的页面
oops.gui.open(UIID.UI_MAIN);

这样的处理方式主要是为了避免: 页面资源较多,切换页面时导致黑屏情况的出现。

框架针对于这种情况,通过页面回调的方式做了处理,主要定义是: 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
}

注:这个接口的定义在页面参数传递或动画播放中使用很频繁。

弹窗示例

在框架的弹窗中, 虽有PopUp,Dialog,System的几种类型,但他们是类似的

  • PopUp 打开以后,支持非窗口区域透点,支持打开多个不同配置的弹窗
  • Dialog 仅支持显示一个,非窗口区域不可透点
  • SystemDialog类似,作者dgflash增加这个处理的原因主要是为了区别窗口提示的不同类型,比如客户端自身和服务器的提示,方便问题的定位。

增加一个窗口的UI预制体,如下图所示:
oops-framework框架 之 界面管理(三)_第1张图片

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);
}

如果想在指定的页面中获取传入的参数,可以这样:

@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组件进行添加:

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-frameworkTipsManager.ts中增加了更多的窗口示例,推荐查看学习

Toast示例

提示内容的显示就相对简单了,主要代码如下:

private _tipIndex: number = 0;

public clickTip(event, customData: string) {
  this._tipIndex++;
  // 参数:内容,是否使用多语言默认false
  oops.gui.toast(`这是第${this._tipIndex}个提示`);
}

总结

在框架中对界面的显示和隐藏主要逻辑如下:

  1. 页面的构建,继承于Component即可,如果需要获取参数,就增加 onAdded 方法
  2. GameUIConfig.ts中设置 UIID的唯一标识符ID 和 UIConfig 配置
  3. 页面的调用,使用openopenAsync;提示的显示使用Toast

最后注意:

  1. 任何页面的显示,不建议重复调用
oops.gui.open(UIID.UI_LAYER, {param: "参数"});
oops.gui.open(UIID.UI_LAYER, {param: "参数"});

注:框架会告知路径为【game/prefab/uiLayer】的预制重复加载

  1. PopUp的弹窗支持显示多个,指的是不同配置的Pop弹窗,原因1

感谢作者dgflash的分享,作者CSDN博客: dgflash CSDN

最后,祝大家学习和生活愉快!

你可能感兴趣的:(oops-framework,cocos2d)