最佳实践
本文介绍 TapTap 排行榜的最佳实践。共用说明适用于各技术栈,代码示例可按 Unity、Android、iOS 和 UE5 切换。接入前请先完成对应的开发指南。
1. 内存管理
记得在适当的时机取消注册回调,避免内存泄漏:
- Unity
- Android Java
- Android Kotlin
- iOS Swift
- iOS Objective-C
- UE5
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:需要登录
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// 取消注册回调
TapTapLeaderboard.unregisterLeaderboardCallback(callback);
}
override fun onDestroy() {
super.onDestroy()
// 取消注册回调
TapTapLeaderboard.unregisterLeaderboardCallback(callback)
}
示例中的回调处理类定义见 iOS 开发指南和设置分享功能。LeaderboardDelegate 为游戏自定义协议。
import TapTapLeaderboardSDK
class YourViewController: UIViewController {
private var leaderboardCallback: LeaderboardCallbackHandler?
private var shareCallback: ShareCallbackHandler?
override func viewDidLoad() {
super.viewDidLoad()
setupLeaderboard()
}
private func setupLeaderboard() {
// 注册回调
leaderboardCallback = LeaderboardCallbackHandler()
shareCallback = ShareCallbackHandler()
TapTapLeaderboard.registerLeaderboardCallback(callback: leaderboardCallback!)
TapTapLeaderboard.setShareCallback(callback: shareCallback!)
}
deinit {
// 取消注册回调避免内存泄漏
if let callback = leaderboardCallback {
TapTapLeaderboard.unregisterLeaderboardCallback(callback: callback)
}
print("YourViewController deinitialized, callbacks cleaned up")
}
}
// 推荐使用弱引用避免循环引用
class LeaderboardManager {
weak var delegate: LeaderboardDelegate?
private var callbacks: [TapTapLeaderboardCallback] = []
func registerCallback(_ callback: TapTapLeaderboardCallback) {
callbacks.append(callback)
TapTapLeaderboard.registerLeaderboardCallback(callback: callback)
}
func cleanup() {
for callback in callbacks {
TapTapLeaderboard.unregisterLeaderboardCallback(callback: callback)
}
callbacks.removeAll()
}
}
示例中的回调处理类定义见 iOS 开发指南和设置分享功能。LeaderboardDelegate 为游戏自定义协议。
#import <TapTapLeaderboardSDK/TapTapLeaderboardSDK-Swift.h>
@interface YourViewController : UIViewController
@property (nonatomic, strong) LeaderboardCallbackHandler *leaderboardCallback;
@property (nonatomic, strong) ShareCallbackHandler *shareCallback;
@end
@implementation YourViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setupLeaderboard];
}
- (void)setupLeaderboard {
// 注册回调
self.leaderboardCallback = [[LeaderboardCallbackHandler alloc] init];
self.shareCallback = [[ShareCallbackHandler alloc] init];
[TapTapLeaderboard registerLeaderboardCallbackWithCallback:self.leaderboardCallback];
[TapTapLeaderboard setShareCallbackWithCallback:self.shareCallback];
}
- (void)dealloc {
// 取消注册回调避免内存泄漏
if (self.leaderboardCallback) {
[TapTapLeaderboard unregisterLeaderboardCallbackWithCallback:self.leaderboardCallback];
}
NSLog(@"YourViewController dealloc, callbacks cleaned up");
}
@end
// 推荐使用弱引用避免循环引用
@interface LeaderboardManager : NSObject
@property (nonatomic, weak) id<LeaderboardDelegate> delegate;
@property (nonatomic, strong) NSMutableArray<id<TapTapLeaderboardCallback>> *callbacks;
@end
@implementation LeaderboardManager
- (instancetype)init {
self = [super init];
if (self) {
_callbacks = [[NSMutableArray alloc] init];
}
return self;
}
- (void)registerCallback:(id<TapTapLeaderboardCallback>)callback {
[self.callbacks addObject:callback];
[TapTapLeaderboard registerLeaderboardCallbackWithCallback:callback];
}
- (void)cleanup {
for (id<TapTapLeaderboardCallback> callback in self.callbacks) {
[TapTapLeaderboard unregisterLeaderboardCallbackWithCallback:callback];
}
[self.callbacks removeAllObjects];
}
@end
用 UPROPERTY 成员持有回调对象,直到请求完成。不再监听时取消注册并清除分享回调:
// 取消注册排行榜事件回调
TScriptInterface<ITapLeaderboardCallback> Callback;
Callback.SetObject(LeaderboardHandler.Get());
Callback.SetInterface(Cast<ITapLeaderboardCallback>(LeaderboardHandler.Get()));
UTapLeaderboard::UnregisterLeaderboardCallback(Callback);
// 清除分享回调
UTapLeaderboard::SetShareCallback(TScriptInterface<ITapLeaderboardShareCallback>{});
2. 用户登录检测
在使用排行榜功能前,建议先检查用户登录状态:
- Unity
- Android Java
- Android Kotlin
- iOS Swift
- iOS Objective-C
- UE5
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()
{
// 引导用户进行登录
}
}
import com.taptap.sdk.leaderboard.callback.TapTapLeaderboardCallback;
// 在排行榜回调中处理未登录状态
TapTapLeaderboardCallback callback = new TapTapLeaderboardCallback() {
@Override
public void onLeaderboardResult(int code, String message) {
if (code == 500102) {
// 引导用户登录
}
}
};
// 在排行榜回调中处理未登录状态
val callback = object : TapTapLeaderboardCallback {
override fun onLeaderboardResult(code: Int, message: String) {
when (code) {
500102 -> {
// 引导用户登录
}
}
}
}
import TapTapLeaderboardSDK
// 在排行榜回调中处理未登录状态
class LoginCheckHandler: NSObject, TapTapLeaderboardCallback {
func onLeaderboardResult(code: Int, message: String) {
if code == 500102 {
// 引导用户登录
showLoginDialog()
}
}
private func showLoginDialog() {
DispatchQueue.main.async {
// 显示登录对话框或跳转到登录页面
let alert = UIAlertController(
title: "需要登录",
message: "请先登录TapTap账号才能使用排行榜功能",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "立即登录", style: .default) { _ in
// 调用 TapTap 登录
// TapTapLogin.startTapTapLogin(...)
})
alert.addAction(UIAlertAction(title: "稍后再说", style: .cancel))
// 显示警告框
if let topController = UIApplication.shared.keyWindow?.rootViewController {
var presentedController = topController
while let controller = presentedController.presentedViewController {
presentedController = controller
}
presentedController.present(alert, animated: true)
}
}
}
}
#import <TapTapLeaderboardSDK/TapTapLeaderboardSDK-Swift.h>
// 在排行榜回调中处理未登录状态
@interface LoginCheckHandler : NSObject <TapTapLeaderboardCallback>
@end
@implementation LoginCheckHandler
- (void)onLeaderboardResultWithCode:(NSInteger)code message:(NSString *)message {
if (code == 500102) {
// 引导用户登录
[self showLoginDialog];
}
}
- (void)showLoginDialog {
dispatch_async(dispatch_get_main_queue(), ^{
// 显示登录对话框或跳转到登录页面
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"需要登录"
message:@"请先登录TapTap账号才能使用排行榜功能"
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"立即登录"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
// 调用 TapTap 登录
// [TapTapLogin startTapTapLogin:...];
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"稍后再说"
style:UIAlertActionStyleCancel
handler:nil]];
// 显示警告框
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
[topController presentViewController:alert animated:YES completion:nil];
});
}
@end
OnLeaderboardResult 是 BlueprintNativeEvent,C++ 中需重写 OnLeaderboardResult_Implementation 方法:
#include "TapLeaderboard.h"
// 在排行榜回调中处理未登录状态
UCLASS()
class ULeaderboardCallbackHandler : public UObject, public ITapLeaderboardCallback
{
GENERATED_BODY()
public:
void OnLeaderboardResult_Implementation(int32 Code, const FString& Message) override
{
if (Code == 500102)
{
// 引导用户登录
}
}
};
回调注册方式见 UE5 开发指南。
3. 完整使用示例
以下示例展示了如何正确使用排行榜功能:
- Unity
- Android Java
- Android Kotlin
- iOS Swift
- iOS Objective-C
- UE5
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);
}
}
以下代码放在 Activity 的对应成员函数中:初始化和登录成功后注册回调、设置分享回调,再加载数据和打开榜单;Activity 销毁时取消注册。分享功能的 FileProvider 配置见 Android 开发指南。
import com.taptap.sdk.leaderboard.androidx.TapTapLeaderboard;
import com.taptap.sdk.leaderboard.callback.TapTapLeaderboardCallback;
// 注册排行榜事件回调
TapTapLeaderboardCallback callback = new TapTapLeaderboardCallback() {
@Override
public void onLeaderboardResult(int code, String message) {
// 处理排行榜事件
switch (code) {
case 500102:
// 用户未登录,需要引导用户登录
// showLoginDialog();
break;
// 处理其他事件
default:
Log.d("Leaderboard", "code: " + code + ", message: " + message);
break;
}
}
};
TapTapLeaderboard.registerLeaderboardCallback(callback);
import com.taptap.sdk.leaderboard.androidx.TapTapLeaderboard;
import com.taptap.sdk.leaderboard.callback.TapTapLeaderboardShareCallback;
// 设置分享回调
TapTapLeaderboard.setShareCallback(new TapTapLeaderboardShareCallback() {
@Override
public void onShareSuccess(String localPath) {
// 处理排行榜分享图片保存成功
// localPath 为本地图片路径,可用于分享到系统
// shareImageToSystem(localPath);
}
@Override
public void onShareFailed(Throwable error) {
// 处理分享失败(可选实现)
// 开发者可以根据需要选择是否实现此方法
Log.e("Leaderboard", "分享失败", error);
}
});
import com.taptap.sdk.leaderboard.androidx.TapTapLeaderboard;
import com.taptap.sdk.leaderboard.data.request.LeaderboardCollection;
import com.taptap.sdk.leaderboard.callback.TapTapLeaderboardResponseCallback;
import com.taptap.sdk.leaderboard.data.response.LeaderboardScoresResponse;
TapTapLeaderboard.loadLeaderboardScores(
"your_leaderboard_id", // 排行榜ID
LeaderboardCollection.PUBLIC, // 总榜
null, // nextPage - 首次请求传null
null, // periodToken - 时间周期标识
new TapTapLeaderboardResponseCallback<LeaderboardScoresResponse>() {
@Override
public void onSuccess(LeaderboardScoresResponse data) {
// 获取成功
// 根据 data.getScores() 渲染榜单。
String nextPage = data.getNextPage(); // 用于下一页请求
}
@Override
public void onFailure(int code, String message) {
// 获取失败
Log.e("Leaderboard", "获取排行榜数据失败: code=" + code + ", message=" + message);
}
}
);
import com.taptap.sdk.leaderboard.androidx.TapTapLeaderboard;
// 打开总榜
String leaderboardId = "your_leaderboard_id";
String collection = "public";
TapTapLeaderboard.openLeaderboard(
this, // Activity
leaderboardId,
collection
);
@Override
protected void onDestroy() {
super.onDestroy();
// 取消注册回调
TapTapLeaderboard.unregisterLeaderboardCallback(callback);
}
以下代码放在 Activity 的对应成员函数中:初始化和登录成功后注册回调、设置分享回调,再加载数据和打开榜单;Activity 销毁时取消注册。分享功能的 FileProvider 配置见 Android 开发指南。
// 在排行榜回调中处理未登录状态
val callback = object : TapTapLeaderboardCallback {
override fun onLeaderboardResult(code: Int, message: String) {
when (code) {
500102 -> {
// 引导用户登录
}
}
}
}
TapTapLeaderboard.registerLeaderboardCallback(callback)
import com.taptap.sdk.leaderboard.androidx.TapTapLeaderboard
// 设置分享回调
TapTapLeaderboard.setShareCallback(object : TapTapLeaderboardShareCallback {
override fun onShareSuccess(localPath: String) {
// 处理排行榜分享图片保存成功
// localPath 为本地图片路径,可用于分享到系统
// shareImageToSystem(localPath)
}
override fun onShareFailed(error: Throwable) {
// 处理分享失败(可选实现)
// 开发者可以根据需要选择是否实现此方法
Log.e("Leaderboard", "分享失败", error)
}
})
import com.taptap.sdk.leaderboard.androidx.TapTapLeaderboard
import com.taptap.sdk.leaderboard.data.request.LeaderboardCollection
import com.taptap.sdk.leaderboard.callback.TapTapLeaderboardResponseCallback
import com.taptap.sdk.leaderboard.data.response.LeaderboardScoresResponse
TapTapLeaderboard.loadLeaderboardScores(
leaderboardId = "your_leaderboard_id", // 排行榜ID
leaderboardCollection = LeaderboardCollection.PUBLIC, // 总榜
nextPage = null, // 首次请求传null
callback = object : TapTapLeaderboardResponseCallback<LeaderboardScoresResponse> {
override fun onSuccess(data: LeaderboardScoresResponse) {
// 获取成功
val scores = data.scores
val nextPage = data.nextPage // 用于下一页请求
}
override fun onFailure(code: Int, message: String) {
// 获取失败
Log.e("Leaderboard", "获取排行榜数据失败: code=$code, message=$message")
}
}
)
import com.taptap.sdk.leaderboard.androidx.TapTapLeaderboard
// 打开总榜
val leaderboardId = "your_leaderboard_id"
val collection = "public"
TapTapLeaderboard.openLeaderboard(
activity = this, // Activity
leaderboardId = leaderboardId,
collection = collection
)
override fun onDestroy() {
super.onDestroy()
// 取消注册回调
TapTapLeaderboard.unregisterLeaderboardCallback(callback)
}
import TapTapLeaderboardSDK
import TapTapCoreSDK
// 排行榜代理协议
protocol LeaderboardManagerDelegate: AnyObject {
func didLoadScores(_ scores: [TapTapLeaderboardScore])
func didFailWithError(_ error: Error)
func didSubmitScore(_ success: Bool)
}
class LeaderboardManager: NSObject {
// MARK: - Properties
static let shared = LeaderboardManager()
weak var delegate: LeaderboardManagerDelegate?
private let leaderboardId = "your_leaderboard_id"
private var nextPageToken: String?
private var isInitialized = false
// 回调处理器
private var leaderboardCallback: LeaderboardCallbackHandler?
private var shareCallback: ShareCallbackHandler?
// MARK: - Initialization
override init() {
super.init()
setupLeaderboard()
}
deinit {
cleanup()
}
// MARK: - Setup
private func setupLeaderboard() {
// 创建回调处理器
leaderboardCallback = LeaderboardCallbackHandler()
shareCallback = ShareCallbackHandler()
// 注册回调
if let callback = leaderboardCallback {
TapTapLeaderboard.registerLeaderboardCallback(callback: callback)
}
if let shareHandler = shareCallback {
TapTapLeaderboard.setShareCallback(callback: shareHandler)
}
isInitialized = true
print("Leaderboard initialized successfully")
}
// MARK: - Public Methods
/// 提交玩家分数
func submitScore(_ score: Int64) {
let scoreItem = TapTapLeaderboardScoreItem(leaderboardId: leaderboardId, score: score)
let callback = SubmitScoreCallback { [weak self] success in
self?.delegate?.didSubmitScore(success)
}
TapTapLeaderboard.submitScores(scores: [scoreItem], callback: callback)
}
/// 打开排行榜界面
func openLeaderboard(friendsOnly: Bool = false) {
let collection = friendsOnly ? TapTapLeaderboardCollection.friends : TapTapLeaderboardCollection.public
TapTapLeaderboard.openLeaderboard(leaderboardId: leaderboardId, collection: collection)
}
/// 加载排行榜数据
func loadLeaderboardScores(friendsOnly: Bool = false, loadMore: Bool = false) {
let collection = friendsOnly ? TapTapLeaderboardCollection.friends : TapTapLeaderboardCollection.public
let pageToken = loadMore ? nextPageToken : nil
let callback = LoadScoresCallback { [weak self] response in
if let scores = response?.scores {
self?.nextPageToken = response?.nextPage
self?.delegate?.didLoadScores(scores)
}
} onFailure: { [weak self] error in
self?.delegate?.didFailWithError(error)
}
TapTapLeaderboard.loadLeaderboardScores(
leaderboardId: leaderboardId,
collection: collection,
nextPage: pageToken,
periodToken: nil,
callback: callback
)
}
/// 打开用户个人资料
func openUserProfile(openId: String) {
TapTapLeaderboard.openUserProfile(openId: openId, unionId: "")
}
// MARK: - Private Methods
private func cleanup() {
if let callback = leaderboardCallback {
TapTapLeaderboard.unregisterLeaderboardCallback(callback: callback)
}
isInitialized = false
print("Leaderboard manager cleaned up")
}
}
// MARK: - Callback Handlers
private class LeaderboardCallbackHandler: NSObject, TapTapLeaderboardCallback {
func onLeaderboardResult(code: Int, message: String) {
switch code {
case 500102:
print("User not logged in, need to login")
// 可以在这里触发登录流程
default:
print("Leaderboard callback: code=\(code), message=\(message)")
}
}
}
private class ShareCallbackHandler: NSObject, TapTapLeaderboardShareCallback {
func onShareSuccess(localPath: String) {
print("Share success: \(localPath)")
// 可以在这里实现系统分享
}
func onShareFailed(error: Error) {
print("Share failed: \(error.localizedDescription)")
}
}
private class SubmitScoreCallback: NSObject, TapTapLeaderboardResponseCallback {
private let onSuccess: (Bool) -> Void
init(onSuccess: @escaping (Bool) -> Void) {
self.onSuccess = onSuccess
super.init()
}
func onSuccess(data: Any) {
onSuccess(true)
}
func onFailure(code: Int, message: String) {
onSuccess(false)
print("Submit score failed: code=\(code), message=\(message)")
}
}
private class LoadScoresCallback: NSObject, TapTapLeaderboardResponseCallback {
private let onSuccess: (TapTapLeaderboardDataResponse?) -> Void
private let onFailure: (Error) -> Void
init(onSuccess: @escaping (TapTapLeaderboardDataResponse?) -> Void,
onFailure: @escaping (Error) -> Void) {
self.onSuccess = onSuccess
self.onFailure = onFailure
super.init()
}
func onSuccess(data: Any) {
if let response = data as? TapTapLeaderboardDataResponse {
onSuccess(response)
} else {
onSuccess(nil)
}
}
func onFailure(code: Int, message: String) {
let error = NSError(domain: "TapLeaderboard", code: code, userInfo: [NSLocalizedDescriptionKey: message])
onFailure(error)
}
}
示例使用的 LeaderboardCallbackHandler、ShareCallbackHandler、SubmitScoresCallback 和 LoadLeaderboardCallback 定义,见 iOS 开发指南。
#import <TapTapLeaderboardSDK/TapTapLeaderboardSDK-Swift.h>
#import <TapTapCoreSDK/TapTapSDK.h>
// 排行榜代理协议
@protocol LeaderboardManagerDelegate <NSObject>
- (void)didLoadScores:(NSArray<TapTapLeaderboardScore *> *)scores;
- (void)didFailWithError:(NSError *)error;
- (void)didSubmitScore:(BOOL)success;
@end
@interface LeaderboardManager : NSObject
@property (nonatomic, weak) id<LeaderboardManagerDelegate> delegate;
@property (nonatomic, strong, readonly) NSString *leaderboardId;
@property (nonatomic, strong) NSString *nextPageToken;
@property (nonatomic, assign) BOOL isInitialized;
// 回调处理器
@property (nonatomic, strong) LeaderboardCallbackHandler *leaderboardCallback;
@property (nonatomic, strong) ShareCallbackHandler *shareCallback;
+ (instancetype)sharedManager;
- (void)submitScore:(long long)score;
- (void)openLeaderboardWithFriendsOnly:(BOOL)friendsOnly;
- (void)loadLeaderboardScoresWithFriendsOnly:(BOOL)friendsOnly loadMore:(BOOL)loadMore;
- (void)openUserProfileWithOpenId:(NSString *)openId;
@end
@implementation LeaderboardManager
+ (instancetype)sharedManager {
static LeaderboardManager *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[LeaderboardManager alloc] init];
});
return instance;
}
- (instancetype)init {
self = [super init];
if (self) {
_leaderboardId = @"your_leaderboard_id";
[self setupLeaderboard];
}
return self;
}
- (void)setupLeaderboard {
// 创建回调处理器
self.leaderboardCallback = [[LeaderboardCallbackHandler alloc] init];
self.shareCallback = [[ShareCallbackHandler alloc] init];
// 注册回调
[TapTapLeaderboard registerLeaderboardCallbackWithCallback:self.leaderboardCallback];
[TapTapLeaderboard setShareCallbackWithCallback:self.shareCallback];
self.isInitialized = YES;
NSLog(@"Leaderboard initialized successfully");
}
- (void)submitScore:(long long)score {
TapTapLeaderboardScoreItem *scoreItem = [[TapTapLeaderboardScoreItem alloc]
initWithLeaderboardId:self.leaderboardId score:score];
SubmitScoresCallback *callback = [[SubmitScoresCallback alloc] init];
[TapTapLeaderboard submitScoresWithScores:@[scoreItem] callback:callback];
}
- (void)openLeaderboardWithFriendsOnly:(BOOL)friendsOnly {
TapTapLeaderboardCollection *collection = friendsOnly ?
[TapTapLeaderboardCollection friends] : [TapTapLeaderboardCollection public];
[TapTapLeaderboard openLeaderboardWithLeaderboardId:self.leaderboardId collection:collection];
}
- (void)loadLeaderboardScoresWithFriendsOnly:(BOOL)friendsOnly loadMore:(BOOL)loadMore {
TapTapLeaderboardCollection *collection = friendsOnly ?
[TapTapLeaderboardCollection friends] : [TapTapLeaderboardCollection public];
NSString *pageToken = loadMore ? self.nextPageToken : nil;
LoadLeaderboardCallback *callback = [[LoadLeaderboardCallback alloc] init];
[TapTapLeaderboard loadLeaderboardScoresWithLeaderboardId:self.leaderboardId
collection:collection
nextPage:pageToken
periodToken:nil
callback:callback];
}
- (void)openUserProfileWithOpenId:(NSString *)openId {
[TapTapLeaderboard openUserProfileWithOpenId:openId unionId:@""];
}
- (void)dealloc {
if (self.leaderboardCallback) {
[TapTapLeaderboard unregisterLeaderboardCallbackWithCallback:self.leaderboardCallback];
}
NSLog(@"Leaderboard manager cleaned up");
}
@end
初始化和登录成功后,注册回调、设置分享回调,再加载数据和打开排行榜。通过 UPROPERTY 成员 LeaderboardHandler 持有处理对象,分享配置见 UE5 开发指南。
#include "TapLeaderboard.h"
// 实现排行榜事件和分享回调
UCLASS()
class ULeaderboardCallbackHandler : public UObject,
public ITapLeaderboardCallback, public ITapLeaderboardShareCallback
{
GENERATED_BODY()
public:
// BlueprintNativeEvent 回调需重写对应的 _Implementation 方法
void OnLeaderboardResult_Implementation(int32 Code, const FString& Message) override
{
if (Code == 500102)
{
// 引导用户登录
}
UE_LOG(LogTemp, Log, TEXT("Leaderboard event [%d]: %s"), Code, *Message);
}
void OnShareSuccess_Implementation(const FString& LocalPath) override
{
// LocalPath 为本地图片路径,可用于分享到系统
UE_LOG(LogTemp, Log, TEXT("Share image saved: %s"), *LocalPath);
}
void OnShareFailed_Implementation(const FString& ErrorMessage) override
{
// 处理分享失败(可选实现)
UE_LOG(LogTemp, Warning, TEXT("Share failed: %s"), *ErrorMessage);
}
};
// 以下代码放在持有 LeaderboardHandler 的游戏对象成员函数中
LeaderboardHandler = NewObject<ULeaderboardCallbackHandler>(this);
// 注册排行榜事件回调
TScriptInterface<ITapLeaderboardCallback> Callback;
Callback.SetObject(LeaderboardHandler.Get());
Callback.SetInterface(Cast<ITapLeaderboardCallback>(LeaderboardHandler.Get()));
UTapLeaderboard::RegisterLeaderboardCallback(Callback);
// 设置分享回调
TScriptInterface<ITapLeaderboardShareCallback> ShareCallback;
ShareCallback.SetObject(LeaderboardHandler.Get());
ShareCallback.SetInterface(Cast<ITapLeaderboardShareCallback>(LeaderboardHandler.Get()));
UTapLeaderboard::SetShareCallback(ShareCallback);
// 获取排行榜数据
FString LeaderboardId = TEXT("your_leaderboard_id");
FString Collection = TEXT("public"); // 总榜:public,好友榜:friends
FString NextPage = TEXT(""); // 首次请求传空字符串
FString PeriodToken = TEXT(""); // 空字符串表示当前周期
// 加载下一页时,使用回调返回的 NextPage 和 Leaderboard.Period.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)
{
// 引导用户登录,登录成功后再重试。
}
}));
// 打开排行榜页面
UTapLeaderboard::OpenLeaderboard(LeaderboardId, Collection);
// 不再监听时取消注册并清除分享回调
// UTapLeaderboard::UnregisterLeaderboardCallback(Callback);
// UTapLeaderboard::SetShareCallback(TScriptInterface<ITapLeaderboardShareCallback>{});
UE5 数据请求通过 CreateLambda 直接传入原生单播 Delegate,仅支持 C++ 调用。同一种异步操作的请求按顺序执行,不同操作的完成顺序不保证。LeaderboardHandler 只接收持续的事件和分享通知,数据请求不依赖它。