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

袁小威 2024-12-27 457人围观
简介做游戏,那就避免不了整个排行榜功能,因为对于一个游戏来说,排行榜绝对是必不可少的一个功能,它在一定程度上能给增加玩家的活跃度,从而大大的提高游戏自身的曝光量。

做游戏,那就避免不了整个排行榜功能,因为对于一个游戏来说,排行榜绝对是必不可少的一个功能,它在一定程度上能给增加玩家的活跃度,从而大大的提高游戏自身的曝光量。

那么今天,就让小威带大家一起走进怎样给微信小游戏加入排行榜功能”的茶话会吧~不是自夸,绝对干货,新手入门开发者的福音~不接受任何反驳,你要反驳那就算你赢~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/),然后进行项目构建。

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

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

很赞哦! (0)