CocosCreator3D微信小游戏入门RunningBall(五):微信云开发实现排行榜

CocosCreator3D微信小游戏入门RunningBall(五)

以前的开发我们需要实现一个服务器才能完成我们的排行榜开发的工作。
但现在有了微信云开发,通过微信云开发中的云数据库以及云函数,我们不用开发服务器,即可实现我们的排行榜功能

微信官方文档参考:https://developers.weixin.qq.com/minigame/dev/wxcloud/basis/getting-started.html

一、开通微信云开发

首先你要准备一个微信的AppID,这个AppID不能是测试账号,必须是正式的,通过申请的AppID

在微信Web开发者工具中,进入项目后,点击左上方的云开发按钮
CocosCreator3D微信小游戏入门RunningBall(五):微信云开发实现排行榜_第1张图片

点击后我们可以看到这个界面,点击开通按钮就行
CocosCreator3D微信小游戏入门RunningBall(五):微信云开发实现排行榜_第2张图片

接下来需要我们填写微信云开发环境的环境名称,一般填项目名就好,环境ID不用去管它,自动生成就好
CocosCreator3D微信小游戏入门RunningBall(五):微信云开发实现排行榜_第3张图片

这样微信云开发就开通了。

二、使用云函数获取用户的openid

打开我们打包好的微信小游戏项目,在微信工具的右边选择并打开project.config.json文件,在开头添加参数:

"cloudfunctionRoot": "functions/"

CocosCreator3D微信小游戏入门RunningBall(五):微信云开发实现排行榜_第4张图片
这是指定我们云开发的云函数的本地代码位置

然后在右边的资源管理器中,右键创建目录,目录名为我们制定的文件名,比如上面我们制定的是functions,那么我们的这个文件夹就命名为functions

CocosCreator3D微信小游戏入门RunningBall(五):微信云开发实现排行榜_第5张图片

创建好目录后我们可以看到这个目录自动变成了这样子:
CocosCreator3D微信小游戏入门RunningBall(五):微信云开发实现排行榜_第6张图片

右键点击文件夹,选择创建Node.js云函数,命名为getOpenId
CocosCreator3D微信小游戏入门RunningBall(五):微信云开发实现排行榜_第7张图片

此时我们可以看到目录结果变为这样:
CocosCreator3D微信小游戏入门RunningBall(五):微信云开发实现排行榜_第8张图片
这个Index.js就是我们这个getOpenId云函数的入口

将默认代码稍微微调一下即可

// 云函数入口文件
const cloud = require('wx-server-sdk')

cloud.init()

// 云函数入口函数
exports.main = async (event, context) => {
  const wxContext = cloud.getWXContext()

  return {
    openid: wxContext.OPENID,
  }
}

通过cloud.getWXContext()方法可以获得调用这个云函数的上下文,返回的wxContext中就包含了OPENID,APPID,UNIONID这些用户信息,然后我们直接通过return,将OPENID返回给客户端即可,这样客户端就可以通过云函数快速的拿到用户自己的Openid。

这样我们就完成了这个云函数的编写

最后,要注意三点:

  • 编写完云函数后,在云函数的文件夹上右键选择“上传并部署:云端安装依赖”
  • 如果你的云函数使用了一些npm的第三方库,那就选择“上传并部署:所有文件”,这样会将依赖的第三方库也一起上传
  • 如果有修改过云函数,在打包编译前,请将build下的functions文件夹,project.config.json文件(这个可以不用)放到build-templates文件夹下(构建模版文件夹),这样避免打包的时候把functions文件夹清理掉,不方便更改云函数以及调试

三、使用云函数

在客户端使用云函数的时候,我们先创建一个名为Wechat的脚本,这个脚本我们对微信的这些API调用进行一些简单的封装,以便在我们的RunningBall项目中使用

首先这个Wechat脚本我们使用单例的模式

    private static _instance: Wechat = null;

    static instance () {
        if (!this._instance) {
            this._instance = new Wechat();
        }
        return this._instance;
    }

在脚本类上放入上述的代码,这样我们就可以通过Wechat.instance()就可以拿到这个单例

接下来我们在Wechat的构造方法中初始化云开发的环境

    constructor () {
        //初始化云开发环境
        wx.cloud.init({
            traceUser: true,
            env: 'runningball-wbdtu'
        });
    }

这里的env属性的值在这里可以找到:
CocosCreator3D微信小游戏入门RunningBall(五):微信云开发实现排行榜_第9张图片
CocosCreator3D微信小游戏入门RunningBall(五):微信云开发实现排行榜_第10张图片

接下来我们创建一个名为getUserOpenid的方法,这个方法拥有一个参数,这个参数是个回调函数,函数的参数是个openid

    getUserOpenid (cb: ((openid: string) => void)) {
        wx.cloud.callFunction({ 
            name: 'getOpenId',
            complete: (res) => {
                cb(res.result.openid);
            }
        });
    }

我们通过wx.cloud.callFunction就可以对我们的云函数进行调用,在complete参数的方法中获得云函数的运行结果。
如果有云函数需要我们上传一些参数进行调用的话,我们可以增加data字段
例如:

        wx.cloud.callFunction({ 
            name: 'getOpenId',
            data: {
            		name: ""
            }
            complete: (res) => {
                cb(res.result.openid);
            }
        });

在云函数中,我们传过来的参数通过event属性进行读写,例如

// 云函数入口文件
const cloud = require('wx-server-sdk')

cloud.init()

// 云函数入口函数
exports.main = async (event, context) => {
  const wxContext = cloud.getWXContext()
  const name = event.name;
  
  return {
    openid: wxContext.OPENID,
  }
}

云数据库

接下来我们需要建立云数据库,用来储存玩家的游戏数据。

点击云开发按钮,选择数据库
CocosCreator3D微信小游戏入门RunningBall(五):微信云开发实现排行榜_第11张图片
CocosCreator3D微信小游戏入门RunningBall(五):微信云开发实现排行榜_第12张图片

点击集合名称旁边的这个小加号,创建一个新的数据库,我们命名为Data

这个Data里面就是储存我们的Global类中的userData属性的数据,也就是我们的用户数据。
包括用户头像链接,用户昵称,用户的openid,以及用户当前解锁的关卡

新建完数据库后,我们修改一下数据库的权限
在这里插入图片描述
CocosCreator3D微信小游戏入门RunningBall(五):微信云开发实现排行榜_第13张图片

我们把权限修改为所有用户可读、仅创建者可写,这样也就意味着,用户可以获得其他玩家的头像、昵称以及关卡解锁情况这些信息,这样也就为我们制作排行榜提供了方便。

对于云数据库的操作,我们完全可以在客户端进行数据库的增删改查。
不过我们的小游戏目前还用户到删除功能。

我们在Wechat类中封装一下增改查的调用

	//获得某个用户的游戏信息
    getUserCloudData (openid: string, cb: ((userData: UserData | null) => void)) {
        const db = wx.cloud.database();
        db.collection('Data').where({
            openid: openid
        }).get({
            success: (res) => {
                if (res.errMsg === "collection.get:ok") {
                    var userData = new UserData(res.data[0]);
                    cb(userData);
                }
                else {
                    cb(null);
                }
            },
            fail: () => {
                cb(null);
            }
        })
    }

	//增加一项用户信息
    addUserCloudData (option: UserData, cb: (() => void)) {
        const db = wx.cloud.database();
        db.collection('Data').add({
            data: option.toData(),
            success: (res) => {
                option._id = res._id;
                cb();
            }
        })
    }

	//更新用户信息
    updateUserCloudData (newData: UserData, cb: (() => void)) {
        const db = wx.cloud.database();
        db.collection('Data').doc(newData._id).update({
            data : newData.toData(),
            success: (res) => {
                cb();
            }
        })
    }
    
    //设置用户信息
    setUserCloudData (newData: UserData, cb: (() => void)) {
        if (newData.openid !== Global.userData.openid || newData._id !== Global.userData._id) {
            cb();
        }
        //查询一下有没有这个用户的记录
        this.getUserCloudData(newData.openid, (userData: UserData | null) => {
            if (userData) {
                //存在记录,则更新
                this.updateUserCloudData(newData, cb);
            }
            else {
                //不存在记录,则新建记录
                this.addUserCloudData(newData, cb);
            }
            cb();
        })
    }

这样我们在游戏胜利时,通过setUserCloudData这个接口就可以很方便的上传数据
例如将我们之前做好的GameScene.ts脚本中的gameWin方法增加一下这个接口的调用

    gameWin () {
        if (!this.endLayer) {
            return;
        }
        this.endLayer.getComponent(EndLayer).setGameResult(true);
        this.endLayer.active = true;
        var nextIndex = this.sceneIndex + 1;
        var totleLevelNumber = Global.totleLevelNumber;
        nextIndex = nextIndex > totleLevelNumber ? totleLevelNumber : nextIndex;
        nextIndex = nextIndex > Global.userData.currentlevel ? nextIndex : Global.userData.currentlevel;
        Global.userData.currentlevel = nextIndex;
        //设置用户信息
        Wechat.instance().setUserCloudData(Global.userData, () => {});
    }

这样用户的信息就被记录到云数据库中,下一次再打开游戏,通关数据就不会丢失,可以接着继续挑战

世界关卡排行榜

我这里做的排行榜是一个世界排行榜,展示的是每一个关卡最快通关的用户信息

因此我在云数据库中又创建了这么几个数据库:
CocosCreator3D微信小游戏入门RunningBall(五):微信云开发实现排行榜_第14张图片

目前总共有7个关卡,因此我创建了7个数据库都是以Level字段作为开头

当玩家完成了某一关时,我就将他这一关的得分数据上传到对应的这个关卡的云数据库中。

当我需要取第一名时,我只需要对数据库做个排序然后再取第一位即可

相关代码:

	//根据关卡编号,获取该关卡的第一名用户信息
    getWorldRankInfo (level: number, cb : ((data: any) => void)) {
        var collectionName = "Level" + level;
        const db = wx.cloud.database();
        const $ = db.command;
        //orderBy是排序的api,
        //第一个参数是用来排序的字段,
        //第二个参数"asc"意味着低位排序,也就是由低到高进行排序
        //limit是限制输出的数据个数,由于我只要第一名的数据,因此参数为1
        db.collection(collectionName).orderBy('score', 'asc').limit(1).get({
            success: (res) => {
                if (res.errMsg === "collection.get:ok") {
                    if (res.data.length > 0) {
                        cb(res.data);
                    }
                    else
                        cb(null);
                }
                else {
                    cb(null);
                }
                console.log(res);
            }
        })
    }

    setWorldRankInfo (level: number, score: number) {
        var collectionName = "Level" + level;
        const db = wx.cloud.database();
        const $ = db.command;

			//where是查询的api
			//$.gt(score)中,gt代表着查询当数据库中的score数值 大于 传参score时候的数据。
			//这里的score是传进来的参数,也就是玩家这一次完成关卡的成绩
			//这样子的意思就是和玩家记录的最好成绩相比较一下,如果当前成绩比最好成绩要好,那么查询就会有返回
			//如果当前成绩不如云端的最好成绩,那么就没有查询返回
        db.collection(collectionName).where({
            openid: Global.userData.openid,
            score: $.gt(score)
        }).get({
            success: (res) => {
                if (res.errMsg === "collection.get:ok") {
                    if (res.data.length > 0) {
                    	//如果有返回值,意味着当前成绩要好,需要更新数据库中的数据
                        db.collection(collectionName).doc(res.data[0]._id).update({
                            data: {
                                score: score
                            }
                        })
                        return;
                    }
                }
                //如果查询失败,则增加这个数据到数据库中
                db.collection(collectionName).add({
                    data: {
                        openid: Global.userData.openid,
                        score: score
                    }
                })
            }
        })
    }

这里的效率其实不算很高,因为每次操作都要对数据库做很多操作。有优化的点,当然如果你的游戏只有一个关卡(无限挑战)那种,或者是进行排名的数据只有一个(比如等级或战斗力),那么一个表就足矣,而且还方便。

同样,我们在gameWin方法中,也就是胜利的时候调用一下做好的接口就行。最终的gameWin方法

    gameWin () {
        if (!this.endLayer) {
            return;
        }
        this.endLayer.getComponent(EndLayer).setGameResult(true);
        this.endLayer.active = true;
        var nextIndex = this.sceneIndex + 1;
        var totleLevelNumber = Global.totleLevelNumber;
        nextIndex = nextIndex > totleLevelNumber ? totleLevelNumber : nextIndex;
        nextIndex = nextIndex > Global.userData.currentlevel ? nextIndex : Global.userData.currentlevel;
        Global.userData.currentlevel = nextIndex;
        
        Wechat.instance().setUserCloudData(Global.userData, () => {});
        Wechat.instance().setWorldRankInfo(this.sceneIndex, Global.levelTime);
    }

在排行榜界面中创建一个方法,调用一下getWorldRankInfo,即可拿到排行数据。例如这样,拿到数据后储存在Global中,用来进行页面显示的更新数据。

    getInfo () {
        Wechat.instance().getWorldRankInfo(Global.rankLayerIndex, (data: any) => {
            Global.worldRankData = data;
            director.emit('WorldRankDataUpdated');
        });
    }

最终的排行榜效果:
CocosCreator3D微信小游戏入门RunningBall(五):微信云开发实现排行榜_第15张图片

CocosCreator3D微信小游戏入门RunningBall(五):微信云开发实现排行榜_第16张图片

你可能感兴趣的:(CocosCreator3D)