CocosCreator实现微信排行榜

1. 概述

不管是在现实生活还是当今游戏中,各式各样的排名层出不穷。如果我们做好一款游戏,却没有实现排行榜,一定是不完美的。排行榜不仅是玩家了解自己实力的途径,也是游戏运营刺激用户留存的一种途径。在微信小游戏中普遍存以下两种排名

  • 好友关系排名
  • 世界排名

CocosCreator实现微信排行榜

其中好友的排名,需要通过微信子域实现。在子域上下文中,只能调用微信提供相关的api,且数据传输只能进不能出。即使在子域中调用云函数也不行。这个对数据很严格,开发略为复杂。但好处也很明显

  • 无需用户确认授权就可实现排名
  • 排名信息均为自己好友,刺激效果更明显

尽管这样,我们还是先实现世界排行。世界排行需要用户授权。早期只需要调用 wx.authorize就可以实现,现在很不稳定(好像废弃了)。所以不得不通过生成一个授权按钮来实现

2. 微信云开发

微信小游戏为开发者提供了一部分免费的云环境。可以实现文件存储,数据存储以及通过云函数实现服务端接口。开通方式也很简单,这里不做说明。既然要实现排名,优先选用云函数来实现对应的api。要实现云函数,需要在 project.config.json文件中通过属性 cloudfunctionRoot指定云函数目录。由于,是通过cocoscretor开发,每次构建发布都会清空输出内容。为了解决人肉复制粘贴,我们需要通过 定制小游戏构建模板实现微信小游戏所有代码的管理。小游戏地心侠士构建模板如下

CocosCreator实现微信排行榜

从图中,可以看到获取openid、获取世界排名、保存用户授权信息等云函数都放在cocoscreator代码环境中。这样在开发完成后,通过cocoscreator构建发布,对应的云函数也会一起打包过去

3. 实现世界排行

3.1 获取玩家openid

首先在构建模板的 cloud-functions文件件中,使用 npm初始一个名为 getOpenId的node项目。初始好以后,运行 npm install wx-server-sdk@latest --save。这样就建立好了一个云函数的基本框架。

我们在 index.js文件,输入以下代码

// author:herbert 464884492
// project:地心侠士  获取用户openid
const cloud = require('wx-server-sdk')
cloud.init()
exports.main = async (event, context) => {
  const wxContext = cloud.getWXContext()
  return {
    event,
    openid: wxContext.OPENID,
    appid: wxContext.APPID,
    unionid: wxContext.UNIONID,
  }
}

调用云函数时,上下文中便可以得到玩家openid和uninid。玩家进入游戏就先调用此函数,得到玩家的openid用于后边更新玩家数据和获取世界排行的条件。

小游戏端调用云函数前,需要初始云环境。因为采用定制构建模板,所以我们直接在模板的 game.js文件末尾初始我的云环境

javascript;gutter:true; // author:herbert 464884492 // 地心侠士 初始云环境 ....</p> <p>wxDownloader.init(); window.boot();</p> <p>//初始化云调用 this.wx.cloud.init({ traceUser: true, env: 'dxxs-dxxs' }); ...</p> <pre><code> 后续调用云函数中,第一步都是要获取openid,这里定义一个全局变量将其保存起来,调用方法如下 </code></pre> <p>// author:herbert 464884492 // 地心侠士 玩家openid private static openId: string = null; private static initenv() { return new Promise((resolve, reject) => { if (!this.wx) reject(); //直接使用本地缓存 if (this.openId != null) resolve(); // 调用云函数获取 this.wx.cloud.callFunction({ name: 'getOpenId', complete: res => { this.openId = res.result.openid; resolve(); } }); }); }</p> <pre><code> ### 3.2 动态生成授权按钮 先看下地心侠士布局界面 ![](https://pic1.zhimg.com/80/v2-0fef12423f531ded168ad60a673212b8_720w.jpg) 上图中可以看到,地心侠士虚拟了一个游戏操作区域。玩家聚焦到世界排行时,需要渲染一个授权按钮在确定的位置。需求很简单,可考虑到移动端多分辨率,这个操作就变得复杂了。需要做屏幕适配。地心侠士采用自适应宽度的适配策略,配置如下图 ![](https://pic4.zhimg.com/80/v2-bcff27c127939ebb691edbef770ff6af_720w.jpg) 游戏运行时获取实际分辨率的宽度与设计的宽度相除,变可知道当前宽度变化比列,键盘容器九宫格使用了主键 底部111px,高度161px。确定按钮宽度105px ![](https://pic1.zhimg.com/80/v2-e9f3725cb16369ef55e9d5e0d9d5e280_720w.jpg) 微信小游戏以左上角为原点,通过 确定位置。然而,cocoscreator以左下角为原点,所以在计算 值时需要用屏幕宽度 - box上边缘y坐标。适配代码如下 </code></pre> <p>// author:herbert 464884492 // 地心侠士 动态生成透明授权按钮 initUserInfoButton() { // 获取设计尺寸 let desingSize: cc.Size = cc.view.getDesignResolutionSize(); // 获取实际屏幕尺寸 let screenSize: cc.Size = cc.view.getFrameSize(); // 获取宽度倍率 let widthRate = screenSize.width / desingSize.width; // 获取当前倍率下九宫格键盘实际高度 let halfKcHeight = 161 * widthRate / 2; // 获取当前倍率下确定按钮实际宽度 let btnwidth = this.btnKeySuer.width * widthRate; WxCloudFun.createUserinfoButton("", // 确定按钮中心点对应小游戏left值 (屏幕宽度-确定按钮实际宽度)/2 // 定义实际授权按钮size为105<em>40,所以还必须加上对应的偏差值 // 以下代码中left体现整体适配过程,不考虑中间过程可以直接使用 // (屏幕宽度-授权按钮)/2 即可得到left值 screenSize.width / 2 - 52.5 * widthRate + (btnwidth - 105) / 2, // Canvas 适配策略是 Fit Width,所以Canvas下边沿不一定就是屏幕边缘 // 通过111</em>widthRate得到具体下沿值,在加上虚拟键盘一半高度,可得到中心位置 // 由于微信原点在左上角,需要保持按钮处于中心位置,坐标还需要上移一半按钮高度 screenSize.height - (111 * widthRate + halfKcHeight + 20), () => { this.keyCode = cc.macro.KEY.r; this.scheduleOnce(async () => { this.dlgRank.active = true; // 获取排名数据 await this.getRankInfo(); }, 0); }); }</p> <pre><code> ### 3.3 获取用户头像昵称信息 经过上一步骤的适配操作,只要玩家聚焦到【世界排行】,地心侠士虚拟键盘的确定按钮正上方会覆盖一个透明的 ,玩家点击确定就会唤起授权对话框,然后在对应的回调函数就可以完成用户数据保存操作 </code></pre> <p>// author:herbert 464884492 // 地心侠士 获取玩家基本信息 public static createUserinfoButton(text: string, left: number, top: number, cb: Function) { this.userInfoButton = this.wx.createUserInfoButton({ type: 'text', text: text, style: { left: left, top: top, height: 40, width: 105, lineHeight: 40, textAlign: 'center', fontSize: 16, backgroundColor: '#ff000000',// 透明 color: '#ffffff', } }); this.userInfoButton.hide(); this.userInfoButton.onTap((res) => { // 将获取到的用户数据提交到云端 this.wx.cloud.callFunction({ name: 'putUserinfo', data: { ...res.userInfo, openid: this.openId } }); this.hideUserInfoButton(); cb.call(); }); }</p> <pre><code> 在代码中,除了传入玩家微信信息外。我还额外传递进入游戏时就获取的 。正常情况下不需要的,因为,云函数中天然会告诉你openid。不过,我们在后端使用了 获取玩家头像保存到云端文件存储中。引入此包后,后端就获取不到 了,相当奇怪。对应云平台云函数代码如下 </code></pre> <p>// author:herbert 464884492 // 地心侠士 云函数保存玩家基本信息 const cloud = require('wx-server-sdk') const got = require('got') cloud.init() // 云函数入口函数 exports.main = async(event, context) => { const { nickName, avatarUrl, gender, openid } = event; let wxContext = cloud.getWXContext(); // 如果后端拿不到openid就采用前端传入的openid wxContext.OPENID = wxContext.OPENID || openid; const log = cloud.logger() log.info({ tip: <code>正在请求头像地址[${avatarUrl}]</code> }) // 获取头像数据流 const stream = await got.stream(avatarUrl); let chunks = []; let size = 0; const body = await (async() => { return new Promise((res, reg) => { stream.on('data', chunk => { chunks.push(chunk) size += chunk.length log.info({ tip: <code>正在读取图片流信息:[${chunk.length}]</code> }) }) stream.on('end', async() => { const body = Buffer.concat(chunks, size) log.info({ tip: <code>正在保存头像文件:[${size}]</code> }) res(body) }) }) })() //保存头像到云存储 const { fileID } = await cloud.uploadFile({ cloudPath: <code>avatars/${wxContext.OPENID}.jpg</code>, fileContent: body }) // 添加或更新玩家信息到数据库 const db = cloud.database() const { data } = await db.collection("dxxs").where({ _openid: wxContext.OPENID }).get() const updateData = { fileId: fileID, nickName: nickName, sex: gender == 1 ? '男' : '女', avatarUrl: avatarUrl } if (data.length > 0) { log.info({ tip: <code>正在修改数据库信息:[${size}]</code> }) await db.collection("dxxs").doc(data[0]._id).update({ data: updateData }) } else { log.info({ tip: <code>正在添加数据库信息:[${size}]</code> }) await db.collection("dxxs").add({ data: { ...updateData, _openid: openid } }) }</p> <p>return { openid: wxContext.OPENID, appid: wxContext.APPID, unionid: wxContext.UNIONID } }</p> <pre><code> ### 3.4 获取排行数据 保存完用户数据后,通过一个回调函数,实现了玩家排名数据获取。细心的朋友可以在前边授权按钮适配的章节看到 this.getRankInfo();这句代码。后端云函数就是一个简单数据查询。效果图如下 ![](https://pic2.zhimg.com/80/v2-b99b34c09a6384801b62384d6216fef5_720w.jpg) 从上图可以看到,我实现了三个维度排名,需要在前端需要传入排名字段。对应代码如下 </code></pre> <p>// author:herbert 464884492 // 地心侠士 获取排名信息 public static async getWorldRanking(field: string = "level") { const { result } = await this.wx.cloud.callFunction({ name: 'getWordRanking', data: { order: field } }); return result.ranks; }</p> <pre><code> 云函数代码如下 </code></pre> <p>// author:herbert 464884492 // 地心侠士 云函数返回排名信息 const cloud = require('wx-server-sdk') cloud.init() exports.main = async (event, context) => { const wxContext = cloud.getWXContext() const db = cloud.database(); const { order = "level" } = event;</p> <p>const openData = await db.collection("dxxs") .orderBy(order, "asc") .get() const ranks = openData.data.map(item => { return { openid: item._openid, [order]: item[order], nickName: item.nickName, fileId: item.fileId, avatarUrl: item.avatarUrl } }); return { ranks: ranks, openid: wxContext.OPENID, appid: wxContext.APPID, unionid: wxContext.UNIONID } }

4. 总结

  • 微信子域数据很严格,数据只进不出。调用云函数也不行
  • 云函数中使用http请求,可能会得不到openid
  • 屏幕适配知道定位原则,也可以很简单
  • avatarUrl通过Sprite现实头像,需要设置安全域名
  • 目前部分华为手机分享截屏出现黑屏使用canvas.toTempFilePath就可以解决

这里有一个CoscosCreator游戏开发群,欢迎喜欢聊技术的朋友加入

CocosCreator实现微信排行榜

欢迎感兴趣的朋友关注我的订阅号”小院不小”,或点击下方二维码关注。我将多年开发中遇到的难点,以及一些有意思的功能,体会都会一一发布到我的订阅号中

CocosCreator实现微信排行榜

Original: https://www.cnblogs.com/yfrs/p/wxrank.html
Author: _herbert
Title: CocosCreator实现微信排行榜

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/607704/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 20220728-在IDEA中进行Java的断点调试Debug

    断点调试介绍 断点调试是指在程序的某一行设置一个断点,在调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行…

    Java 2023年6月15日
    099
  • Java集合类详解

    集合类的特点:提供一种存储空间可变的存储模型,存储的数据容量可以随时发生改变。 Collection:单列(接口) 概述: 是单列集合的顶层接口,表示一组对象,这些对象也成为Cpl…

    Java 2023年6月6日
    089
  • [JVM] Java内存分配

    程序计数器 程序计数器是一块较小的内存区域,作用可以看做是当前线程执行的字节码的位置指示器。分支、循环、跳转、异常处理和线程恢复等基础功能都需要依赖这个计算器来完成。 虚拟机栈 虚…

    Java 2023年6月5日
    075
  • 概率神经网络 (PNN) 应用的简单DEMO

    概率神经网络的全称是Probabilistic neural network,它主要用于模式分类,是基于贝叶斯策略前馈神经网络。它有着坚实的数学理论基础,当然本文并不打算从数学符号…

    Java 2023年6月15日
    090
  • 两小无猜的爱恨情仇–java =+和+=揭秘

    故事背景 当一个人问另一个人”敢不敢”的时候,另一个人必须说”敢”,这就是游戏的规则。小男孩朱利安和小女孩苏菲的相遇即开始于这样一场…

    Java 2023年5月29日
    057
  • 面试官太难伺候?一个try-catch问出这么多花样

    原创:微信公众号 【阿Q说代码】,欢迎分享,转载请保留出处。 哈喽大家好,我是阿Q! 刚刚面试回来的B哥又在吐槽了:现在的面试官太难伺候了,放着好好的堆、栈、方法区不问,上来就让我…

    Java 2023年6月5日
    093
  • 多线程与高并发(三)—— 源码解析 AQS 原理

    一、前言 AQS 是一个同步框架,关于同步在操作系统(一)—— 进程同步 中对进程同步做了些概念性的介绍,我们了解到进程(线程同理,本文基于 JVM 讲解,故下文只称线程)同步的工…

    Java 2023年6月9日
    079
  • 制作自己的Docker镜像

    制作镜像有2种方式,一种是容器转换成镜像,另一种是使用dockerfile创建镜像,一般后者更常用。 使用 docker commit命令将容器转换成镜像 docker commi…

    Java 2023年6月7日
    078
  • 双路快速排序以及三路排序算法

    目录 双路快速排序 一、概念及其介绍 二、适用说明 三、过程图示 四、Java 实例代码 三路排序算法 一、概念及其介绍 二、适用说明 三、过程图示 四、Java 实例代码 双路快…

    Java 2023年6月5日
    0102
  • Redis缓存相关的几个问题

    1 缓存穿透 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,进而给数据库带来…

    Java 2023年6月7日
    072
  • Maven Archetype 多 Module 自定义代码脚手架

    大部分公司都会有一个通用的模板项目,帮助你快速创建一个项目。通常,这个项目需要集成一些公司内部的中间件、单元测试、标准的代码格式、通用的代码分层等等。 今天,就利用 Maven 的…

    Java 2023年6月13日
    0104
  • 动态代理-jdk代理

    实现InvocationHandler接口(代理处理器) 1 package cn.learn.proxy; 2 3 import java.lang.reflect.Invoca…

    Java 2023年5月30日
    091
  • 如何引用 System.Runtime.Serialization.Json;

    今天新开的一个项目突然发现引用System.Runtime.Serialization.Json 提示 命名空间 不存在类型或命名空间名称 json 明明前段时间刚开发的WCF是很…

    Java 2023年6月14日
    083
  • 第二周

    学了个新知识:幂等性👍 在使用pageInfo时警告: Unchecked call to ‘PageInfo(List)’ as a member of …

    Java 2023年6月7日
    0102
  • Ubuntu 20.04 挂载 NTFS 硬盘

    创建挂载目录 mkdir /mnt/hdd 此目录在最后一步中用得到。 确定要挂载的硬盘 fdisk -l 由于我们要挂载的是 NTFS 硬盘,根据上面的信息,可以确定 /dev/…

    Java 2023年6月7日
    083
  • Day13 note

    super注意点: 1、super调用父类的构造方法,必须在构造方法的第一行 2、super必须只能出现在子类的方法或者构造方法中 3、super和this不能同时调用构造方法对比…

    Java 2023年6月5日
    082
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球