数据传输
自定义消息同步
通过常见的象棋对战游戏来描述自定义消息在游戏中的使用方式,流程图如下:

建议游戏类型
通过发送自定义消息进行游戏各端的状态同步,适合不需要高实时性进行数据更新的回合制或简单休闲游戏,例如:
- 棋牌类游戏 - 五子棋、象棋、斗地主、麻将等
- 回合制游戏 - 战棋、策略游戏、回合 制 RPG 等
- 休闲游戏 - 你画我猜、狼人杀、问答游戏等
- 合作游戏 - 简单的多人闯关、合作解谜等
发送自定义消息
该接口 和 更新玩家自定义状态(UpdatePlayerCustomStatus)、更新玩家自定义属性(UpdatePlayerCustomProperties)、更新房间信息(UpdateRoomProperties)共享每秒15次调用限制
开始游戏后,开发者通过将玩家操作数据封装为自定义消息内容同步给其他玩家,每秒最多 15 次,调用示例如下:
- Unity
- Android Java
- Android Kotlin
- iOS Swift
- iOS Objective-C
using TapSDK.OnlineBattle;
try
{
await TapTapOnlineBattle.SendCustomMessage(
customMsg, // 自定义消息,格式由开发者决定,必须是utf8字符串,最大2048字节
customMsgType, // 消息接收者类型。0:房间内所有玩家,不包括发送者;1:发送给指定玩家
playerIds // 当type==1时有效,发送给该字段指定的玩家,最多20个ID
);
LogMessage(" 发送自定义消息 成功 ");
// 房间内对应玩家通过 OnCustomMessageReceived 回调收到通知
}
catch (TapException e)
{
LogMessage($"发送自定义消息 error {e.Code} {e.Message}");
}
import com.taptap.sdk.battle.TapTapBattle;
import com.taptap.sdk.battle.callback.TapBattleCallback;
import com.taptap.sdk.kit.internal.exception.TapError;
TapTapBattle.sendCustomMessage(customMsg, customMsgType, playerIds, new TapBattleCallback<Void>() {
@Override
public void onSuccess(Void result) {
Log.d(TAG, "发送自定义消息 成功");
// 房间内对应玩家通过 onCustomMessageReceived 回调收到通知
}
@Override
public void onError(TapError error) {
Log.e(TAG, "发送自定义消息 error " + error.code + " " + error.message);
}
});
import com.taptap.sdk.battle.TapTapBattle
import com.taptap.sdk.battle.callback.TapBattleCallback
import com.taptap.sdk.kit.internal.exception.TapError
import android.util.Log
TapTapBattle.sendCustomMessage(customMsg, customMsgType, playerIds, object : TapBattleCallback<Void> {
override fun onSuccess(result: Void?) {
Log.d(TAG, "发送自定义消息 成功")
// 房间内对应玩家通过 onCustomMessageReceived 回调收到通知
}
override fun onError(error: TapError) {
Log.e(TAG, "发送自定义消息 error ${error.code} ${error.message}")
}
})
import TapTapSDK
TapTapBattle.sendCustomMessage(customMsg: customMsg, customMsgType: customMsgType, playerIds: playerIds) { error in
if let error = error {
print("发送自定义消息 error \(error.code) \(error.message)")
} else {
print("发送自定义消息 成功")
// 房间内对应玩家通过 onCustomMessageReceived 回调收到通知
}
}
#import <TapTapSDK/TapTapSDK.h>
[TapTapBattle sendCustomMessage:customMsg
customMsgType:customMsgType
playerIds:playerIds
completion:^(NSError *error) {
if (error) {
NSLog(@"发送自定义消息 error %ld %@", (long)error.code, error.localizedDescription);
} else {
NSLog(@"发送自定义消息 成功");
// 房间内对应玩家通过 onCustomMessageReceived 回调收到通知
}
}];
发送成功后,房间内对应玩家会通过 SDK 的 OnCustomMessageReceived 回调收到通知:
- Unity
- Android Java
- Android Kotlin
- iOS Swift
- iOS Objective-C
/// <summary>
/// 收到玩家自定义消息
/// </summary>
/// <param name="playerId"> 消息发送者玩家ID </param>
/// <param name="msg"> 自定义消息,格式由开发者决定,必须是utf8字符串,最大2048字节 </param>
public void OnCustomMessageReceived(string playerId, string msg)
{
// TODO: 解析 msg,渲染对战状态或其他信息
}
import com.taptap.sdk.battle.callback.TapOnlineBattleCallback;
@Override
public void onCustomMessageReceived(String playerId, String msg) {
// TODO: 解析 msg,渲染对战状态或其他信息
}
import com.taptap.sdk.battle.callback.TapOnlineBattleCallback
override fun onCustomMessageReceived(playerId: String, msg: String) {
// TODO: 解析 msg,渲染对战状态或其他信息
}
import TapTapSDK
extension YourClass: TapOnlineBattleCallback {
func onCustomMessageReceived(playerId: String, msg: String) {
// TODO: 解析 msg,渲染对战状态或其他信息
}
}
#import <TapTapSDK/TapTapSDK.h>
- (void)onCustomMessageReceived:(NSString *)playerId msg:(NSString *)msg {
// TODO: 解析 msg,渲染对战状态或其他信息
}
帧数据同步
通过 FPS 对战游戏 来描述帧数据同步在游戏中的使用方式,流程图如下:

单场对战的帧数据同步流程与对战生命周期对应,整体流程如下:
- 开始对战:由房主触发 开始帧同步,所有玩家进入帧同步状态
- 对战进行中:通过 发送玩家操作数据 进行帧数据同步
- 结束对战:由房主触发 停止帧同步,结束数据同步流程
- 帧率为每秒 30 帧
- 开始帧同步后,最大支持 30 分钟,超过时间后 SDK 会强制停止帧同步
- 每个玩家每帧最多允许发送 5 次操作数据
建议游戏类型
帧同步是针对高实时性竞技游戏采用的一种同步技术,主要用于需要实时进行数据同步的对战游戏,例如常见的 MOBA(多人在线战术竞技类游戏)、 和 FPS (第一人称射击类游戏)等。
开始帧同步
当房间内所有玩家状态就绪后,房主开启对战时,调用开始帧同步接口:
- Unity
- Android Java
- Android Kotlin
- iOS Swift
- iOS Objective-C
using TapSDK.OnlineBattle;
try
{
// 开始帧同步,仅房主可调用
await TapTapOnlineBattle.StartFrameSync();
LogMessage(" 开始帧同步 成功 ");
// 房间内所有玩家会通过 OnFrameSyncStarted 回调收到通知
}
catch (TapException e)
{
LogMessage($"开始帧同步 error {e.Code} {e.Message}");
}
import com.taptap.sdk.battle.TapTapBattle;
import com.taptap.sdk.battle.callback.TapBattleCallback;
import com.taptap.sdk.kit.internal.exception.TapError;
// 开始帧同步,仅房主可调用
TapTapBattle.startFrameSync(new TapBattleCallback<Void>() {
@Override
public void onSuccess(Void result) {
Log.d(TAG, "开始帧同步 成功");
// 房间内所有玩家会通过 onFrameSyncStarted 回调收到通知
}
@Override
public void onError(TapError error) {
Log.e(TAG, "开始帧同步 error " + error.code + " " + error.message);
}
});
import com.taptap.sdk.battle.TapTapBattle
import com.taptap.sdk.battle.callback.TapBattleCallback
import com.taptap.sdk.kit.internal.exception.TapError
import android.util.Log
// 开始帧同步,仅房主可调用
TapTapBattle.startFrameSync(object : TapBattleCallback<Void> {
override fun onSuccess(result: Void?) {
Log.d(TAG, "开始帧同步 成功")
// 房间内所有玩家会通过 onFrameSyncStarted 回调收到通知
}
override fun onError(error: TapError) {
Log.e(TAG, "开始帧同步 error ${error.code} ${error.message}")
}
})
import TapTapSDK
// 开始帧同步,仅房主可调用
TapTapBattle.startFrameSync { error in
if let error = error {
print("开始帧同步 error \(error.code) \(error.message)")
} else {
print("开始帧同步 成功")
// 房间内所有玩家会通过 onFrameSyncStarted 回调收到通知
}
}
#import <TapTapSDK/TapTapSDK.h>
// 开始帧同步,仅房主可调用
[TapTapBattle startFrameSync:^(NSError *error) {
if (error) {
NSLog(@"开始帧同步 error %ld %@", (long)error.code, error.localizedDescription);
} else {
NSLog(@"开始帧同步 成功");
// 房间内所有玩家会通过 onFrameSyncStarted 回调收到通知
}
}];
此时房间内所有玩家会通过 SDK 的 OnFrameSyncStarted 回调收到通知:
- Unity
- Android Java
- Android Kotlin
- iOS Swift
- iOS Objective-C
/// <summary>
/// 开始帧同步
/// </summary>
/// <param name="frameSyncInfo"> 帧同步基础信息 </param>
public void OnFrameSyncStarted(FrameSyncInfo frameSyncInfo)
{
LogMessage($"receive OnFrameSyncStarted {JsonConvert.SerializeObject(frameSyncInfo)}");
}
import com.taptap.sdk.battle.callback.TapOnlineBattleCallback;
import com.taptap.sdk.battle.model.FrameSyncInfo;
import com.google.gson.Gson;
@Override
public void onFrameSyncStarted(FrameSyncInfo frameSyncInfo) {
Log.d(TAG, "receive OnFrameSyncStarted " + new Gson().toJson(frameSyncInfo));
}
import com.taptap.sdk.battle.callback.TapOnlineBattleCallback
import com.taptap.sdk.battle.model.FrameSyncInfo
import com.google.gson.Gson
override fun onFrameSyncStarted(frameSyncInfo: FrameSyncInfo) {
Log.d(TAG, "receive OnFrameSyncStarted ${Gson().toJson(frameSyncInfo)}")
}
import TapTapSDK
extension YourClass: TapOnlineBattleCallback {
func onFrameSyncStarted(frameSyncInfo: FrameSyncInfo) {
print("receive OnFrameSyncStarted \(frameSyncInfo)")
}
}
#import <TapTapSDK/TapTapSDK.h>
- (void)onFrameSyncStarted:(FrameSyncInfo *)frameSyncInfo {
NSLog(@"receive OnFrameSyncStarted %@", frameSyncInfo);
}
回调中会返回本次帧同步的基础信息 (FrameSyncInfo),主要包含房间信息(roomInfo) 、随机种子(seed)等
发送玩家操作数据
对战过程中,玩家的操作数据可通过调用如下接口进行同步:
- Unity
- Android Java
- Android Kotlin
- iOS Swift
- iOS Objective-C
using TapSDK.OnlineBattle;
try
{
// playData 玩家操作数据,utf8字符串格式,最大1024字节
await TapTapOnlineBattle.SendFrameInput(playData);
LogMessage(" 发送玩家 Input 数据 成功 ");
// 房间内其他玩家通过 OnFrameReceived 回调收到通知
}
catch (TapException e)
{
LogMessage($"发送玩家 Input 数据 error {e.Code} {e.Message}");
}
import com.taptap.sdk.battle.TapTapBattle;
import com.taptap.sdk.battle.callback.TapBattleCallback;
import com.taptap.sdk.kit.internal.exception.TapError;
// playData 玩家操作数据,utf8字符串格式,最大1024字节
TapTapBattle.sendFrameInput(playData, new TapBattleCallback<Void>() {
@Override
public void onSuccess(Void result) {
Log.d(TAG, "发送玩家 Input 数据 成功");
// 房间内其他玩家通过 onFrameReceived 回调收到通知
}
@Override
public void onError(TapError error) {
Log.e(TAG, "发送玩家 Input 数据 error " + error.code + " " + error.message);
}
});
import com.taptap.sdk.battle.TapTapBattle
import com.taptap.sdk.battle.callback.TapBattleCallback
import com.taptap.sdk.kit.internal.exception.TapError
import android.util.Log
// playData 玩家操作数据,utf8字符串格式,最大1024字节
TapTapBattle.sendFrameInput(playData, object : TapBattleCallback<Void> {
override fun onSuccess(result: Void?) {
Log.d(TAG, "发送玩家 Input 数据 成功")
// 房间内其他玩家通过 onFrameReceived 回调收到通知
}
override fun onError(error: TapError) {
Log.e(TAG, "发送玩家 Input 数据 error ${error.code} ${error.message}")
}
})
import TapTapSDK
// playData 玩家操作数据,utf8字符串格式,最大1024字节
let request = SendFrameInputRequest(data: playData)
TapTapBattle.sendFrameInput(request: request) { error in
if let error = error {
print("发送玩家 Input 数据 error \(error.code) \(error.message)")
} else {
print("发送玩家 Input 数据 成功")
// 房间内其他玩家通过 onFrameReceived 回调收到通知
}
}
#import <TapTapSDK/TapTapSDK.h>
// playData 玩家操作数据,utf8字符串格式,最大1024字节
SendFrameInputRequest *request = [[SendFrameInputRequest alloc] initWithData:playData];
[TapTapBattle sendFrameInputWithRequest:request completion:^(NSError *error) {
if (error) {
NSLog(@"发送玩家 Input 数据 error %ld %@", (long)error.code, error.localizedDescription);
} else {
NSLog(@"发送玩家 Input 数据 成功");
// 房间内其他玩家通过 onFrameReceived 回调收到通知
}
}];
房间内其他玩家会通过 SDK 的 OnFrameReceived 回调收到通知:
- Unity
- Android Java
- Android Kotlin
- iOS Swift
- iOS Objective-C
/// <summary>
/// 收到帧同步数据回调
/// </summary>
/// <param name="frameData"> 帧数据 </param>
public void OnFrameReceived(FrameData frameData)
{
if (frameData != null && frameData.inputs != null && frameData.inputs.Count > 0)
{
LogMessage($"receive OnFrameReceived {JsonConvert.SerializeObject(frameData)}");
}
}
import com.taptap.sdk.battle.callback.TapOnlineBattleCallback;
import com.taptap.sdk.battle.model.FrameData;
import com.google.gson.Gson;
@Override
public void onFrameReceived(FrameData frameData) {
if (frameData != null && frameData.getInputs() != null && frameData.getInputs().size() > 0) {
Log.d(TAG, "receive OnFrameReceived " + new Gson().toJson(frameData));
}
}
import com.taptap.sdk.battle.callback.TapOnlineBattleCallback
import com.taptap.sdk.battle.model.FrameData
import com.google.gson.Gson
override fun onFrameReceived(frameData: FrameData) {
if (frameData != null && frameData.inputs != null && frameData.inputs.size > 0) {
Log.d(TAG, "receive OnFrameReceived ${Gson().toJson(frameData)}")
}
}
import TapTapSDK
extension YourClass: TapOnlineBattleCallback {
func onFrameReceived(frameData: FrameData) {
if frameData.inputs?.isEmpty == false {
print("receive OnFrameReceived \(frameData)")
}
}
}
#import <TapTapSDK/TapTapSDK.h>
- (void)onFrameReceived:(FrameData *)frameData {
if (frameData != nil && frameData.inputs != nil && frameData.inputs.count > 0) {
NSLog(@"receive OnFrameReceived %@", frameData);
}
}
回调中返回的参数 frameData 会包括当前帧 ID 及多个玩家的多个操作数据,其中 inputs 字段为玩家操作数据集合,集合中每条数据包含了玩家 ID、玩家操作数据、发送时间等信息。
停止帧同步
当房主结束对战时,开发者通过调用停止帧同步接口结束同步玩家数据:
- Unity
- Android Java
- Android Kotlin
- iOS Swift
- iOS Objective-C
using TapSDK.OnlineBattle;
try
{
// 停止帧同步,仅房主可调用
await TapTapOnlineBattle.StopFrameSync();
LogMessage(" 停止帧同步 成功 ");
// 房间内其他玩家通过 OnFrameSyncStopped 回调收到通知
}
catch (TapException e)
{
LogMessage($"停止帧同步 error {e.Code} {e.Message}");
}
import com.taptap.sdk.battle.TapTapBattle;
import com.taptap.sdk.battle.callback.TapBattleCallback;
import com.taptap.sdk.kit.internal.exception.TapError;
// 停止帧同步,仅房主可调用
TapTapBattle.stopFrameSync(new TapBattleCallback<Void>() {
@Override
public void onSuccess(Void result) {
Log.d(TAG, "停止帧同步 成功");
// 房间内其他玩家通过 onFrameSyncStopped 回调收到通知
}
@Override
public void onError(TapError error) {
Log.e(TAG, "停止帧同步 error " + error.code + " " + error.message);
}
});
import com.taptap.sdk.battle.TapTapBattle
import com.taptap.sdk.battle.callback.TapBattleCallback
import com.taptap.sdk.kit.internal.exception.TapError
import android.util.Log
// 停止帧同步,仅房主可调用
TapTapBattle.stopFrameSync(object : TapBattleCallback<Void> {
override fun onSuccess(result: Void?) {
Log.d(TAG, "停止帧同步 成功")
// 房间内其他玩家通过 onFrameSyncStopped 回调收到通知
}
override fun onError(error: TapError) {
Log.e(TAG, "停止帧同步 error ${error.code} ${error.message}")
}
})
import TapTapSDK
// 停止帧同步,仅房主可调用
TapTapBattle.stopFrameSync { error in
if let error = error {
print("停止帧同步 error \(error.code) \(error.message)")
} else {
print("停止帧同步 成功")
// 房间内其他玩家通过 onFrameSyncStopped 回调收到通知
}
}
#import <TapTapSDK/TapTapSDK.h>
// 停止帧同步,仅房主可调用
[TapTapBattle stopFrameSync:^(NSError *error) {
if (error) {
NSLog(@"停止帧同步 error %ld %@", (long)error.code, error.localizedDescription);
} else {
NSLog(@"停止帧同步 成功");
// 房间内其他玩家通过 onFrameSyncStopped 回调收到通知
}
}];
其他玩家会通过 OnFrameSyncStopped 回调收到通知:
- Unity
- Android Java
- Android Kotlin
- iOS Swift
- iOS Objective-C
/// <summary>
/// 停止帧同步
/// </summary>
/// <param name="roomId"> 房间 ID </param>
/// <param name="frameSyncId"> 帧同步 ID,房间内唯一 </param>
/// <param name="reason"> 原因 0:房主主动结束,1:因30分钟超时结束 </param>
public void OnFrameSyncStopped(string roomId, int frameSyncId, int reason)
{
LogMessage($"receive OnFrameSyncStopped {roomId}:{frameSyncId}:{reason}");
}
import com.taptap.sdk.battle.callback.TapOnlineBattleCallback;
@Override
public void onFrameSyncStopped(String roomId, int frameSyncId, int reason) {
Log.d(TAG, "receive OnFrameSyncStopped " + roomId + ":" + frameSyncId + ":" + reason);
}
import com.taptap.sdk.battle.callback.TapOnlineBattleCallback
override fun onFrameSyncStopped(roomId: String, frameSyncId: Int, reason: Int) {
Log.d(TAG, "receive OnFrameSyncStopped $roomId:$frameSyncId:$reason")
}
import TapTapSDK
extension YourClass: TapOnlineBattleCallback {
func onFrameSyncStopped(roomId: String, frameSyncId: Int, reason: Int) {
print("receive OnFrameSyncStopped \(roomId):\(frameSyncId):\(reason)")
}
}
#import <TapTapSDK/TapTapSDK.h>
- (void)onFrameSyncStopped:(NSString *)roomId frameSyncId:(NSInteger)frameSyncId reason:(NSInteger)reason {
NSLog(@"receive OnFrameSyncStopped %@:%ld:%ld", roomId, (long)frameSyncId, (long)reason);
}
随机数生成器
为保证玩家对战过程中,对于随机掉落物品或其他奖励的概率保持一致,不同玩家可使用该生成器生成相同序列的随机数。
建议在收到开始帧同步( OnFrameSyncStarted) 的回调中,使用返回的帧同步基础信息 FrameSyncInfo 参数的 seed 字段来初始化随机数生成器。
- Unity
- Android Java
- Android Kotlin
- iOS Swift
- iOS Objective-C
using TapSDK.OnlineBattle;
// 使用帧同步回调返回的随机种子
int seed = frameSyncInfo.seed;
// 创建生成器
var randomNumberGenerator = TapTapOnlineBattle.NewRandomNumberGenerator(seed);
// 生成随机数
try
{
int value = randomNumberGenerator.RandomInt();
LogMessage(" 生成随机数 成功 " + value);
}
catch (TapException e)
{
LogMessage($"生成随机数 error {e.Code} {e.Message}");
}
// 当对战结束后或已确定不需要生成器时,可调用如下接口销毁随机数生成器
randomNumberGenerator.Free();
import com.taptap.sdk.battle.TapTapBattle;
import com.taptap.sdk.battle.util.RandomNumberGenerator;
import com.taptap.sdk.kit.internal.exception.TapError;
// 使用帧同步回调返回的随机种子
int seed = frameSyncInfo.getSeed();
// 创建生成器
RandomNumberGenerator randomNumberGenerator = TapTapBattle.newRandomNumberGenerator(seed);
// 生成随机数
try {
int value = randomNumberGenerator.randomInt();
Log.d(TAG, "生成随机数 成功 " + value);
} catch (TapError e) {
Log.e(TAG, "生成随机数 error " + e.code + " " + e.message);
}
// 当对战结束后或已确定不需要生成器时,可调用如下接口销毁随机数生成器
randomNumberGenerator.free();
import com.taptap.sdk.battle.TapTapBattle
import com.taptap.sdk.battle.util.RandomNumberGenerator
import com.taptap.sdk.kit.internal.exception.TapError
// 使用帧同步回调返回的随机种子
val seed = frameSyncInfo.seed
// 创建生成器
val randomNumberGenerator = TapTapBattle.newRandomNumberGenerator(seed)
// 生成随机数
try {
val value = randomNumberGenerator.randomInt()
Log.d(TAG, "生成随机数 成功 $value")
} catch (e: TapError) {
Log.e(TAG, "生成随机数 error ${e.code} ${e.message}")
}
// 当对战结束后或已确定不需要生成器时,可调用如下接口销毁随机数生成器
randomNumberGenerator.free()
import TapTapSDK
// 使用帧同步回调返回的随机种子
let seed = frameSyncInfo.seed
// 创建生成器
let randomNumberGenerator = TapTapBattle.newRandomNumberGenerator(seed: seed)
// 生成随机数
do {
let value = try randomNumberGenerator.randomInt()
print("生成随机数 成功 \(value)")
} catch {
print("生成随机数 error \(error)")
}
// 当对战结束后或已确定不需要生成器时,可调用如下接口销毁随机数生成器
randomNumberGenerator.free()
#import <TapTapSDK/TapTapSDK.h>
// 使用帧同步回调返回的随机种子
NSInteger seed = frameSyncInfo.seed;
// 创建生成器
TapRandomNumberGenerator *randomNumberGenerator = [TapTapBattle newRandomNumberGeneratorWithSeed:seed];
// 生成随机数
NSError *error = nil;
NSInteger value = [randomNumberGenerator randomInt:&error];
if (error) {
NSLog(@"生成随机数 error %ld %@", (long)error.code, error.localizedDescription);
} else {
NSLog(@"生成随机数 成功 %ld", (long)value);
}
// 当对战结束后或已确定不需要生成器时,可调用如下接口销毁随机数生成器
[randomNumberGenerator free];