实现微信小游戏排行榜
在游戏中加入排行榜是非常有必要的。本节教程就带大家了解下如何在微信小游戏中加入排行榜。
运行效果如下:
Cocos Creator版本:2.2.0
后台回复"排行榜",获取该项目完整文件:
其实Cocos官方在文档中已经提供了较为详细的说明,并给出了一个Demo,大家可以先去了解下。
请大家点击该链接查看微信开放数据域各个API的用法。
完成主域项目
下图显示了笔者在主域项目中创建的节点:
1. bg节点用作背景,颜色为白色。
2. showRanksBtn是一个按钮节点,用于显示和关闭排行榜。
3. WXSubContextView节点非常关键,我们必须通过它来显示开放数据域(以下统称子域)中的贴图。它上面挂有WXSubContextView组件。
注:对各个节点的尺寸和属性设置,请大家直接打开项目去查看下,笔者这里不再赘述。
进行适当布局后,显示如下:
新建一个ShowRanks.js脚本,并挂到Canvas节点上。脚本编写如下:
// ShowRanks.js
cc.Class({
extends: cc.Component,
properties: {
wxSubContextView: cc.Node //主域视窗容器
},
// LIFE-CYCLE CALLBACKS:
onLoad () {
// 获取授权
this.initUserInfoButton();
},
showRanks() {
if (typeof wx === 'undefined') {
return;
}
if (!this.wxSubContextView.active) {
// 设置容器可见
this.wxSubContextView.active = true;
// 设置随机数(把这个当做玩家的分数)
let score = Math.round(Math.random()*10);
// 发送结算分数到开放域
wx.getOpenDataContext().postMessage({
message: score
});
}
else {
// 设置容器不可见,即关闭排行榜,并让开放域清空排名信息
this.wxSubContextView.active = false;
wx.getOpenDataContext().postMessage({
message: 'clear'
});
}
},
initUserInfoButton () {
// 微信授权,此代码来自Cocos官方
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('授权成功!');
}
else {
console.log('授权失败!');
}
button.hide();
button.destroy();
});
},
});
1. initUserInfoButton这个方法用于用户授权,大家不必对该方法留意过多,照抄就行(调用微信开放数据域中的方法前需要用户授权)。
2. 重点是showRanks方法,每次玩家点击按钮后,都会调用该方法。
其中我们首先判断WXSubContextView节点是否可见,若不是,则显示可见并调用wx.getOpenDataContext().postMessage()发送当前玩家的分数,与子域进行通信请求生成排行榜内容。
若WXSubContextView可见,那么我们设置其active属性为false,并再次与子域通信,请求删除之前生成的排行榜信息。如果不删除,那么等玩家下次再点击按钮打开排行榜时,会发现有重复内容出现。
完成子域项目
子域节点内容如下:
1. 首选确保Canvas节点大小同主域中的WXSubContextView大小相同,记得将Fit Height和Fit Width都勾选上。
2. bg为背景,颜色为白色,跟主域背景颜色一样(这由项目素材决定,不必跟笔者这里一样)。
3. rankScrollView是一个滑动视窗节点(笔者删除了一些用不到的子节点),读者可以将该视窗的背景换成自己的素材。
注意笔者这里在content节点上加了一个Layout组件,并将Type和Resize Mode分别设置为VERTICAL和CONTAINER。因为content下会有很多的子节点生成,这样做可以更好地排版,content高度也可以自动调整。
效果如下:
现在我们需要一个预制,在该预制节点上会显示各个玩家的信息。
- rank子节点用于显示名次。
- avatar子节点用于显示微信头像。
- nameTip子节点显示固定文本Name:。
- scoreTip子节点显示固定文本Score:。
- name子节点显示玩家的微信名。
- score子节点显示玩家的分数。
设置好后的效果如下:
接下来新建OpenData.js脚本,在properties中添加以下两个属性:
// OpenData.js
properties: {
itemPrefab: cc.Prefab, // item预制
content: cc.Node, // content节点
},
onLoad方法实现如下:
// OpenData.js
onLoad () {
if (typeof wx === 'undefined') {
return;
}
wx.onMessage( data => {
if (data.message) {
if (data.message != 'clear') {
this.score = data.message; // 将主域传过来的分数保存在this.score中
this.compareOldNewScore(); // 将当前玩家的新分数和之前玩的分数进行比较
this.allInfoList = []; // 所有玩家的数据保存在这个数组中,用于排序
this.getFriendInfo(); // 获取同城好友信息(当前玩家的信息也会返回)
// this.getPlayerInfo(); // 用于读者Demo测试,实际项目中调用getFriendInfo()
}
else {
this.content.removeAllChildren(); // 关闭排行榜时清空节点
}
}
});
},
在子域中我们调用wx.onMessage方法获取从主域传过来的数据。如果主域传来的是'clear',那么我们就清空content节点下的所有子节点;如果传来的是分数,那么我们首先调用this.compareOldNewScore方法来比较该分数与之前的云托管分数。接着调用getFriendInfo()来获取所有玩家的信息(在该方法中会进行排名)。
由于不是实际上线项目(没有其他好友云托管的数据),所以读者在微信开发工具上运行该项目后,会发现生成的排名只有读者一人。那为了缓解这种尴尬,笔者放了getPlayerInfo方法,读者可以使用该方法来生成多个玩家信息(其实就是多个自己啦)。当然实际项目中要用getFriendInfo()。
this.compareOldNewScore方法实现如下:
// OpenData.js
compareOldNewScore() {
// 将传过来的新分数和之前玩的分数进行比较
wx.getUserCloudStorage({
keyList: ['score'],
success: (res) => {
if (res.KVDataList.length) {
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);
}
});
},
在该方法中我们调用 wx.getUserCloudStorage()来获取自己的云托管分数(之前玩过保存到云托管上的分数)。如果调用成功,则首选判断有没有云托管分数(因为这可能是第一次玩),如果没有那么我们直接创建一个newKVData变量,并调用setNewCloudScore方法将传过来的分数设置到云托管。如果之前玩过,那么我们来比较下新分数和云托管上的分数,看看哪个比较高。若新分数较大,则同理将新分数存入云托管。
setNewCloudScore方法实现如下:
setNewCloudScore (newKVData) {
// 设置新云托管分数(第一次游戏时,也调用该方法设置云托管分数)
wx.setUserCloudStorage({
KVDataList: [newKVData],
success: (res) => {
console.log('更新玩家分数成功!');
},
fail: (res) => {
console.log(res);
}
});
},
大家可以去微信小游戏API文档上看下具体解释。
更新:请读者在主域中比较分数,并调用wx.setUserCloudStorage这个API来更新云托管分数(该API可以在主域中使用),否则玩家只有在点击了排行榜按钮后才会将分数存入云托管。
逻辑:将每局分数与存储在本地的最高分进行比较,如果超过历史最高分数则同时更新本地和云托管数据。不需要再将分数传到子域。
getPlayerInfo方法笔者略过,这里重点讲下getFriendInfo方法:
// OpenData.js
getFriendInfo () {
// 获取同城好友信息(包括自身)
wx.getFriendCloudStorage({
keyList: ['score'],
success: (res) => {
for (let i = 0; i < res.data.length; i++) {
// 获取玩家微信名,头像url和分数
let nickName = res.data[i].nickname;
let avatarUrl = res.data[i].avatarUrl;
let score = 0;
if (res.data[i].KVDataList.length)
score = res.data[i].KVDataList[0]['value'];
// 加入到数组中
this.allInfoList.push({
nickName: nickName,
avatarUrl: avatarUrl,
score: score
});
}
// 开始排名
this.makeRanks();
},
fail: (res) => {
console.log(res);
}
});
},
在该方法中笔者调用wx.getFriendCloudStorage()获取同城好友的云托管数据(只要好友玩过你的游戏,那他们的信息就可以从云托管中读取到)。注意wx.getFriendCloudStorage()也会返回自身的数据。
如果调用成功,我们就循环读取玩家的信息,并将数据添加到allInfoList数组中。获取完毕之后,就可以调用makeRanks方法进行排名了。
makeRanks方法编写如下:
// OpenData.js
makeRanks () {
// 首先将allInfoList内部元素进行排序,根据分数来降序排列
this.allInfoList.sort((a, b) => {
return b['score'] - a['score'];
});
// 根据各个玩家的分数制作排名
for (let i=0; i<this.allInfoList.length; i++) {
let nickName = this.allInfoList[i]['nickName'];
let avatarUrl = this.allInfoList[i]['avatarUrl'];
let score = this.allInfoList[i]['score'];
this.createItem(i+1, nickName, avatarUrl, score);
}
},
第一步必须先对allInfoList内的元素按照分数进行排序,分数高排在最前。
接着循环数组,调用createItem方法生成预制:
// OpenData.js
createItem (rank, nickName, avatarUrl, score) {
// 生成item
let item = cc.instantiate(this.itemPrefab);
// 排名
item.children[0].getComponent(cc.Label).string = String(rank);
// 微信名
item.children[4].getComponent(cc.Label).string = nickName;
// 分数
item.children[5].getComponent(cc.Label).string = score;
// 头像
cc.loader.load({url: avatarUrl, type: 'png'}, (err, texture) => {
if (err) console.error(err);
item.children[1].getComponent(cc.Sprite).spriteFrame = new cc.SpriteFrame(texture);
});
// 添加到content中
this.content.addChild(item);
}
构建运行
对主域项目和子域项目分别进行构建(哪个先构建没关系)。
主域:
- 发布平台选择微信小游戏。
- appid中填写自己小游戏的appid。
- 开放数据域代码目录填写子域项目文件夹名称。
- 填好之后点击构建。
子域:
- 发布平台选择微信小游戏开放数据域。
- 选好之后点击构建。
接着我们将子域项目下build文件夹中构建好的OpenDataDomain文件夹拷贝到主域项目下build文件夹下的wechatgame文件夹中:
P.S.其实我们在构建子域项目前修改下发布路径到主域的wechatgame文件夹中也可以。
如果微信开发者工具在引擎中配置好了的话,那我们打开主域项目的构建面板,点击运行就行了。
好,本节教程就到这,希望大家有所收获!