UE5 SDK 开发指南
本文介绍如何在游戏中加入排行榜系统。
当前 UE5 插件仅支持 Android、iOS,不支持 Tap PC。
集成前准备
- 参考 开发者中心配置 创建应用、配置包名与签名证书
- 参考 UE5 集成指南完成 TapSDK 基础接入
- 排行榜模块依赖于 TapTap 登录模块,开发者接入前应先完成 TapTap 登录的接入
在开发者中心创建排行榜并记录排行榜 ID,等待初始化和登录成功后再调用排行榜接口。
权限说明
Android 权限说明
该模块在 Android 平台需要如下权限:
| 权限 | 使用目的 | 权限申请时机 |
|---|---|---|
| 网络权限 | 用于访问网络数据 | 用户首次使用该功能时会申请权限 |
| 网络状态权限 | 用于检查网络连接状态(如 Wi-Fi 或移动数据是否可用) | 用户首次使用该功能时会申请权限 |
| 写入外部存储权限 | 用于保存排行榜分享图片到设备存储 | 用户首次使用分享功能时会申请权限 |
该模块将在 Android 应用中添加如下权限:
<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" />
iOS 权限说明
该模块在 iOS 平台需要如下权限:
| 权限 | 使用目的 | 权限申请时机 |
|---|---|---|
| 相册权限 | 保存图片到相册 | 用户首次使用该功能时会申请权限 |
在 Project Settings > Platforms > iOS > Additional Plist Data 中添加以下内容,并替换授权文案:
<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问相册以保存图片</string>
SDK 获取
参考 UE5 集成指南完成 TapCore 基础接入。再从下载页下载相同版本的 TapLogin、TapProfile、TapLeaderboard,放入项目的 Plugins 目录并启用。
添加模块依赖
在游戏模块的 <GameModule>.Build.cs 中添加:
PublicDependencyModuleNames.AddRange(new string[]
{
"TapCore",
"TapLogin",
"TapProfile",
"TapLeaderboard",
});
修改 Build.cs 后重新生成项目文件并编译。
注册排行榜事件回调
排行榜 SDK 提供事件回调接口,用于处理排行榜相关事件,如用户未登录等状态。
// LeaderboardCallbackHandler.h:用于排行榜事件和分享回调
#pragma once
#include "TapLeaderboard.h"
#include "LeaderboardCallbackHandler.generated.h"
UCLASS()
class ULeaderboardCallbackHandler : public UObject,
public ITapLeaderboardCallback, public ITapLeaderboardShareCallback
{
GENERATED_BODY()
public:
virtual void OnLeaderboardResult_Implementation(int32 Code, const FString& Message) override
{
if (Code == 500102)
{
// 显示登录入口,登录成功后由玩家重新打开榜单。
}
UE_LOG(LogTemp, Log, TEXT("Leaderboard event [%d]: %s"), Code, *Message);
}
// 分享事件的实现见下方“设置分享回调”
virtual void OnShareSuccess_Implementation(const FString& LocalPath) override;
virtual void OnShareFailed_Implementation(const FString& ErrorMessage) override;
};
在接收结果的游戏对象类中声明成员:
UPROPERTY()
TObjectPtr<ULeaderboardCallbackHandler> LeaderboardHandler;
// 初始化和登录成功后,在游戏对象的成员函数中创建并注册
LeaderboardHandler = NewObject<ULeaderboardCallbackHandler>(this);
TScriptInterface<ITapLeaderboardCallback> Callback;
Callback.SetObject(LeaderboardHandler.Get());
Callback.SetInterface(Cast<ITapLeaderboardCallback>(LeaderboardHandler.Get()));
UTapLeaderboard::RegisterLeaderboardCallback(Callback);
// 不再监听时,使用同一回调对象取消注册
// UTapLeaderboard::UnregisterLeaderboardCallback(Callback);
用上述 UPROPERTY 成员持有处理对象,直到停止事件和分享监听。下方数据请求直接传入 Delegate,不依赖该对象或事件回调注册。
设置分享功能
设置分享回调后,排行榜H5页面会显示分享按钮,用户点击分享按钮后,排行榜截图会保存到本地,并触发分享成功回调。开发者可以根据自己的需要判断是否实现分享失败的回调方法。
配置 FileProvider
在 Android 平台向外部应用分享排行榜图片时,需配置 FileProvider。通过项目的 Android UPL 或构建配置,将以下内容加入 AndroidManifest.xml 的 application 节点:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
同时将以下资源打包为 res/xml/file_paths.xml:
<paths>
<files-path name="shared_images" path="shared_images/" />
</paths>
调用 getUriForFile 时,authority 使用当前包名加 .fileprovider。如项目已有 FileProvider,可在其路径资源中添加上述目录,并使用该 provider 的 authority,无需重复声明。
设置分享回调
分享回调需实现 ITapLeaderboardShareCallback 接口。
void ULeaderboardCallbackHandler::OnShareSuccess_Implementation(const FString& LocalPath)
{
// 处理排行榜分享图片保存成功
// LocalPath 为本地图片路径,可用于分享到系统
UE_LOG(LogTemp, Log, TEXT("Share image saved: %s"), *LocalPath);
}
void ULeaderboardCallbackHandler::OnShareFailed_Implementation(const FString& ErrorMessage)
{
// 处理分享失败(可选实现)
UE_LOG(LogTemp, Warning, TEXT("Share failed: %s"), *ErrorMessage);
}
// 设置分享回调(在打开排行榜前调用,LeaderboardHandler 需实现分享回调接口)
TScriptInterface<ITapLeaderboardShareCallback> ShareCallback;
ShareCallback.SetObject(LeaderboardHandler.Get());
ShareCallback.SetInterface(Cast<ITapLeaderboardShareCallback>(LeaderboardHandler.Get()));
UTapLeaderboard::SetShareCallback(ShareCallback);
// 不再接收分享结果时,传入空接口
// UTapLeaderboard::SetShareCallback(TScriptInterface<ITapLeaderboardShareCallback>{});
打开排行榜页面
打开排行榜H5页面对话框,支持总榜和好友榜两种类型。
如果排行榜页面显示不完整、按钮被遮挡或布局方向异常,请先确认 TapSDK 初始化时的 ScreenOrientation 参数是否与游戏实际屏幕方向一致。
#include "TapLeaderboard.h"
FString LeaderboardId = TEXT("your_leaderboard_id");
// 打开总榜
UTapLeaderboard::OpenLeaderboard(LeaderboardId, TEXT("public"));
// 打开好友榜(登录时需申请 UserFriends 授权)
UTapLeaderboard::OpenLeaderboard(LeaderboardId, TEXT("friends"));
打开用户个人资料
展示指定用户的个人资料对话框,传入用户的openId。
// 使用 OpenId 打开用户个人资料
UTapLeaderboard::ShowTapUserProfile(TEXT("user_open_id"), TEXT(""));
// 或使用 UnionId,OpenId 和 UnionId 二选一,另一个传空字符串
UTapLeaderboard::ShowTapUserProfile(TEXT(""), TEXT("user_union_id"));
提交分数
批量提交用户排行榜分数,一次最多提交 5 个分数。
#include "TapLeaderboard.h"
FTapLeaderboardScoreItem ScoreItem;
ScoreItem.LeaderboardId = TEXT("your_leaderboard_id");
ScoreItem.Score = 1000;
FTapLeaderboardScoreItem AnotherScoreItem;
AnotherScoreItem.LeaderboardId = TEXT("another_leaderboard_id");
AnotherScoreItem.Score = 2000;
TArray<FTapLeaderboardScoreItem> Scores{ScoreItem, AnotherScoreItem};
UTapLeaderboard::SubmitScores(
Scores,
FTapLeaderboardSubmitCallback::CreateLambda([](const FTapSubmitScoresResult& Result)
{
for (const FTapSubmitScoreResultItem& Item : Result.Items)
{
UE_LOG(LogTemp, Log, TEXT("Board: %s, score: %lld, new best: %d"),
*Item.LeaderboardId, Item.RawScore, Item.bNewBest);
}
}),
FTapLeaderboardErrorCallback::CreateLambda([](const FString& Operation, int32 Code, const FString& Message)
{
UE_LOG(LogTemp, Warning, TEXT("%s failed [%d]: %s"), *Operation, Code, *Message);
if (Code == 500102)
{
// 引导用户登录,登录成功后再重试。
}
}));
获取排行榜数据
分页获取排行榜数据,支持总榜和好友榜。
FString LeaderboardId = TEXT("your_leaderboard_id");
FString Collection = TEXT("public"); // 总榜:public,好友榜:friends
FString NextPage = TEXT(""); // 首次请求传空字符串,加载更多时传上次返回的 NextPage
FString PeriodToken = TEXT(""); // 空字符串表示当前周期,连续分页使用同一周期
// 查询历史周期时,使用返回的 Leaderboard.AvailablePeriods 中的 PeriodToken。
UTapLeaderboard::LoadLeaderboardScores(
LeaderboardId, Collection, NextPage, PeriodToken,
FTapLeaderboardScoresCallback::CreateLambda([](const FTapLeaderboardScoresResult& Result)
{
// 加载下一页时,使用 Result.NextPage 和 Result.Leaderboard.Period.PeriodToken。
// Result.NextPage 为空时停止分页。
for (const FTapLeaderboardScore& Score : Result.Scores)
{
UE_LOG(LogTemp, Log, TEXT("Rank: %lld, player: %s, score: %s"),
Score.Rank, *Score.User.Name, *Score.ScoreDisplay);
}
}),
FTapLeaderboardErrorCallback::CreateLambda([](const FString& Operation, int32 Code, const FString& Message)
{
UE_LOG(LogTemp, Warning, TEXT("%s failed [%d]: %s"), *Operation, Code, *Message);
if (Code == 500102)
{
// 引导用户登录,登录成功后再重试。
}
}));
获取当前用户分数
获取当前登录用户在指定排行榜的分数和排名。
FString LeaderboardId = TEXT("your_leaderboard_id");
FString Collection = TEXT("public"); // 总榜:public,好友榜:friends
FString PeriodToken = TEXT(""); // 空字符串表示当前周期
UTapLeaderboard::LoadCurrentPlayerLeaderboardScore(
LeaderboardId, Collection, PeriodToken,
FTapLeaderboardCurrentScoreCallback::CreateLambda([](const FTapUserScoreResult& Result)
{
// CurrentUserScore 是值类型,无需判空;默认值不能用于判断用户是否已提交成绩。
const FTapLeaderboardScore& Score = Result.CurrentUserScore;
UE_LOG(LogTemp, Log, TEXT("Rank: %lld, score: %lld"), Score.Rank, Score.Score);
}),
FTapLeaderboardErrorCallback::CreateLambda([](const FString& Operation, int32 Code, const FString& Message)
{
UE_LOG(LogTemp, Warning, TEXT("%s failed [%d]: %s"), *Operation, Code, *Message);
if (Code == 500102)
{
// 引导用户登录,登录成功后再重试。
}
}));
获取用户相近分数
查询当前用户附近的其他用户成绩。
FString LeaderboardId = TEXT("your_leaderboard_id");
FString Collection = TEXT("public"); // 总榜:public,好友榜:friends
FString PeriodToken = TEXT(""); // 空字符串表示当前周期
int32 MaxCount = 10; // 最大返回数量
UTapLeaderboard::LoadPlayerCenteredScores(
LeaderboardId, Collection, PeriodToken, MaxCount,
FTapLeaderboardScoresCallback::CreateLambda([](const FTapLeaderboardScoresResult& Result)
{
for (const FTapLeaderboardScore& Score : Result.Scores)
{
UE_LOG(LogTemp, Log, TEXT("Rank: %lld, player: %s, score: %s"),
Score.Rank, *Score.User.Name, *Score.ScoreDisplay);
}
}),
FTapLeaderboardErrorCallback::CreateLambda([](const FString& Operation, int32 Code, const FString& Message)
{
UE_LOG(LogTemp, Warning, TEXT("%s failed [%d]: %s"), *Operation, Code, *Message);
if (Code == 500102)
{
// 引导用户登录,登录成功后再重试。
}
}));
错误处理
排行榜 SDK 的数据请求通过 FTapLeaderboardErrorCallback 返回错误,开发者可根据错误码进行处理。
错误码说明
| 错误码 | 错误类型 | 描述 |
|---|---|---|
| 500000 | 排行榜周期已过期 | 提示用户排行榜周期已结束,引导查看其他周期 |
| 500001 | 排行榜 ID 未找到 | 检查排行榜ID是否正确,提示用户排行榜不存在 |
| 500002 | 排行榜参数错误 | leaderboard_id 与 client_id 不匹配 |
| 500101 | 当前用户未授权获取此产品(服务) | 提示用户权限不足,联系管理员 |
| 500102 | 用户未登录 | 引导用户登录 |
| 500199 | 未知错误 | 通用错误处理,建议重试或联系技术支持 |
UE5 未初始化或平台不可用时返回 -1,具体原因见 Message。
错误处理示例
可将以下 Delegate 作为请求的失败回调参数:
auto OnError = FTapLeaderboardErrorCallback::CreateLambda([](const FString& Operation, int32 Code, const FString& Message)
{
UE_LOG(LogTemp, Warning, TEXT("%s failed [%d]: %s"), *Operation, Code, *Message);
if (Code == 500102)
{
// 引导用户登录,登录成功后再重试。
}
});