在集合類遊戲中,不論是大廳還是子游戲都會涉及到版本的更新,在開發調試階段,檢查更新是否生效的一個直觀的方法就是觀察版本號的變化,因此版本號的顯示是遊戲中不可缺少的細節,特別是集合類遊戲。
1. 熟悉manifest
這裏我們使用 Cocos Creator 提供的 AssetsManager 熱更新框架所要求的 project.manifest 它是一個JSON格式的配置文件:
{
"version": "0.0.1",
"packageUrl": "http://192.168.1.100/update",
"remoteManifestUrl": "http://192.168.1.100/update/hall-project.manifest",
"remoteVersionUrl": "http://192.168.1.100/update/hall-version.manifest",
"assets": {}
}
上面是一個hall-project.manifest,再看一個game-project.manifest內容如下:
{
"version": "0.1.1",
"packageUrl": "http://192.168.1.100/update",
"remoteManifestUrl": "http://192.168.1.100/update/game-project.manifest",
"remoteVersionUrl": "http://192.168.1.100/update/game-version.manifest",
"assets": {}
}
獲取版本號,其實就是讀取 manifest 中的 version 字段,顯示到一個 cc.Label 組件上。這對大多數人來說都是小菜一碟,但是Shawn發現,利用組件方式實現一個相對通用的版本號組件做法卻並不多見。
2. VersionLabel組件
瞭解過 manifest,我們就可以開始動手編寫一個基於 Cocos Creator 引擎提供的 AssetsManager 熱更新框架的版本號組件,這裏將組件取名爲“VersionLabel”,先看一下組件提供的屬性接口:
對於組件的使者關心的是ModuleName屬性,當你想顯示不同遊戲模塊的版本時,只需要指定正確的ModuleName就可以了,下面是組件代碼:
cc.Class({
extends: cc.Component,
editor: CC_EDITOR && {
requireComponent: cc.Label, //強制依賴cc.Label組件
},
properties: {
default: '0.0.0',
moduleName: {
default: '',
notify(oldValue) {
if (CC_EDITOR || oldValue === this.moduleName) {
return;
}
this._updateContent();
}
}
},
start () {
//獲取Label組件
this.label = this.getComponent(cc.Label);
//更新版本內容
this._updateContent();
},
/**
* 更新內容
*/
_updateContent() {
//加載“resources/manifest/xxx-project.manifest”
let url = `manifest/${this.moduleName}-project`;
this._getManifestContent(url, (content) => {
this._setVersion(content);
});
},
/**
*
* @param {String} url resources以下路徑
* @param {Function} cb 異步回調函數,返回manifest上下文
*/
_getManifestContent(url, cb) {
cc.loader.loadRes(url, cc.Asset, null, (error, asset) => {
if (error) {
cb(null);
return;
}
//通過nativeUrl讀取文件內容
let content = cc.loader.getRes(asset.nativeUrl);
cb(content);
});
},
/**
* 設置Label文本
* @param {String} content
*/
_setVersion(content) {
let data;
try {
data = JSON.parse(content);
this.label.string = data.version;
} catch(e) {
cc.warn(e);
this.label.string = this.default;
}
}
});
簡單說明一下上面的代碼:
- 該組件提供了一個moduleName的屬性,這裏注意不要使用name屬性,因爲是‘name’是Cocos Creator組件內置屬性,還有定義manifest文件需要按照一定文件名命規範,我這裏的名命模版是“xxx-project.manifest”
- 我們是將版本號文本顯示到Label組件上,因此requireComponent: cc.Label 是定義該組件強制依賴cc.Label組件。使用它的好處是,當直接將該腳本拖動到場景或層級管理器時,會自動掛載一個cc.Label組件,增強組件的使用體驗。
- manifest文件是放在resources目錄下的,雖然manifest內部是json格式,但目前cc.loader還不能直接解析manifest這個擴展名的文件內容,當使用cc.loader.loadRes加載後,只能獲取到文件的基本信息,通過使用cc.loader.getRes(asset.nativeUrl)獲取文件內容。
3. 讀取搜索路徑下的manifest
上面的組件代碼還存在一個Bug,我們是讀取的安裝包中的manifest文件,看下面代碼:
let url = `manifest/${this.moduleName}-project`;
此文件存在於 resources 目錄,當運行在原生設備上它是存在於安裝路徑中,當遊戲通過熱更新下載了新的 manifest 文件,此路徑是否還正確呢?當然就不對了,我們需要讀取熱更新包中的 manifest 文件才能獲得正確的版本號,看下面代碼:
cc.Class({
extends: cc.Component,
...
/**
* 更新內容
*/
_updateContent() {
//如果爲原生環境,嘗試加載可寫路徑下的xxx-project.manifes文件
if (cc.sys.isNative) {
let remoteAssets = cc.path.join(jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/', 'remote-assets');
let url = cc.path.join(remoteAssets, this.moduleName, '-project.manifest');
//使用jsb函數讀取文件內容
let content = jsb.fileUtils.getStringFromFile(url);
if (content) {
this._setVersion(content);
return;
}
}
let url = `manifest/${this.moduleName}-project`;
this._getManifestContent(url, (content) => {
this._setVersion(content);
});
},
...
});
上面代碼在_updateContent函數中檢查當前如果爲原生環境,先嚐試讀取可寫路徑/remote-assets/xxx-project.manifest
文件,如果文件內容不存在纔讀取安裝包resources目錄下的manifest文件。
需要注意的是remote-assets
是使用AssetsManager下載熱更新包時指定的,你需要根據自己的實際情況設置正確的路徑。
4. 小結
讀取版本號這個邏輯上下文只需要關心到那裏去獲取版本字符串,使用組件的方式,可以將很多邏輯細節代碼封裝到一段小代碼中獨立執行,以達到與其它代碼老死不相往來,從而有效減少耦合。
同時藉助Cocos Creator的可視化屬性面板暴露接口,可以讓非程序員也能輕鬆使用組件搭建出遊戲內容,不知道大家獲取版本號是如何實現的呢,也歡迎分享你的實現方案。