做游戏,那就避免不了整个排行榜功能,因为对于一个游戏来说,排行榜绝对是必不可少的一个功能,它在一定程度上能给增加玩家的活跃度,从而大大的提高游戏自身的曝光量。
那么今天,就让小威带大家一起走进“怎样给微信小游戏加入排行榜功能”的茶话会吧。不是自夸,绝对干货,新手入门开发者的福音。不接受任何反驳,你要反驳那就算你赢!hahaha~
其他的前置内容就不用我多说了嘛,自行解决,今天重点只说排行榜,为了用户隐私和数据安全,微信的好友数据只能在开放数据域中使用,那么这个时候就会有客官会问:神马是开放数据域?很好,给你一个小心心。
对于微信开放数据域,Cocos官方有相关文档进行解释,如图:
对于如何整合,Cocos官方也有详细说明,如图:
好了,直接进入主题吧,首先我们需要在主项目(以下称主域)中增加显示排行榜的触发按钮,以及搭建要显示排行榜(以下称子域)的窗口UI结构(这里我们尽可能的将除了数据部分之外的功能都放在主域中展示,不要问为什么,因为问了我也不会说,哈哈哈!还有就是今天重点只说好友榜,其他的暂时无视~)
重点来了,有注意到topContent这个节点了波(这个名字根据你喜欢随意),这个节点上有个SubContextView组件,要有这个属性,这个节点才会成为子域的容器窗口,子域的内容也才会显示在主域上,所以这个节点的大小要跟子域大小保持一致,不然嘛,你懂得~
接下来就是今天的重头戏咯,竖起你的小耳朵仔细听,当我们在主域中点击排行榜按钮的时候,我们除了要显示主域中显示排行榜的窗口外,还需要通过wx.postMessage向子域发送指令信息,以此来获取排行榜数据或者设置玩家成绩(因为获取开放数据域数据的api只能在子域中调用,所以主域只能发送相关指令,子域根据对应指令执行相关api)。
cc.Class({
extends: cc.Component,
properties: {
FriendcontentBg:cc.Node,
FriendBtn:cc.Node,
},
//关闭排行榜
hide() {
this.node.active = false;
wx.postMessage({ type: 'close_rank' });
},
//上一页
prePage(userInfo) {
if (window.wx) {
if(this.FriendcontentBg.active){
wx.postMessage({ type: 'pre_page', userinfo: { openid: userInfo.openid, avatar: userInfo.avatar, nickName: userInfo.nickname, score: userInfo.MaxScor } });
}
}
},
//下一页
nextPage(userInfo) {
if (window.wx) {
if(this.FriendcontentBg.active){
wx.postMessage({ type: 'next_page', userinfo: { openid: userInfo.openid, avatar: userInfo.avatar, nickName: userInfo.nickname, score: userInfo.MaxScor } });
}
}
},
//发送数据给私域
postMsg(top_name,userinfo) {
window.sharedCanvas.width = 658;
window.sharedCanvas.height = 810;
window.wx.postMessage({
type: top_name,
name: "",
size: { width: 658, height: 810 },
userinfo: {
openid: userinfo.openid,
avatar: userinfo.avatar,
nickName: userinfo.nickname,
score: userinfo.MaxScore
}
});
},
//展示排行榜
show() {
this.node.active = true;
this.scheduleOnce(() => {
this.postMsg("show_rank");
}, 1);
this.FriendcontentBg.active = true
this.FriendBtn.active = true
},
});
下面我们新建一个子域项目,需要注意的是子域中的Canvas节点组件的大小要设置成与主域的容器节点大小一致,否则运行效果会与预期有所出入。
然后将子域的Main Camera节点中Camera组件的Background Color属性的不透明度设为0,不然可能会出现子域内容是黑屏~
接着,我们就制作排行榜的布局,并将写好的列表制作成预制体rankitem,这里跟普通页面布局一样,就不一一赘述了。
编写rankitem脚本,并添加到预制体rankitem上,主要是设置头像、昵称、分数、地址等信息。
const { get } = require('http');
var util = require('util');
cc.Class({
extends: cc.Component,
properties: {
secondSpr: {
default: null,
type: cc.SpriteFrame
},
thirdSpr: {
default: null,
type: cc.SpriteFrame
},
_number: 0,
_avatar: '',
_nickName: '',
_score: 0,
_city:'',
number: {
get() {
return this._number;
},
set(value) {
var spr = this.node.getChildByName('art');
var order = this.node.getChildByName('num');
if (value > 3 || value=="无") {
spr.active = false;
order.active = true;
var label = order.getComponent(cc.Label);
label.string = value;
} else {
spr.active = true;
order.active = false;
var spr1 = spr.getComponent(cc.Sprite);
if (value == 2) {
spr1.spriteFrame = this.secondSpr;
} else if (value == 3) {
spr1.spriteFrame = this.thirdSpr;
}
}
this._number = value;
}
},
avatar: {
get() {
return this._avatar;
},
set(value) {
util.loadImg(value, (sf) => {
var avatar = this.node.getChildByName('avatar').getChildByName("sub").getComponent(cc.Sprite);
avatar.spriteFrame = sf;
});
this._avatar = value;
}
},
nickName: {
get() {
return this._nickName;
},
set(value) {
var nick = this.node.getChildByName('nick');
if (nick) {
var label = nick.getComponent(cc.Label);
label.string = value;
}
this._nickName = value;
}
},
city: {
get() {
return this._city;
},
set(value) {
var city = this.node.getChildByName('city');
var city_icon = this.node.getChildByName('city').getChildByName("address")
if(value){
city_icon.active = true;
if (city) {
var label = city.getComponent(cc.Label);
label.string = value;
}
this._city = value;
}else{
city_icon.active = false;
city.active = false;
}
}
},
score: {
get() {
return this._score;
},
set(value) {
var label = this.node.getChildByName('score').getComponent(cc.Label);
label.string = value;
this._score = value;
}
}
},
});
编写rank脚本,主要是获取成绩和展示成绩列表的功能,在子域中,我们需要使用wx.onMessage来监听主域发送过来的指令。
var rankItem = require('rankitem');
var util = require('util');
var etype = {
rankwrap: 1,
}
cc.Class({
extends: cc.Component,
properties: {
subElement: {
default: [],
type: cc.Node
},
rankWrap: {
default: null,
type: cc.Node
},
rankItem: {
default: null,
type: cc.Prefab
},
Mycontent: {
default: null,
type: cc.Node
},
prebt: {
default: null,
type: cc.Node
},
nextbt: {
default: null,
type: cc.Node
},
loading: {
default: null,
type: cc.Node
},
},
//获取数据存储的key
getFlag() {
return 'game_top_list'
},
//关闭排行榜
closRank() {
this.sumpage = 1;
this.curpage = 1;
this.rankWrap.removeAllChildren();
this.Mycontent.removeAllChildren();
},
//处理排行榜数据并渲染排行榜信息
updateRank(data, offset, userinfo,page) {
this.rankWrap.removeAllChildren();
if (offset == 0) {
}
let openId = userinfo.openid;
let MyInfo = this.MyInfoss
if(page != "page"){
MyInfo = { number: "未上榜", avatar: userinfo.avatar, nickName: userinfo.nickName, score: userinfo.score + "分", city: "" };
}
data.forEach((v, i) => {
var obj = v.KVDataList.find(v1 => v1.key == this.getFlag()) || { value: 0 };
if(obj.value ==""){
obj.value = 0;
}
var param = { number: offset + i + 1, avatar: v.avatarUrl, nickName: v.nickname, score: obj.value + "分", city: "" }
//获取自己所在排名
if (page != "page" && v.openid == openId) {
MyInfo.number = offset + i + 1;
MyInfo.avatar = v.avatarUrl;
MyInfo.nickName = v.nickname;
MyInfo.score = obj.value + "分";
MyInfo.city = "";
this.MyInfoss = MyInfo
}
//渲染排行榜数据信息
var item = cc.instantiate(this.rankItem);
item.parent = this.rankWrap;
var curitem = item.getComponent('rankitem');
for (var k in param) {
curitem[k] = param[k];
}
});
//渲染自己所在榜信息
this.Mycontent.removeAllChildren();
var items = cc.instantiate(this.rankItem);
items.parent = this.Mycontent;
var curitems = items.getComponent("rankitem");
for (var ks in MyInfo) {
curitems[ks] = MyInfo[ks];
}
//上一页 下一页按钮
this.prebt.active = this.curpage == 1 ? false : true;
this.nextbt.active = this.curpage == this.sumpage ? false : true;
//关闭加载动画
this.hideLoading();
},
//上一页
prePage(userinfo) {
this.getRankData((data, offset) => this.updateRank(data, offset, userinfo,"page"), null, this.curpage);
},
//下一页
nextPage(userinfo) {
this.curpage = Math.min(this.curpage + 1, this.sumpage);
this.getRankData((data, offset) => this.updateRank(data, offset, userinfo,"page"), null, this.curpage);
},
//获取排行榜数据
getRankData(cb, score, page) {
//获取排行榜数据
if (page && page > 1 && this.rankData) {
var offset = (page - 1) * 6;
cb(this.rankData.slice(offset, offset + 6), offset);
return;
}
if (window.wx) {
window.wx.getFriendCloudStorage({
type: "group",
keyList: [this.getFlag()],
extra: {
sortKey: this.getFlag(), // 指定的key需
groupId: "level_group"
},
success: res => {
var data = res.data;
data.forEach(v2 => {
v2.KVDataList = v2.KVDataList.map(v1 => {
if (v1.key == this.getFlag()) {
var vobj = JSON.parse(v1.value);
if (typeof (vobj) != "number") {
v1.value = vobj.ttgame.score;
v1.update_time = vobj.ttgame.update_time;
}
}
return v1;
})
});
if (score) {
var selfitem = data.find(v1 => v1.avatarUrl == this.selfAvatar);
if (selfitem) {
var selfObj = selfitem.KVDataList.find(v1 => v1.key == this.getFlag());
selfObj.value = score;
} else {
data.push({ nickname: this.selfNick, avatarUrl: this.selfAvatar, KVDataList: [{ key: this.getFlag(), value: score }] });
}
}
data.sort((a, b) => {
if (a.KVDataList.length == 0 && b.KVDataList.length == 0) {
return 0;
}
if (a.KVDataList.length == 0) {
return 1;
}
if (b.KVDataList.length == 0) {
return -1;
}
var obj1 = b.KVDataList.find(v1 => v1.key == this.getFlag());
var obj2 = a.KVDataList.find(v1 => v1.key == this.getFlag());
return obj1.value - obj2.value;
});
//获取当前所属周的周一和周日时间戳
var times = util.getMondayAndSundayTimestamps();
var datas = [];
//过滤非本周成绩的数据
data.forEach(v1 => {
if (v1.KVDataList[0].key ==this.getFlag() && v1.KVDataList[0].update_time >= times.monday && v1.KVDataList[0].update_time <= times.sunday) {
datas.push(v1);
}
})
this.rankData = datas;
//分页数据
this.sumpage = Math.ceil(datas.length / 6);
if (page) {
var offset = (page - 1) * 6;
datas = datas.slice(offset, offset + 6);
}
cb(datas, offset);
},
});
}
},
//展示排行榜信息
showRank(size, name, userinfo) {
//显示加载动画
this.showLoading();
this.playerName = name;
this.subElement.forEach(v => v.active = false);
this.subElement[etype.rankwrap].active = true;
var sz = cc.director.getWinSize();
this.getRankData((data, offset) => this.updateRank(data, offset, userinfo,""), null, 1);
},
//显示加载动画
showLoading() {
this.loading.active = true;
},
//关闭加载动画
hideLoading() {
this.loading.active = false;
},
//
onLoad() {
this.sumpage = 1;
this.curpage = 1;
this.MyInfoss = [];
if (window.wx) {
//监听来自主域的指令
wx.onMessage(msg => {
if (msg.type == "show_rank") {
this.curpage = 1;
this.showRank(msg.size, msg.name, msg.userinfo);
}else if (msg.type == "pre_page") {
this.prePage(msg.userinfo,msg.top_type);
}else if (msg.type == "next_page") {
this.nextPage(msg.userinfo,msg.top_type);
}else if (msg.type == "close_rank") {
this.closRank();
}
})
}
},
});
这时候,可能有人会问,那怎么提交好友的成绩到微信开放域呢?真棒,这都被你发现了,奖励你大聪明一个~
设置成绩很简单,先封装提交分数的方法setScore:
setScore(key, value) {
if (window.wx) {
var recordCurTime = Date.parse(new Date());
const data = {
ttgame: {
score: value,
update_time: Math.floor((new Date()).getTime() / 1000),
},
};
if (window.wx) {
wx.setUserCloudStorage({
KVDataList: [
{ key: key, value: JSON.stringify(data) },
],
});
}
}
}
然后再游戏结束的时候调用这个方法即可:
this.setScore("game_top_list", 8888888);
终于到了我们的最后一步咯,你猜的没错,就是打包~首先是在主域项目中打开构建发布面板,设置好相关参数,并将开放数据域代码目录设置为你的子域项目名称,然后进行构建操作。
接着就是子域项目进行构建发布,将发布平台设置为微信小游戏开放数据域,发布路径设置为主域项目打包的根目录(一般是你的主项目目录下build/wechatgame/),然后进行项目构建。
全部构建完成之后,我们打开微信开发者工具来运行我们的主项目,这个时候点击排行榜就能看到排行列表啦~很激动有木有(如果你看不到数据的话,那就说明还没有上传过分数)
好咯,以上就是袁小威此次所有的分享了,小弟不才,各位客官老爷多多包涵,奏事酱紫,我们下次见~