前言
为了实现小游戏的好友排行榜功能,在无数坑里爬起来后,决定分享一下
以下所用的素材均来自官方Demo
— Ranking、开放数据域文档
实现效果
- 点击排行榜图标,出现排行榜窗口
- 加载展示好友排行榜
- 滑动排行榜可滚动榜单
- 点击底下按钮可分享到群聊
- 点击排行榜外将隐藏排行榜
实现步骤
通往罗马的路不止一条,以下只是一种实现方式,仅供参考
设置
主要有两处设置
- 在微信公众平台配置排行榜细则
- 功能 》游戏运营工具箱 》排行榜设置,如下(这是已经设置好的),点添加即可
- 填写排行榜配置项,提交等待审核即可
- 功能 》游戏运营工具箱 》排行榜设置,如下(这是已经设置好的),点添加即可
- 在Unity内设置
- 点击上方菜单栏
微信小游戏
,打开转换小游戏窗口,在下方设置列表里,将SDK功能选项底下的使用好友关系链勾选
- 点击上方菜单栏
Unity场景配置
- 该功能具体使用到的场景实体如下左图红框标示的,具体场景的展示如下右图


-
Canvas: 项目其它UI所在Canvas,与该功能无关,无需理会
- RankBtn: 点击呼出排行榜的按钮,如无其它Canvas,想放在底下RankCanvas也是可以的,按钮点击事件绑定脚本(底下给出)里的
ShowRank()
- RankBtn: 点击呼出排行榜的按钮,如无其它Canvas,想放在底下RankCanvas也是可以的,按钮点击事件绑定脚本(底下给出)里的
-
RankCanvas: 新建Canvas,用于展示排行榜相关内容的Canvas,
渲染模式不能选摄像机
,这里用的是覆盖Overlay
,Canvas Scaler
设置如下,根据自己的需要来即可
PS:这是一个坑位,渲染模式选摄像机后,榜单将不能滚动
- RankMask: 作为全屏幕的透明遮罩,大小与上层
Canvas
设置一致,为了实现点击可退出排行榜的功能,本质是一个不指定源图像的Image
,并且添加Button
属性,按钮绑定脚本里的HideRank()
。未点击排行榜时不渲染,因此需SetActive(false)
,即名称旁边的勾取消掉 - RankBox: 里面包含了排行榜真正展示的内容,本身是什么不重要,只是为了更好地约束底下成员,这里是用一个不知道精灵属性的
Sprite
。同样的,一开始需SetActive(false)
- Bg: 排行榜的背景图
- FTitle: 排行榜标题
- RankBody: 一个
RawImage
对象,由于实际渲染是上下颠倒的,所以旋转属性的x
需设置为180
,该对象即为小游戏概念里SharedCanvas
渲染的占位纹理 - SharedBtn: 一个按钮,点击可分享到群聊,绑定脚本里的
ShareGroupRank()
- RankMask: 作为全屏幕的透明遮罩,大小与上层
-
Ranking: 一个空对象,用于绑定脚本,其中,脚本的一些配置如下
PS:个人习惯,你也可以把脚本绑定在其它对象上,自己喜欢就好
脚本编写
- 主要逻辑脚本,基本与
官方Demo
一致,可根据自己需要更改
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using WeChatWASM;
/// <summary>
/// 排行榜相关测试
/// </summary>
/// 目前只有好友排行榜功能
/// 后续可整合加入其它排行榜
public class RankingTest : MonoBehaviour
{
/// <description>
/// <list type="table">
/// 微信小游戏 主域 与 开放数据域 的理解
/// <item>
/// 主域
/// <description>即我们进入游戏玩的、看到的内容(canvas),除开放数据域之外的,本质就是我们的游戏项目</description>
/// </item>
/// <item>
/// 开放数据域
/// <description>
/// 一个私有的域,或者说canvas(sharedCanvas),本质是一个子文件夹(由game.json配置的)
/// 里面可以调用一些敏感数据API,然后渲染在sharedCanvas
/// 主域能做的就是把内容设置好的sharedCanvas渲染在界面的某处
/// </description>
/// </item>
/// </list>
/// </description>
// 排行榜内容的实际显示,即小游戏开放数据域的sharedCanvas
public RawImage RankBody;
// 当展开排行榜时全屏的一个遮罩
// 用于监听防止点击其它区域,同时监听关闭(实现点击排行榜外时关闭排行榜)
public GameObject RankMask;
// 排行榜内容的集合
// 层级关系
// RankBox
// - Bg
// - RankBody
// - SharedBtn(可加)
// - 其它排行呼出按钮(可加)
// 当排行榜显示/隐藏,只需控制RankBox的显隐
public GameObject RankBox;
// private bool isShow = false;
// Start is called before the first frame update
void Start()
{
Init();
GroupSharedInit();
}
/// <summary>
/// 通知开放数据域渲染排行榜
/// </summary>
private void ShowOpenData()
{
// 显示排行榜相关的Unity-UI
RankMask.SetActive(true);
RankBox.SetActive(true);
// 注意这里传x,y,width,height是为了点击区域能正确点击,x,y 是距离屏幕左上角的距离,宽度传 (int)RankBody.rectTransform.rect.width是在canvas的UI Scale Mode为 Constant Pixel Size的情况下设置的。
/**
* 如果父元素占满整个窗口的话,pivot 设置为(0,0),rotation设置为180,则左上角就是离屏幕的距离
* 注意这里传x,y,width,height是为了点击区域能正确点击,因为开放数据域并不是使用 Unity 进行渲染而是可以选择任意第三方渲染引擎
* 所以开放数据域名要正确处理好事件处理,就需要明确告诉开放数据域,排行榜所在的纹理绘制在屏幕中的物理坐标系
* 比如 iPhone Xs Max 的物理尺寸是 414 * 896,如果排行榜被绘制在屏幕中央且物理尺寸为 200 * 200,那么这里的 x,y,width,height应当是 107,348,200,200
* x,y 是距离屏幕左上角的距离,宽度传 (int)RankBody.rectTransform.rect.width是在canvas的UI Scale Mode为 Constant Pixel Size的情况下设置的
* 如果是Scale With Screen Size,且设置为以宽度作为缩放,则要这要做一下换算,比如canavs宽度为960,rawImage设置为200 则需要根据 referenceResolution 做一些换算
* 不过不管是什么屏幕适配模式,这里的目的就是为了算出 RawImage 在屏幕中绝对的位置和尺寸
*/
// CanvasScaler scaler = gameObject.GetComponent<CanvasScaler>();
// var referenceResolution = scaler.referenceResolution;
// 获取canvas的单位像素,即上面RankCanvas设置的大小
var referenceResolution = new Vector2(1080, 1920);
var p = RankBody.transform.position;
// 计算渲染的区域、大小,通知开放数据域初始化渲染
// 这里的计算效果不一定理想,需根据实际慢慢调试
// 此处设置的值可能影响排行榜单的滚动、点击等操作
WX.ShowOpenData(RankBody.texture, (int)p.x, Screen.height - (int)p.y, (int)((Screen.width / referenceResolution.x) * RankBody.rectTransform.rect.width), (int)((Screen.width / referenceResolution.x) * RankBody.rectTransform.rect.height));
}
/// <summary>
/// 显示好友排行榜
/// </summary>
public void ShowRank()
{
Debug.Log("展示排行榜");
// 渲染数据
this.ShowOpenData();
// 通知开放数据域
string msg = JsonUtility.ToJson(OpenDataMessage.SHOW_FRIENDS_RANK());
WX.GetOpenDataContext().PostMessage(msg);
}
/// <summary>
/// 隐藏排行榜
/// </summary>
public void HideRank()
{
// 隐藏排行榜相关实体
RankMask.SetActive(false);
RankBox.SetActive(false);
Debug.Log("隐藏排行榜");
// 调用WX-API通知开放数据域执行隐藏操作,防止一些材质混乱问题
WX.HideOpenData();
}
public void ShareGroupRank()
{
// 分享信息到群聊
WX.ShareAppMessage(new ShareAppMessageOption()
{
// 标题
title = "最强战力排行榜!谁是第一?",
// 分享的路径参数,与OnShow约定进行判断
query = "action=show_group_rank",
// 封面 - 后续可更换为自己的,但需要在微信公众平台配置
// https://developers.weixin.qq.com/minigame/dev/guide/open-ability/share/share.html
imageUrl = "https://mmgame.qpic.cn/image/5f9144af9f0e32d50fb878e5256d669fa1ae6fdec77550849bfee137be995d18/0",
});
}
/// <summary>
/// 初始化
/// </summary>
/// <description>可用于做一些需要的初始化、判断什么的,这里暂时没有</description>
void Init()
{
Debug.Log("初始化排行榜");
}
/// <summary>
/// 初始化群排行榜分享
/// </summary>
void GroupSharedInit()
{
/**
* 使用群排行功能需要特殊设置分享功能,详情可见链接
* https://developers.weixin.qq.com/minigame/dev/guide/open-ability/share/share.html
*/
WX.UpdateShareMenu(new UpdateShareMenuOption()
{
withShareTicket = true,
isPrivateMessage = true,
});
/**
* 群排行榜功能需要配合 WX.OnShow 来使用,整体流程为:
* 1. WX.UpdateShareMenu 分享功能;
* 2. 监听 WX.OnShow 回调,如果存在 shareTicket 且 query 里面带有启动特定 query 参数则为需要展示群排行的场景
* 3. 调用 WX.ShowOpenData 和 WX.GetOpenDataContext().PostMessage 告知开放数据域侧需要展示群排行信息
* 4. 开放数据域调用 wx.getGroupCloudStorage 接口拉取获取群同玩成员的游戏数据
* 5. 将群同玩成员数据绘制到 sharedCanvas
*/
WX.OnShow((OnShowListenerResult res) =>
{
string shareTicket = res.shareTicket;
Dictionary<string, string> query = res.query;
if (!string.IsNullOrEmpty(shareTicket) && query != null && query["action"] == "show_group_rank")
{
OpenDataMessage msgData = OpenDataMessage.SHOW_GROUP_FRIENDS_RANK(shareTicket);
string msg = JsonUtility.ToJson(msgData);
// 展示排行榜
this.ShowOpenData();
// 通知开放数据域
WX.GetOpenDataContext().PostMessage(msg);
}
});
}
}
OpenDataMessage
,与开放数据域通信的消息结构
using UnityEngine.SocialPlatforms.Impl;
[System.Serializable]
public class OpenDataMessage
{
// type 用于表明消息类型
// 需与open-data里约定一致
public string type;
public string shareTicket;
public int score;
/* 底下的静态方法只是为了使用方便 */
/// <summary>
/// 展示好友排行榜
/// </summary>
/// <returns></returns>
public static OpenDataMessage SHOW_FRIENDS_RANK()
{
OpenDataMessage message = new OpenDataMessage();
message.type = "showFriendsRank";
return message;
}
/// <summary>
/// 展示群组排行榜
/// </summary>
/// <param name="shareTicket">群分享对应的标识</param>
/// <returns></returns>
public static OpenDataMessage SHOW_GROUP_FRIENDS_RANK(string shareTicket)
{
OpenDataMessage message = new OpenDataMessage();
message.type = "showGroupFriendsRank";
message.shareTicket = shareTicket;
return message;
}
/// <summary>
/// 设置用户记录
/// </summary>
/// <param name="score">分数</param>
/// <returns></returns>
public static OpenDataMessage SET_USER_RECORD(int score)
{
OpenDataMessage message = new OpenDataMessage();
message.type = "setUserRecord";
message.score = score;
return message;
}
}
- 设置
open-data
的key
- 在
WX-WASM-SDK-V2/Runtime/wechat-default/open-data/index.js
内,更改const RANK_KEY
的值,改为前面在微信公众平台
排行榜设置里配置的排行榜唯一标识 - 在该文件内默认存在以下测试代码,会在第一次加载时给用户随机设置个分数,不需要去掉即可
// test setUserRecord(RANK_KEY, Math.ceil(Math.random() * 1000));
- 在
更改榜单样式
如需更改榜单样式,可在WX-WASM-SDK-V2/Runtime/wechat-default/open-data/render
进行更改,其中tpls
为模板,styles
为模板样式,模板标签跟小程序类似,可使用纯js
语法结合模板字符串语法书写;样式就是普通的CSS
PS:如果模板使用了
text
标签,内容要写在标签属性value
里,写在标签内可能不展示,算是一个小坑位