新手小白福音:CocosCreator微信小游戏排行榜保姆级教程,它来了~

做游戏,那就避免不了整个排行榜功能,因为对于一个游戏来说,排行榜绝对是必不可少的一个功能,它在一定程度上能给增加玩家的活跃度,从而大大的提高游戏自身的曝光量。
那么今天,就让小威带大家一起走进“怎样给微信小游戏加入排行榜功能”的茶话会吧。不是自夸,绝对干货,新手入门开发者的福音。不接受任何反驳,你要反驳那就算你赢!hahaha~
其他的前置内容就不用我多说了嘛,自行解决,今天重点只说排行榜,为了用户隐私和数据安全,微信的好友数据只能在开放数据域中使用,那么这个时候就会有客官会问:神马是开放数据域?很好,给你一个小心心。
对于微信开放数据域,Cocos官方有相关文档进行解释,如图:

CocosCreator微信小游戏排行榜保姆级教程

对于如何整合,Cocos官方也有详细说明,如图:
CocosCreator微信小游戏排行榜保姆级教程

好了,直接进入主题吧,首先我们需要在主项目(以下称主域)中增加显示排行榜的触发按钮,以及搭建要显示排行榜(以下称子域)的窗口UI结构(这里我们尽可能的将除了数据部分之外的功能都放在主域中展示,不要问为什么,因为问了我也不会说,哈哈哈!还有就是今天重点只说好友榜,其他的暂时无视~)
CocosCreator微信小游戏排行榜保姆级教程

重点来了,有注意到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节点组件的大小要设置成与主域的容器节点大小一致,否则运行效果会与预期有所出入。


CocosCreator微信小游戏排行榜保姆级教程

然后将子域的Main Camera节点中Camera组件的Background Color属性的不透明度设为0,不然可能会出现子域内容是黑屏~


CocosCreator微信小游戏排行榜保姆级教程

接着,我们就制作排行榜的布局,并将写好的列表制作成预制体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);

终于到了我们的最后一步咯,你猜的没错,就是打包~首先是在主域项目中打开构建发布面板,设置好相关参数,并将开放数据域代码目录设置为你的子域项目名称,然后进行构建操作。


CocosCreator微信小游戏排行榜保姆级教程

接着就是子域项目进行构建发布,将发布平台设置为微信小游戏开放数据域,发布路径设置为主域项目打包的根目录(一般是你的主项目目录下build/wechatgame/),然后进行项目构建。


CocosCreator微信小游戏排行榜保姆级教程

全部构建完成之后,我们打开微信开发者工具来运行我们的主项目,这个时候点击排行榜就能看到排行列表啦~很激动有木有(如果你看不到数据的话,那就说明还没有上传过分数)
CocosCreator微信小游戏排行榜保姆级教程

好咯,以上就是袁小威此次所有的分享了,小弟不才,各位客官老爷多多包涵,奏事酱紫,我们下次见~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容