查看原文
其他

CreatorPrimer|编写一个版本号组件

张晓衡 Creator星球游戏开发社区 2021-08-09

在集合类游戏中,不论是大厅还是子游戏都会涉及到版本的更新,在开发调试阶段,检查更新是否生效的一个直观的方法就是观察版本号的变化,因此版本号的显示是游戏中不可缺少的细节,特别是集合类游戏。

1. 熟悉manifest

这里我们使用 Cocos Creator 提供的 AssetsManager 热更新框架所要求的 project.manifest 它是一个JSON格式的配置文件:

  1. {

  2.    "version": "0.0.1",

  3.    "packageUrl": "http://192.168.1.100/update",

  4.    "remoteManifestUrl": "http://192.168.1.100/update/hall-project.manifest",

  5.    "remoteVersionUrl": "http://192.168.1.100/update/hall-version.manifest",

  6.    "assets": {}

  7. }

上面是一个hall-project.manifest,再看一个game-project.manifest内容如下:

  1. {

  2.    "version": "0.1.1",

  3.    "packageUrl": "http://192.168.1.100/update",

  4.    "remoteManifestUrl": "http://192.168.1.100/update/game-project.manifest",

  5.    "remoteVersionUrl": "http://192.168.1.100/update/game-version.manifest",

  6.    "assets": {}

  7. }

获取版本号,其实就是读取 manifest 中的 version 字段,显示到一个 cc.Label 组件上。

这对大多数人来说都是小菜一碟,但是Shawn发现,利用组件方式实现一个相对通用的版本号组件做法却并不多见。

2. VersionLabel组件

了解过 manifest,我们就可以开始动手编写一个基于 Cocos Creator 引擎提供的 AssetsManager 热更新框架的版本号组件,这里将组件取名为“VersionLabel”,先看一下组件提供的属性接口:

对于组件的使者关心的是ModuleName属性,当你想显示不同游戏模块的版本时,只需要指定正确的ModuleName就可以了,下面是组件代码:

  1. cc.Class({

  2.    extends: cc.Component,

  3.    editor: CC_EDITOR && {

  4.        requireComponent: cc.Label, //强制依赖cc.Label组件

  5.    },


  6.    properties: {

  7.        default: '0.0.0',

  8.        moduleName: {

  9.            default: '',

  10.            notify(oldValue) {

  11.                if (CC_EDITOR || oldValue === this.moduleName) {

  12.                    return;

  13.                }

  14.                this._updateContent();

  15.            }

  16.        }

  17.    },


  18.    start () {

  19.        //获取Label组件

  20.        this.label = this.getComponent(cc.Label);

  21.        //更新版本内容

  22.        this._updateContent();

  23.    },


  24.    /**

  25.     * 更新内容

  26.     */

  27.    _updateContent() {

  28.        //加载“resources/manifest/xxx-project.manifest”

  29.        let url = `manifest/${this.moduleName}-project`;

  30.        this._getManifestContent(url, (content) => {

  31.            this._setVersion(content);

  32.        });

  33.    },


  34.    /**

  35.     *

  36.     * @param {String} url      resources以下路径

  37.     * @param {Function} cb     异步回调函数,返回manifest上下文

  38.     */

  39.    _getManifestContent(url, cb) {

  40.        cc.loader.loadRes(url, cc.Asset, null, (error, asset) => {

  41.            if (error) {

  42.                cb(null);

  43.                return;

  44.            }

  45.            //通过nativeUrl读取文件内容

  46.            let content = cc.loader.getRes(asset.nativeUrl);

  47.            cb(content);

  48.        });

  49.    },


  50.    /**

  51.     * 设置Label文本

  52.     * @param {String} content

  53.     */

  54.    _setVersion(content) {

  55.        let data;

  56.        try {

  57.            data = JSON.parse(content);

  58.            this.label.string = data.version;

  59.        } catch(e) {

  60.            cc.warn(e);

  61.            this.label.string = this.default;

  62.        }

  63.    }

  64. });

简单说明一下上面的代码:

  1. 该组件提供了一个moduleName的属性,这里注意不要使用name属性,因为是‘name’是Cocos Creator组件内置属性,还有定义manifest文件需要按照一定文件名命规范,我这里的名命模版是“xxx-project.manifest”

  2. 我们是将版本号文本显示到Label组件上,因此requireComponent: cc.Label 是定义该组件强制依赖cc.Label组件。使用它的好处是,当直接将该脚本拖动到场景或层级管理器时,会自动挂载一个cc.Label组件,增强组件的使用体验。

  3. manifest文件是放在resources目录下的,虽然manifest内部是json格式,但目前cc.loader还不能直接解析manifest这个扩展名的文件内容,当使用cc.loader.loadRes加载后,只能获取到文件的基本信息,通过使用cc.loader.getRes(asset.nativeUrl)获取文件内容。

3. 读取搜索路径下的manifest

上面的组件代码还存在一个Bug,我们是读取的安装包中的manifest文件,看下面代码:

  1. let url = `manifest/${this.moduleName}-project`;

此文件存在于 resources 目录,当运行在原生设备上它是存在于安装路径中,当游戏通过热更新下载了新的 manifest 文件,此路径是否还正确呢?当然就不对了,我们需要读取热更新包中的 manifest 文件才能获得正确的版本号,看下面代码:

  1. cc.Class({

  2.    extends: cc.Component,

  3.    ...


  4.    /**

  5.     * 更新内容

  6.     */

  7.    _updateContent() {

  8.        //如果为原生环境,尝试加载可写路径下的xxx-project.manifes文件

  9.        if (cc.sys.isNative) {

  10.            let remoteAssets = cc.path.join(jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/', 'remote-assets');

  11.            let url = cc.path.join(remoteAssets, this.moduleName, '-project.manifest');

  12.            //使用jsb函数读取文件内容

  13.            let content = jsb.fileUtils.getStringFromFile(url);

  14.            if (content) {

  15.                this._setVersion(content);

  16.                return;

  17.            }

  18.        }


  19.        let url = `manifest/${this.moduleName}-project`;

  20.        this._getManifestContent(url, (content) => {

  21.            this._setVersion(content);

  22.        });

  23.    },


  24.    ...

  25. });

上面代码在_updateContent函数中检查当前如果为原生环境,先尝试读取 可写路径/remote-assets/xxx-project.manifest文件,如果文件内容不存在才读取安装包resources目录下的manifest文件。

需要注意的是 remote-assets是使用AssetsManager下载热更新包时指定的,你需要根据自己的实际情况设置正确的路径。

源码地址:https://github.com/ShawnZhang2015/CreatorPrimer

4. 小结

读取版本号的逻辑上下文只需要关心到那里去获取版本字符串,使用专用的组件脚本可以将很多逻辑细节代码封装到一段小代码中独立执行,以达到与其它代码老死不相往来的效果,从而有效减少耦合。

同时借助 Cocos Creator 的可视化属性面板暴露接口,可以让非程序员也能轻松使用组件创作游戏内容,不知道大家获取版本号是如何实现的呢?欢迎分享你的方案,参与讨论!




欢迎关注「奎特尔星球」公众号,欢迎大家投稿,来我们一起成长!

「奎特尔星球」微信公众号

「奎特尔星球」博客网站,建设中...

HTTPS   详细X

基本翻译

abbr. 超文本传输协议安全(Hyper Text Transfer Protocol)

网络释义

HTTPS: HTTP Secure

HTTPS tracker: 支援

android https: 通信安全

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存