跳到主要内容
版本:v4

TapTap排行榜开发指南

本文介绍如何在游戏中加入排行榜系统。

权限说明

该模块需要如下权限:

权限使用目的权限申请时机
网络权限用于访问网络数据用户首次使用该功能时会申请权限
网络状态权限用于检查网络连接状态(如 Wi-Fi 或移动数据是否可用)用户首次使用该功能时会申请权限
写入外部存储权限用于保存排行榜分享图片到设备存储用户首次使用分享功能时会申请权限

该模块将在应用中添加如下权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

集成前准备

参考 准备工作 创建应用、开启排行榜服务。

SDK 获取

由于 TapSDK V4 依赖于 TapTapCore 核心库 以及 TapTapLogin 登录,所以需要在 TapTapCore 的基础上,另外添加 TapTapLeaderboardTapTapLogin 模块:

第一步:添加 SDK 所需的外部依赖

SDK 内部使用了部分第三方库,开发者接入时需先确保 SDK 外部依赖库已正常接入,具体设置如下:

  1. SDK 使用的 JSON 解析库为 Newtonsoft-json,如果当前工程已接入该依赖库,则不需额外处理,否则需在 Packages/manifest.json 添加如下依赖:
"com.unity.nuget.newtonsoft-json":"3.2.1"
  1. SDK 使用 com.google.external-dependency-manager 管理 Android、iOS 依赖,如果当前工程已接入该依赖库,则不需额外处理,否则需在 Packages/manifest.json 添加如下依赖:
{
"dependencies": {
"com.google.external-dependency-manager": "1.2.179"
},
"scopedRegistries": [
{
"name": "package.openupm.com",
"url": "https://package.openupm.com",
"scopes": [
"com.google.external-dependency-manager"
]
}
]
}

第二步:添加 SDK 依赖

SDK 支持 Unity Package Manager本地文件导入两种集成方式,开发者根据需求选择其中一种即可,推荐使用远程依赖。

远程依赖

SDK 支持通过 NPMJS 及 GitHub 两种方式,开发者选择其中一种即可。

1. NPMJS 接入

在项目的 Packages/manifest.json 文件中添加以下依赖:

"dependencies":{
"com.taptap.sdk.core":"4.8.2",
"com.taptap.sdk.login":"4.8.2",
"com.taptap.sdk.leaderboard":"4.8.2",
}

但需要注意的是,需在 Packages/manifest.jsondependencies 同级下声明 scopedRegistries

"scopedRegistries":[
{
"name": "NPMJS",
"url": "https://registry.npmjs.org/",
"scopes": ["com.taptap"]
}
]
2. GitHub 接入

在项目的 Packages/manifest.json 文件中添加以下依赖:

"dependencies":{
"com.taptap.sdk.core":"https://github.com/taptap/tapsdk-unity-dist.git?path=/Core#4.8.2",
"com.taptap.sdk.login":"https://github.com/taptap/tapsdk-unity-dist.git?path=/Login#4.8.2",
"com.taptap.sdk.leaderboard":"https://github.com/taptap/tapsdk-unity-dist.git?path=/Leaderboard#4.8.2",
}

说明:自 4.7.2 起,Unity Packages 仓库已统一为大仓 tapsdk-unity-dist,所有包通过目录区分并使用 ?path= 指定子目录、# 指定版本标签。例如 Core 模块:

"com.taptap.sdk.core": "https://github.com/taptap/tapsdk-unity-dist.git?path=/Core#4.7.2"

在 Unity 顶部菜单中选择 Window > Package Manager 可查看已经安装在项目中的包。

本地文件导入

  1. 下载页 下载下列模块对应 unitypackage 文件,并在 Unity 项目中依次通过 Assets > Import Packages > Custom Packages 进行导入,包括:
  • TapSDK_Core.unitypackage TapTapSDK 核心模块,必选
  • TapSDK_Login.unitypackage TapTapSDK 登录模块,必选
  • TapSDK_Leaderboard.unitypackage TapTapSDK 排行榜模块,必选
  1. 如果当前项目已集成 Newtonsoft.Json 依赖,则忽略该步骤,否则在 NuGet.org Newtonsoft.Json 页面中通过点击右侧 「Download package」 下载库文件,并将下载的文件后缀从.nupkg 修改为 .zip,同时解压该文件并复制内部的 Newtonsoft.Json.dll 文件拷贝到工程 AssetsPlugins 目录下,另外为了避免导出 IL2CPP 平台时删除必要数据,需在 Assets 目录下创建 link.xml 文件(如果已有该文件,则添加如下内容),其内容如下:
<linker>
<assembly fullname="System.Core">
<type fullname="System.Linq.Expressions.Interpreter.LightLambda" preserve="all" />
</assembly>
</linker>

SDK 初始化

详见 TapTapSDK 初始化文档

TapTapSdkOptions 详细参数见 入门指南#快速开始

using TapSDK.Core;
using TapSDK.Leaderboard

// 核心配置 详细参数见 [TapTapSDK]
TapTapSdkOptions coreOptions = new TapTapSdkOptions();
// 设置屏幕方向:0-竖屏 1-横屏
coreOptions.screenOrientation = 1;
// TapSDK 初始化
TapTapSDK.Init(coreOptions, otherOptions);

注册排行榜事件回调

排行榜 SDK 提供事件回调接口,用于处理排行榜相关事件,如用户未登录等状态。

using TapSDK.Leaderboard

LeaderboardCallback callback = new LeaderboardCallback();
TapTapLeaderboard.RegisterLeaderboardCallback(callback);
TapTapLeaderboard.UnregisterLeaderboardCallback(callback);

class LeaderboardCallback : ITapTapLeaderboardCallback
{

public LeaderboardCallback(){}

public void OnLeaderboardResult(int code, string message)
{
// 处理排行榜 SDK 的状态码
// 500102:需要登录
}

}

设置分享功能

设置分享回调后,排行榜H5页面会显示分享按钮,用户点击分享按钮后,排行榜截图会保存到本地,并触发分享成功回调。开发者可以根据自己的需要判断是否实现分享失败的回调方法。

using TapSDK.Leaderboard;
using UnityEngine;

// 设置分享回调(同步方法,建议在初始化时调用)
public void SetupLeaderboardSharing()
{
var shareCallback = new TapTapLeaderboardShareCallback
{
OnShareSuccessAction = (localPath) =>
{
// 处理排行榜分享图片保存成功
// localPath 为本地图片路径,可用于分享到系统
Debug.Log($"Share image saved to: {localPath}");

// 可以在这里调用系统分享功能
ShareImageToSystem(localPath);
},
OnShareFailedAction = (error) =>
{
// 处理分享失败(可选实现)
// 开发者可以根据需要选择是否实现此方法
Debug.LogError($"Share failed: {error?.Message}");
}
};

TapTapLeaderboard.SetShareCallback(shareCallback);
Debug.Log("Leaderboard share callback has been set");
}

// 分享图片到系统的示例方法
private void ShareImageToSystem(string imagePath)
{
// 这里可以实现具体的系统分享逻辑
// 例如使用Native Share插件或Unity的分享功能
Debug.Log($"Sharing image: {imagePath}");
}

打开排行榜页面

打开排行榜H5页面对话框,支持总榜和好友榜两种类型。

using TapSDK.Leaderboard;

// 打开排行榜页面(同步方法,无需async)
public void OpenLeaderboardPanel()
{
string leaderboardId = "your_leaderboard_id";

// 打开总榜
TapTapLeaderboard.OpenLeaderboard(leaderboardId, "public");

// 打开好友榜
TapTapLeaderboard.OpenLeaderboard(leaderboardId, "friends");
}

// 根据条件动态打开不同类型的排行榜
public void OpenLeaderboardByType(bool isFriendsBoard)
{
string leaderboardId = "your_leaderboard_id";
string collection = isFriendsBoard ? "friends" : "public";

TapTapLeaderboard.OpenLeaderboard(leaderboardId, collection);
}

打开用户个人资料

展示指定用户的个人资料对话框,传入用户的openId。

using TapSDK.Leaderboard;

// 打开用户个人资料(使用OpenID)
string openId = "user_open_id"; // 用户的OpenID
TapTapLeaderboard.ShowTapUserProfile(openId, null);

// 或者使用UnionID
string unionId = "user_union_id"; // 用户的UnionID
TapTapLeaderboard.ShowTapUserProfile(string.Empty, unionId);

提交分数

批量提交用户排行榜分数,一次最多提交5个分数。

using System.Collections.Generic;
using TapSDK.Leaderboard;
using UnityEngine;

// 提交分数(异步方法,需要在async方法中调用)
public async void SubmitPlayerScores()
{
// 单个分数提交
var singleScore = new List<SubmitScoresRequest.ScoreItem>
{
new SubmitScoresRequest.ScoreItem
{
LeaderboardId = "your_leaderboard_id",
Score = 1000
}
};

// 提交分数(使用异步方式)
try
{
var result = await TapTapLeaderboard.SubmitScores(singleScore);
// 提交成功
Debug.Log($"Submit scores success: {result}");
}
catch (TapException e)
{
// 提交失败
Debug.LogError($"Submit scores failed, code: {e.code}, message: {e.message}");
}
}

// 或者在需要时批量提交多个分数
public async void SubmitMultipleScores()
{
var multipleScores = new List<SubmitScoresRequest.ScoreItem>
{
new SubmitScoresRequest.ScoreItem
{
LeaderboardId = "leaderboard_1",
Score = 1000
},
new SubmitScoresRequest.ScoreItem
{
LeaderboardId = "leaderboard_2",
Score = 2000
}
};

try
{
var result = await TapTapLeaderboard.SubmitScores(multipleScores);
Debug.Log($"Submit multiple scores success: {result}");
}
catch (TapException e)
{
Debug.LogError($"Submit multiple scores failed, code: {e.code}, message: {e.message}");
}
}

获取排行榜数据

分页获取排行榜数据,支持总榜和好友榜。

using TapSDK.Leaderboard;
using UnityEngine;

// 获取排行榜数据(异步方法,需要在async方法中调用)
public async void LoadLeaderboardData()
{
string leaderboardId = "your_leaderboard_id";
string leaderboardCollection = "public"; // 总榜:"public",好友榜:"friends"
string nextPageToken = null; // 首次请求传null,加载更多时传上次请求返回的nextPage
string periodToken = null; // 时间周期标识,传null使用当前周期

try
{
var scores = await TapTapLeaderboard.LoadLeaderboardScores(
leaderboardId,
leaderboardCollection,
nextPageToken,
periodToken);

// 获取成功
if (scores != null)
{
// 保存分页标识用于加载更多数据
nextPageToken = scores.nextPage;

// 处理分数数据
if (scores.scores != null && scores.scores.Count > 0)
{
Debug.Log($"Loaded {scores.scores.Count} scores");

// 遍历排行榜数据
for (int i = 0; i < scores.scores.Count; i++)
{
var score = scores.scores[i];
Debug.Log($"Rank {score.rank}: {score.user?.name} - Score: {score.score}");
}
}
else
{
Debug.Log("No scores found for this leaderboard");
}
}
}
catch (TapException e)
{
// 获取失败,根据错误码进行处理
Debug.LogError($"Load leaderboard scores failed, code: {e.code}, message: {e.message}");

// 可以根据错误码进行特殊处理
if (e.code == 500102)
{
// 用户未登录,引导登录
Debug.LogWarning("User not logged in, please login first");
}
}
}

获取当前用户分数

获取当前登录用户在指定排行榜的分数和排名。

using TapSDK.Leaderboard;
using UnityEngine;

// 获取当前用户分数(异步方法,需要在async方法中调用)
public async void LoadCurrentPlayerScore()
{
string leaderboardId = "your_leaderboard_id";
string leaderboardCollection = "public"; // 总榜,使用"friends"获取好友榜
string periodToken = null; // 时间周期标识,默认为当前周期

try
{
var result = await TapTapLeaderboard.LoadCurrentPlayerLeaderboardScore(
leaderboardId,
leaderboardCollection,
periodToken);

// 获取成功
if (result.currentUserScore != null)
{
var userScore = result.currentUserScore;
Debug.Log($"Current player score: {userScore.score}, rank: {userScore.rank}");
// 处理用户分数和排名
}
else
{
Debug.Log("User has no score on this leaderboard");
}
}
catch (TapException e)
{
// 获取失败
Debug.LogError($"Load current player score failed, code: {e.code}, message: {e.message}");
}
}

获取用户相近分数

查询当前用户相近的其他用户成绩(上下X位)。

using TapSDK.Leaderboard;
using UnityEngine;

// 获取用户相近分数(异步方法,需要在async方法中调用)
public async void LoadPlayerCenteredScores()
{
string leaderboardId = "your_leaderboard_id";
string leaderboardCollection = "friends"; // 好友榜
string periodToken = "weekly"; // 时间周期标识
int maxCount = 10; // 最大返回数量

try
{
var result = await TapTapLeaderboard.LoadPlayerCenteredScores(
leaderboardId,
leaderboardCollection,
periodToken,
maxCount);

// 获取成功
if (result.scores != null)
{
Debug.Log($"Loaded {result.scores.Count} player centered scores");
// 处理相近用户分数数据
foreach (var score in result.scores)
{
Debug.Log($"Player: {score.user.name}, Score: {score.score}, Rank: {score.rank}");
}
}
}
catch (TapException e)
{
// 获取失败
Debug.LogError($"Load player centered scores failed, code: {e.code}, message: {e.message}");
}
}

错误处理

排行榜 SDK 的所有异步操作都通过 TapTapLeaderboardResponseCallback 返回结果,开发者需要在 onFailure 方法中处理错误情况。

错误码说明

错误码错误类型描述
500000排行榜周期已过期提示用户排行榜周期已结束,引导查看其他周期
500001排行榜 ID 未找到检查排行榜ID是否正确,提示用户排行榜不存在
500002排行榜参数错误leaderboard_id 与 client_id 不匹配
500101当前用户未授权获取此产品(服务)提示用户权限不足,联系管理员
500102用户未登录引导用户登录
500199未知错误通用错误处理,建议重试或联系技术支持

错误处理示例

using TapSDK.Leaderboard;
using UnityEngine;

// 示例:获取排行榜数据并处理错误(异步方法,需要在async方法中调用)
public async void LoadLeaderboardWithErrorHandling()
{
try
{
var data = await TapTapLeaderboard.LoadLeaderboardScores(
"your_leaderboard_id",
"public",
null,
null);

// 处理成功结果
Debug.Log($"Successfully loaded {data.scores?.Count ?? 0} scores");
}
catch (TapException e)
{
// 根据错误码处理不同的错误情况
switch (e.code)
{
case 500000:
// 排行榜周期已过期
Debug.LogWarning("排行榜周期已结束,请查看其他周期排行榜");
break;
case 500001:
// 排行榜 ID 未找到
Debug.LogError("排行榜不存在,请检查排行榜ID");
break;
case 500002:
// 排行榜参数错误
Debug.LogError("参数错误,请检查输入参数");
break;
case 500101:
// 用户未授权
Debug.LogError("权限不足,请联系管理员");
break;
case 500102:
// 用户未登录
Debug.LogWarning("用户未登录,需要引导用户登录");
// ShowLoginDialog();
break;
case 500199:
// 未知错误
Debug.LogError("未知错误,请稍后重试");
break;
default:
// 其他错误
Debug.LogError($"操作失败: {e.message}");
break;
}
}
}

最佳实践

1. 内存管理

记得在适当的时机取消注册回调,避免内存泄漏:

using TapSDK.Leaderboard;
using UnityEngine;

public class YourGameManager : MonoBehaviour, ITapTapLeaderboardCallback
{
private void Start()
{
// 注册回调
TapTapLeaderboard.RegisterLeaderboardCallback(this);
}

private void OnDestroy()
{
// 取消注册回调避免内存泄漏
TapTapLeaderboard.UnregisterLeaderboardCallback(this);
}

public void OnLeaderboardResult(int code, string message)
{
// 处理排行榜 SDK 的状态码
// 500102:需要登录
}
}

2. 用户登录检测

在使用排行榜功能前,建议先检查用户登录状态:

using TapSDK.Leaderboard;
using UnityEngine;

public class YourGameManager : MonoBehaviour, ITapTapLeaderboardCallback
{
private void Start()
{
// 注册排行榜回调
TapTapLeaderboard.RegisterLeaderboardCallback(this);
}

public void OnLeaderboardResult(int code, string message)
{
switch (code)
{
case 500102:
// 引导用户登录
ShowLoginDialog();
break;
}
}

private void ShowLoginDialog()
{
// 引导用户进行登录
}
}

3. 完整使用示例

以下是一个完整的排行榜管理器示例,展示了如何在Unity中正确使用排行榜功能:

using System.Collections.Generic;
using TapSDK.Leaderboard;
using UnityEngine;

public class LeaderboardManager : MonoBehaviour, ITapTapLeaderboardCallback
{
[Header("排行榜配置")]
public string leaderboardId = "your_leaderboard_id";

private string nextPageToken = null;
private bool isInitialized = false;

private void Start()
{
InitializeLeaderboard();
}

private void OnDestroy()
{
// 清理资源
if (isInitialized)
{
TapTapLeaderboard.UnregisterLeaderboardCallback(this);
}
}

/// <summary>
/// 初始化排行榜
/// </summary>
public void InitializeLeaderboard()
{
// 注册回调
TapTapLeaderboard.RegisterLeaderboardCallback(this);

// 设置分享回调
var shareCallback = new TapTapLeaderboardShareCallback
{
OnShareSuccessAction = (localPath) =>
{
Debug.Log($"Share success: {localPath}");
},
OnShareFailedAction = (error) =>
{
Debug.LogError($"Share failed: {error?.Message}");
}
};
TapTapLeaderboard.SetShareCallback(shareCallback);

isInitialized = true;
Debug.Log("Leaderboard initialized successfully");
}

/// <summary>
/// 提交玩家分数
/// </summary>
public async void SubmitScore(long score)
{
if (string.IsNullOrEmpty(leaderboardId))
{
Debug.LogError("Leaderboard ID is not set");
return;
}

var scoreItems = new List<SubmitScoresRequest.ScoreItem>
{
new SubmitScoresRequest.ScoreItem
{
LeaderboardId = leaderboardId,
Score = score
}
};

try
{
var result = await TapTapLeaderboard.SubmitScores(scoreItems);
Debug.Log($"Score submitted successfully: {score}");
}
catch (TapException e)
{
Debug.LogError($"Submit score failed: {e.code} - {e.message}");
HandleLeaderboardError(e.code);
}
}

/// <summary>
/// 打开排行榜界面
/// </summary>
public void OpenLeaderboard(bool friendsOnly = false)
{
string collection = friendsOnly ? "friends" : "public";
TapTapLeaderboard.OpenLeaderboard(leaderboardId, collection);
}

/// <summary>
/// 加载排行榜数据
/// </summary>
public async void LoadLeaderboardScores(bool friendsOnly = false, bool loadMore = false)
{
string collection = friendsOnly ? "friends" : "public";
string pageToken = loadMore ? nextPageToken : null;

try
{
var scores = await TapTapLeaderboard.LoadLeaderboardScores(
leaderboardId, collection, pageToken, null);

if (scores?.scores != null && scores.scores.Count > 0)
{
nextPageToken = scores.nextPage;
Debug.Log($"Loaded {scores.scores.Count} scores");

// 处理数据
ProcessLeaderboardScores(scores.scores);
}
else
{
Debug.Log("No scores available");
}
}
catch (TapException e)
{
Debug.LogError($"Load leaderboard failed: {e.code} - {e.message}");
HandleLeaderboardError(e.code);
}
}

/// <summary>
/// 处理排行榜数据
/// </summary>
private void ProcessLeaderboardScores(List<Score> scores)
{
foreach (var score in scores)
{
Debug.Log($"Rank: {score.rank}, Player: {score.user?.name}, Score: {score.score}");
}
}

/// <summary>
/// 处理错误码
/// </summary>
private void HandleLeaderboardError(int errorCode)
{
switch (errorCode)
{
case 500102:
Debug.LogWarning("User not logged in");
// 这里可以触发登录流程
break;
case 500001:
Debug.LogError("Leaderboard not found");
break;
default:
Debug.LogError($"Leaderboard error: {errorCode}");
break;
}
}

/// <summary>
/// 排行榜回调处理
/// </summary>
public void OnLeaderboardResult(int code, string message)
{
Debug.Log($"Leaderboard callback: {code} - {message}");
HandleLeaderboardError(code);
}
}