- 参考Cocos接入微信小游戏官方文档,为了保护其社交关系链数据,微信小游戏增加了
开放数据域
的概念。只有在开放数据域中才能访问微信提供的wx.getFriendCloudStorage()
和wx.getGroupCloudStorage()
两个 API来实现排行榜功能。
- 查看微信开放接口API的官方文档,了解相关用法。
- 参考《Cocos Creator游戏实战》实现微信小游戏排行榜 进行排行榜功能开发。
一、主域设置
- 在
box节点
下创建rankBox节点
,包含rankBg
、titleBox
、mainBox
、buttonBox
。并设置其不可见。
rankBg:颜色为#FFFFFF78
半透明白色,考虑到屏幕适配的情况,添加Widget组件
,分别为-30% -27% -35% -35%
;
titleBox:包含titleBg
、titleLabel
,即排行榜文字标签;
mainBox:包含mainBg
、openData
,openData为开放数据域;
buttonBox:包含returnButton
、shareButton
。
- 在
openData节点
下添加其他组件
-> WXSubContextView组件
。
- 对
game.js
进行相关函数的增加。首先在属性中添加rankBox
、rankButton
。并与Canvas节点
绑定。
properties: {
...
rankBox: cc.Node,
rankButton: cc.Button,
},
- 在
onLoad()方法
中,添加对rankButton的click
响应事件,调用showRanks方法
。并添加initUserInfoButton()方法。
onLoad() {
...
this.initUserInfoButton();
this.rankButton.node.on('click', this.showRanks, this);
},
showRanks()方法
,将rankBox
显示可见,并调用wx.getOpenDataContext().postMessage()方法
发送score(这里先取了100以内随机数)
,与子域进行通信生成排行榜内容。
showRanks() {
if (typeof wx === 'undefined') {
return;
}
this.rankBox.active = true;
let score = Math.round(Math.random() * 100);
wx.getOpenDataContext().postMessage({
message: score
});
},
initUserInfoButton()方法
为官方文档中微信小游戏用户授权相关。
initUserInfoButton() {
if (typeof wx === 'undefined') {
return;
}
let systemInfo = wx.getSystemInfoSync();
let width = systemInfo.windowWidth;
let height = systemInfo.windowHeight;
let button = wx.createUserInfoButton({
type: 'text',
text: '',
style: {
left: 0,
top: 0,
width: width,
height: height,
lineHeight: 40,
backgroundColor: '#00000000',
color: '#00000000',
textAlign: 'center',
fontSize: 10,
borderRadius: 4
}
});
button.onTap((res) => {
if (res.userInfo) {
console.log('authorized success!');
}
else {
console.log('authorized fail!');
}
button.hide();
button.destroy();
});
},
- 构建发布,需要在
开放数据域代码目录
中输入子域项目名称,笔者设置为openData
,点击构建。
二、子域设置
- 新建一个
openData项目
,将Canvas的宽高设置为主域openData节点
的宽高。
- 在资源管理器中assets下新建
prefabs
、scenes
、scripts
文件夹,在scenes下新建openData场景
。
- 并在
Canvas节点
下新建UI节点
-> ScrollView
节点命名为rankScrollView
,添加Widget组件
与父节点对齐,设置Color属性为#CDC1B4
。(Canvas下新建bg节点是为了防止适配屏幕时出现黑边,但预览时发现不需要)。
- 删除
scrollBar节点
,并对view节点
添加Widget组件
与父节点对齐。
- 对
content节点
添加Widget组件
与父节点对齐,添加Layout组件
设置Type为VERTICAL
、ResizeMode为CONTAINER
。
item节点
添加Widget组件
与父节点对齐,设置高度为150
,并在item节点下添加bg
、rank
、avatar
、name
、score
、line
。
bg:item的背景颜色,Color属性为#FAF8EF
;
rank:item中显示的用户排名;
avatar:item中显示的用户头像;
name:item中显示的用户昵称;
score:item中显示的用户最高分;
line:高度3
的Sprite(单色)节点
,呈线状,用于分隔每个item。
- 将设置好的
item
拖到prefabs文件夹下作为预制资源
,对层级管理器中item节点取消可见。
- 在
scrpit目录
下创建openData.js
,与Canvas节点绑定。添加属性itemPrefab
、content
,与Canvas节点绑定。
properties: {
itemPrefab: cc.Prefab,
content: cc.Node,
},
onLoad()
中调用wx.onMessage()方法
,如果从主域中传入的message不是clear字串,则与云托管上的分数比较,初始化排行榜列表rankList
,传入玩家数据,getPlayerInfo()
用于测试;getFriendInfo()方法
用于获取好友的数据,设置了0.25s
的时延,因为在实际预览过程中出现compareScore()方法
没结束就调用getFriendInfo()
出错的情况。
onLoad() {
if (typeof wx === 'undefined') {
return;
}
wx.onMessage(data => {
if (data.message != undefined) {
if (data.message != 'clear') {
this.score = data.message;
this.compareScore();
this.rankList= [];
this.getPlayerInfo();
}
else {
this.content.removeAllChildren();
}
}
});
},
compareOldScore()方法
调用wx.getUserCloudStorage()方法
。首先判断有没有云托管分数,如果没有则直接创建一个newKVData
变量,并调用setNewCloudScore()方法
将传过来的分数设置到云托管;如果有云托管分数,则比较新分数和云托管分数,若新分数大则将新分数存入云托管。
compareScore() {
wx.getUserCloudStorage({
keyList: ['score'],
success: (res) => {
if (res.KVDataList.length > 0) {
let KVData = res.KVDataList[0];
let storedScore = Number(KVData['value']);
if (this.score > storedScore) {
let newKVData = { key: 'score', value: String(this.score) }
this.setNewCloudScore(newKVData);
}
}
else {
let newKVData = { key: 'score', value: String(this.score) }
this.setNewCloudScore(newKVData);
}
},
fail: (res) => {
console.log(res);
}
});
},
setNewCloudScore(newKVData)
调用wx.setUserCloudStorage()方法
上传云托管分数。
setNewCloudScore(newKVData) {
wx.setUserCloudStorage({
KVDataList: [newKVData],
success: (res) => {
console.log('update score success!');
},
fail: (res) => {
console.log(res);
}
});
},
getPlayerInfo()
调用wx.getUserInfo()方法
用于测试,用自己的信息模拟50名不同分数的玩家。
getPlayerInfo() {
wx.getUserInfo({
openIdList: ['selfOpenId'],
lang: 'zh_CN',
success: (res) => {
let userInfo = res.data[0];
for (let i = 0; i < 50; i++) {
this.rankList.push({
nickName: userInfo.nickName,
avatarUrl: userInfo.avatarUrl,
score: Math.round(Math.random() * 100)
});
}
this.makeRanks();
},
fail: (res) => {
console.log(res);
}
});
},
getFriendInfo()
调用wx.getFriendCloudStorage()方法
,往rankList
中传入数据。
getFriendInfo() {
wx.getFriendCloudStorage({
keyList: ['score'],
success: (res) => {
for (let i = 0; i < res.data.length; i++) {
this.rankList.push({
nickName: res.data[i].nickname,
avatarUrl: res.data[i].avatarUrl,
score: res.data[i].KVDataList[0]['value'],
});
}
this.makeRanks();
},
fail: (res) => {
console.log(res);
}
});
},
makeRanks()方法
用于在rankList
进行正确排序,排序完调用createItem()方法
生成item。
makeRanks() {
this.rankList.sort((a, b) => {
return b['score'] - a['score'];
});
for (let i = 0; i < this.rankList.length; i++) {
let nickName = this.rankList[i]['nickName'];
let avatarUrl = this.rankList[i]['avatarUrl'];
let score = this.rankList[i]['score'];
this.createItem(i + 1, nickName, avatarUrl, score);
}
},
createItem()方法
用于生成item实体包括rank
、avatarUrl
、nickName
、score
。
createItem(rank, nickName, avatarUrl, score) {
let item = cc.instantiate(this.itemPrefab);
item.children[1].getComponent(cc.Label).string = rank <= 9 ? ' ' + rank : rank;
cc.loader.load({ url: avatarUrl, type: 'png' }, (err, texture) => {
if (err) console.error(err);
item.children[2].getComponent(cc.Sprite).spriteFrame = new cc.SpriteFrame(texture);
});
item.children[3].getComponent(cc.Label).string = '昵称:' + nickName;
item.children[4].getComponent(cc.Label).string = '最高分:' + score;
this.content.addChild(item);
},
- 构建发布,可设置发布路径为主目录
./build/wechatgame目录
下,点击构建。
- 在
微信开发者工具
中预览,可以看到,点击排行榜按钮,进行微信授权,之后正常显示排行榜。(但此时没有取消监听,可以看到控制台打印了move up
语句)。
三、其他相关设置
- 监听设置。在
showRanks()
添加对removeEventHandler()
的调用,并禁用重新开始按钮
和排行榜按钮
。
showRanks() {
this.removeEventHandler();
this.restartButton.onDisable();
this.rankButton.onDisable();
...
},
- 返回按钮。在属性中添加
returnButton
与Canvas
绑定。在onLoad()
监听按钮点击触发returnGame()
。returnGame()添加屏幕滑动监听,启用重新开始按钮
和排行榜按钮
,并将rankBox
显示不可见,并再次与子域通信发送clear字串
,请求删除之前生成的排行榜信息(如果不删除,下次打开排行榜时会有重复内容出现)。
properties: {
...
returnButton: cc.Button,
},
onLoad() {
...
this.returnButton.node.on('click', this.returnGame, this);
},
...
returnGame() {
this.addEventHandler();
this.restartButton.onEnable();
this.rankButton.onEnable();
this.rankBox.active = false;
if (typeof wx === 'undefined') {
return;
}
wx.getOpenDataContext().postMessage({
message: 'clear'
});
},
- 历史最高分更新
updateBest(number)方法
。可参考updateScore()方法
,同时在init()时读取本地最高分,在游戏过程中只有当score > best
时才更新最高分。
init() {
...
this.updateBest(cc.sys.localStorage.getItem('best'));
...
},
afterMove(hasMoved) {
if (hasMoved) {
this.updateScore(this.score);
this.updateBest(this.score);
...
}
...
},
...
updateBest(number) {
this.best = cc.sys.localStorage.getItem('best');
if (!this.best || number >= this.best) {
cc.sys.localStorage.setItem('best', number);
this.best = number;
this.bestLabel.string = number;
}
},
- 在
微信开发者工具
中预览,设置SUCCESS为32
,可以看到:
1)第一阶段:分数、历史最佳、排行榜上显示的最高分均为0;
2)第二阶段:第一次游戏成功,分数、历史最佳、排行榜上显示的最高分均为120,排行榜成功更新;
3)第三阶段:点击再玩一次按钮后,分数清零,历史最佳仍为120;
4)第四阶段:第二次游戏过程中,分数变化,历史最佳在分数大于120之后变化,排行榜上成功更新为220。