Godot 4 的 Android TapSDK v4 插件集成与使用说明。
- Godot
4.2+
- Android 自定义构建
- TapSDK
4.10.7
- 已在 TapTap 开发者中心创建应用,并取得 Client ID 和 Client Token
将 plugin/demo/addons/GodotTapTapSDK 整个目录复制到游戏项目的 addons 目录。
然后在 Godot 中完成以下操作:
- 打开 项目设置 -> 插件,启用
GodotTapTapSDK。
- 打开 Android 导出预设,启用 自定义构建。
- 执行一次 Android 导出。
导出插件会自动解析 TapSDK 所需的 Maven 依赖,无需手动向 Godot 项目添加 TapSDK AAR 文件。
在仓库根目录执行:
构建完成后,插件目录为 plugin/demo/addons/GodotTapTapSDK。
启用插件后,项目中会自动注册 GodotTapTap 自动加载节点。调用任意接口前必须初始化一次:
func _ready() -> void:
GodotTapTap.initialize(
"your_client_id",
"your_client_token",
"CN",
OS.is_debug_build()
)
| 参数 |
类型 |
默认值 |
说明 |
client_id |
String |
无 |
TapTap 开发者中心的 Client ID |
client_token |
String |
无 |
TapTap 开发者中心的 Client Token |
region |
String |
"CN" |
CN 或 GLOBAL,必须与应用配置的地区一致 |
enable_log |
bool |
false |
是否输出 TapSDK 调试日志,发布包建议关闭 |
所有异步结果通过 GodotTapTap 信号返回。账户、成就、云存档和排行榜等结构化结果使用 JSON 字符串;统一解析方式如下:
func parse_taptap_json(value: String):
var result = JSON.parse_string(value)
if result == null:
push_error("TapTap 返回了无效 JSON")
return result
| 方法 |
参数 |
返回值 |
说明 |
tap_login() |
无 |
无 |
打开 TapTap 登录流程 |
isLogin() |
无 |
bool |
当前是否存在登录账户 |
getCurrentProfile() |
无 |
String 或 null |
当前账户 JSON;未登录时为 null |
logOut() |
无 |
无 |
退出登录,同时退出合规认证 |
signal onLoginResult(code, account_json)
| 参数 |
类型 |
说明 |
code |
int |
200 成功,400 失败,499 用户取消 |
account_json |
String |
成功时为账户 JSON;失败或取消时为空字符串 |
账户 JSON 字段:
{
"openId": "TapTap OpenID",
"unionId": "TapTap UnionID"
}
func _ready() -> void:
GodotTapTap.onLoginResult.connect(_on_login_result)
func login() -> void:
GodotTapTap.tap_login()
func _on_login_result(code: int, account_json: String) -> void:
if code != 200:
return
var account = JSON.parse_string(account_json)
print("openId: ", account.openId)
| 方法 |
参数 |
返回值 |
说明 |
quickCheck(user_identifier := "") |
user_identifier: String |
无 |
开始合规认证;为空时使用当前登录账户的 openId |
antiExit() |
无 |
无 |
退出当前合规认证状态 |
signal onAntiAddictionCallback(code)
| 参数 |
类型 |
说明 |
code |
int |
合规认证状态码;游戏可开始时为 500 |
func _ready() -> void:
GodotTapTap.onAntiAddictionCallback.connect(_on_compliance_result)
func begin_game() -> void:
GodotTapTap.quickCheck()
# 使用游戏自己的账号体系时:GodotTapTap.quickCheck(player_id)
func _on_compliance_result(code: int) -> void:
if code == 500:
start_game()
| 方法 |
参数 |
返回值 |
说明 |
momentOpen() |
无 |
无 |
打开 TapTap 内嵌动态页面 |
signal onTapMomentCallBack(code)
| 参数 |
类型 |
说明 |
code |
int |
内嵌动态模块返回的状态码 |
func _ready() -> void:
GodotTapTap.onTapMomentCallBack.connect(_on_moment_result)
func open_moment() -> void:
GodotTapTap.momentOpen()
func _on_moment_result(code: int) -> void:
print("动态状态: ", code)
| 方法 |
参数 |
返回值 |
说明 |
check_force_update() |
无 |
无 |
使用开发者中心配置检查并处理强制更新 |
update_game() |
无 |
无 |
游戏自行判断更新后,打开更新下载和安装流程 |
signal onUpdateCancelled()
用户在 update_game() 的更新流程中取消时触发,无参数。
func _ready() -> void:
GodotTapTap.onUpdateCancelled.connect(_on_update_cancelled)
GodotTapTap.check_force_update()
func _on_update_cancelled() -> void:
print("用户取消更新")
| 方法 |
参数 |
返回值 |
说明 |
check_license(force_check := false) |
force_check: bool |
无 |
执行正版验证;true 表示强制检查 |
query_dlc(sku_ids) |
sku_ids: PackedStringArray |
无 |
查询多个 DLC SKU 的状态 |
purchase_dlc(sku_id) |
sku_id: String |
无 |
发起指定 DLC 的购买流程 |
signal onLicenseValidated()
signal onDLCQueryResult(code, states_json)
signal onDLCPurchaseResult(sku_id, status)
| 信号 |
参数 |
说明 |
onLicenseValidated |
无 |
正版验证成功 |
onDLCQueryResult |
code: int、states_json: String |
查询状态码和 SKU 状态 JSON |
onDLCPurchaseResult |
sku_id: String、status: int |
被购买 SKU 和购买状态码 |
states_json 的格式为 SKU 到状态码的映射:
{
"soundtrack": 1,
"expansion_1": 0
}
func _ready() -> void:
GodotTapTap.onLicenseValidated.connect(_on_license_validated)
GodotTapTap.onDLCQueryResult.connect(_on_dlc_query)
GodotTapTap.onDLCPurchaseResult.connect(_on_dlc_purchase)
GodotTapTap.check_license()
GodotTapTap.query_dlc(PackedStringArray(["soundtrack", "expansion_1"]))
func _on_license_validated() -> void:
print("正版验证成功")
func _on_dlc_query(code: int, states_json: String) -> void:
var states = JSON.parse_string(states_json)
func _on_dlc_purchase(sku_id: String, status: int) -> void:
print("DLC: ", sku_id, ", 状态: ", status)
share(
title: String = "",
contents: String = "",
hashtag_ids: String = "",
group_label_id: String = "",
image_uris: PackedStringArray = PackedStringArray(),
fail_url: String = ""
) -> int
| 参数 |
说明 |
title |
分享标题 |
contents |
分享正文 |
hashtag_ids |
TapTap 话题 ID |
group_label_id |
TapTap 分组标签 ID |
image_uris |
Android 图片 URI 数组,例如 content://... |
fail_url |
分享失败时打开的网页地址 |
其他接口:
| 方法 |
参数 |
返回值 |
说明 |
open_review() |
无 |
无 |
打开游戏评价页 |
open_rep(url) |
url: String |
无 |
打开 REP 页面 |
signal onShareResult(code)
signal onRepResult(code, message)
| 信号 |
参数 |
说明 |
onShareResult |
code: int |
与 share() 的返回值一致 |
onRepResult |
code: int、message: String |
REP 结果;code == 0 表示成功 |
func share_result() -> void:
var code = GodotTapTap.share(
"我的游戏",
"完成了一次挑战",
"",
"",
PackedStringArray(),
"https://www.taptap.cn/"
)
print("分享调用结果: ", code)
func open_review() -> void:
GodotTapTap.open_review()
| 方法 |
参数 |
返回值 |
说明 |
unlock_achievement(achievement_id) |
achievement_id: String |
无 |
解锁指定成就 |
increment_achievement(achievement_id, steps) |
achievement_id: String、steps: int |
无 |
增加分步成就进度 |
show_achievements() |
无 |
无 |
打开成就展示页 |
set_achievement_toast_enabled(enabled) |
enabled: bool |
无 |
是否显示 SDK 成就冒泡 |
signal onAchievementResult(code, result_json)
signal onAchievementError(achievement_id, code, message)
成功结果 result_json:
{
"achievementId": "first_win",
"achievementName": "首胜",
"achievementType": "NORMAL",
"currentSteps": 1
}
| 信号 |
参数 |
说明 |
onAchievementResult |
code: int、result_json: String |
成就解锁或进度更新成功 |
onAchievementError |
achievement_id: String、code: int、message: String |
成就操作失败 |
func _ready() -> void:
GodotTapTap.onAchievementResult.connect(_on_achievement_result)
GodotTapTap.onAchievementError.connect(_on_achievement_error)
func report_win() -> void:
GodotTapTap.unlock_achievement("first_win")
GodotTapTap.increment_achievement("wins", 1)
func _on_achievement_result(code: int, result_json: String) -> void:
var result = JSON.parse_string(result_json)
func _on_achievement_error(achievement_id: String, code: int, message: String) -> void:
push_error("成就失败: %s %s" % [code, message])
数据分析接口没有 Godot 回调信号。属性参数均为 Dictionary,插件会转换为 JSON 后传给 TapSDK。
| 方法 |
参数 |
说明 |
log_analytics_event(name, properties := {}) |
name: String、properties: Dictionary |
上报自定义事件 |
set_analytics_user_id(user_id, properties := {}) |
user_id: String、properties: Dictionary |
设置分析用户 ID 与用户属性 |
clear_analytics_user() |
无 |
清除当前分析用户 |
get_analytics_device_id() |
无 |
返回 TapSDK 设备 ID |
| 方法 |
参数 |
说明 |
add_analytics_common(properties) |
properties: Dictionary |
添加所有后续事件的公共属性 |
clear_analytics_common(keys) |
keys: PackedStringArray |
清除指定公共属性 |
clear_all_analytics_common() |
无 |
清除全部公共属性 |
analytics_device_initialize(properties) |
properties: Dictionary |
初始化设备属性 |
analytics_device_update(properties) |
properties: Dictionary |
更新设备属性 |
analytics_device_add(properties) |
properties: Dictionary |
增加设备属性 |
analytics_user_initialize(properties) |
properties: Dictionary |
初始化用户属性 |
analytics_user_update(properties) |
properties: Dictionary |
更新用户属性 |
analytics_user_add(properties) |
properties: Dictionary |
增加用户属性 |
set_analytics_oaid(oaid) |
oaid: String |
设置 OAID |
log_device_login_event() |
无 |
上报设备登录事件 |
GodotTapTap.set_analytics_user_id("player_42", {"server": "asia"})
GodotTapTap.add_analytics_common({"game_mode": "ranked"})
GodotTapTap.log_analytics_event("match_finished", {
"duration_seconds": 178,
"won": true
})
create_cloud_archive(
metadata: Dictionary,
data_file_path: String,
cover_file_path: String = ""
)
update_cloud_archive(
archive_uuid: String,
metadata: Dictionary,
data_file_path: String,
cover_file_path: String = ""
)
| 元数据字段 |
类型 |
说明 |
name |
String |
存档名称 |
summary |
String |
存档摘要 |
extra |
String |
自定义扩展字符串 |
playtime |
int |
游戏时长,单位由游戏自行约定 |
data_file_path 和 cover_file_path 为游戏可读取的本地文件路径,例如 user://saves/slot_1.dat。封面路径可以为空字符串。
| 方法 |
参数 |
返回值 |
说明 |
delete_cloud_archive(archive_uuid) |
archive_uuid: String |
无 |
删除存档 |
get_cloud_archive_list() |
无 |
无 |
获取全部存档列表 |
get_cloud_archive_data(archive_uuid, archive_file_id) |
两个 String |
无 |
下载存档数据 |
get_cloud_archive_cover(archive_uuid, archive_file_id) |
两个 String |
无 |
下载存档封面 |
signal onCloudSaveStatus(code)
signal onCloudSaveArchiveCreated(archive_json)
signal onCloudSaveArchiveUpdated(archive_json)
signal onCloudSaveArchiveDeleted(archive_json)
signal onCloudSaveArchiveList(archives_json)
signal onCloudSaveArchiveData(data_base64)
signal onCloudSaveArchiveCover(data_base64)
signal onCloudSaveError(code, message)
单个存档 JSON 的字段:
{
"uuid": "存档 UUID",
"fileId": "存档文件 ID",
"name": "存档名称",
"summary": "存档摘要",
"extra": "扩展信息",
"playtime": 7200,
"saveSize": 1024,
"coverSize": 128,
"createdTime": 0,
"modifiedTime": 0
}
| 信号 |
参数 |
说明 |
onCloudSaveStatus |
code: int |
云存档模块通用状态 |
onCloudSaveArchiveCreated |
archive_json: String |
创建成功的存档 |
onCloudSaveArchiveUpdated |
archive_json: String |
更新成功的存档 |
onCloudSaveArchiveDeleted |
archive_json: String |
删除成功的存档 |
onCloudSaveArchiveList |
archives_json: String |
存档 JSON 数组 |
onCloudSaveArchiveData |
data_base64: String |
下载的存档数据,Base64 编码 |
onCloudSaveArchiveCover |
data_base64: String |
下载的封面数据,Base64 编码 |
onCloudSaveError |
code: int、message: String |
请求失败 |
func _ready() -> void:
GodotTapTap.onCloudSaveArchiveCreated.connect(_on_archive_created)
GodotTapTap.onCloudSaveArchiveData.connect(_on_archive_data)
GodotTapTap.onCloudSaveError.connect(_on_cloud_save_error)
func upload_save() -> void:
GodotTapTap.create_cloud_archive(
{
"name": "第一章",
"summary": "已完成关卡",
"extra": "{\"difficulty\":\"hard\"}",
"playtime": 7200
},
"user://saves/chapter_1.dat",
"user://saves/chapter_1.png"
)
func _on_archive_created(archive_json: String) -> void:
var archive = JSON.parse_string(archive_json)
GodotTapTap.get_cloud_archive_data(archive.uuid, archive.fileId)
func _on_archive_data(data_base64: String) -> void:
var data: PackedByteArray = Marshalls.base64_to_raw(data_base64)
func _on_cloud_save_error(code: int, message: String) -> void:
push_error("云存档失败: %s %s" % [code, message])
| 方法 |
参数 |
返回值 |
说明 |
prepare_relation() |
无 |
无 |
初始化好友实时消息、未读数和新增粉丝能力;使用这些能力前调用 |
start_relation_messenger() |
无 |
无 |
打开聊天页面 |
invite_relation_game() |
无 |
无 |
打开邀请好友上线页面 |
invite_relation_team(team_id) |
team_id: String |
无 |
打开邀请好友组队页面 |
show_relation_user_profile(open_id := "", union_id := "") |
两个 String |
无 |
打开 TapTap 用户名片;传入其中一个 ID |
get_relation_new_fans_count() |
无 |
int |
返回新增粉丝数量 |
get_relation_unread_message_count() |
无 |
int |
返回新增未读消息数量 |
destroy_relation() |
无 |
无 |
销毁好友模块资源 |
signal onRelationMessengerResult(code)
signal onRelationFansCountChanged(code, fans_count)
signal onRelationUnreadMessageCountChanged(code, unread_message_count)
signal onRelationGameInvite(open_id, union_id)
signal onRelationTeamInvite(open_id, union_id, team_id)
| 信号 |
参数 |
说明 |
onRelationMessengerResult |
code: int |
聊天模块状态码 |
onRelationFansCountChanged |
code: int、fans_count: int |
新增粉丝数变化 |
onRelationUnreadMessageCountChanged |
code: int、unread_message_count: int |
新增未读消息数变化 |
onRelationGameInvite |
open_id: String、union_id: String |
收到上线邀请 |
onRelationTeamInvite |
open_id: String、union_id: String、team_id: String |
收到组队邀请 |
func _ready() -> void:
GodotTapTap.onRelationGameInvite.connect(_on_game_invite)
GodotTapTap.onRelationTeamInvite.connect(_on_team_invite)
GodotTapTap.prepare_relation()
func invite_team() -> void:
GodotTapTap.invite_relation_team("room_987")
func _on_game_invite(open_id: String, union_id: String) -> void:
print("收到上线邀请: ", open_id)
func _on_team_invite(open_id: String, union_id: String, team_id: String) -> void:
print("收到组队邀请: ", team_id)
| 方法 |
参数 |
返回值 |
说明 |
open_leaderboard(leaderboard_id, collection := "public") |
leaderboard_id: String、collection: String |
无 |
打开排行榜页面 |
show_leaderboard_user_profile(open_id := "", union_id := "") |
两个 String |
无 |
打开排行榜用户资料;传入其中一个 ID |
submit_leaderboard_scores(scores) |
scores: Array |
无 |
批量提交分数 |
load_leaderboard_scores(leaderboard_id, collection := "public", next_page := "", period_token := "") |
四个参数 |
无 |
获取排行榜分页数据 |
load_current_leaderboard_score(leaderboard_id, collection := "public", period_token := "") |
三个参数 |
无 |
获取当前登录用户分数 |
load_player_centered_leaderboard_scores(leaderboard_id, collection := "public", period_token := "", limit := 0) |
四个参数 |
无 |
获取当前用户附近的分数 |
collection 只支持 "public"(总榜)和 "friends"(好友榜)。next_page 传入上一次 onLeaderboardScores 返回 JSON 中的 nextPage;首次查询传空字符串。period_token 为空时使用当前周期。limit 为 0 时由 SDK 使用默认值。
提交分数的 scores 格式:
[
{"leaderboardId": "weekly_score", "score": 12800},
{"leaderboardId": "weekly_time", "score": 360}
]
signal onLeaderboardStatus(code, message)
signal onLeaderboardShareSuccess(local_path)
signal onLeaderboardShareError(message)
signal onLeaderboardSubmitScores(result_json)
signal onLeaderboardScores(result_json)
signal onLeaderboardCurrentScore(result_json)
signal onLeaderboardPlayerCenteredScores(result_json)
signal onLeaderboardError(operation, code, message)
| 信号 |
参数 |
说明 |
onLeaderboardStatus |
code: int、message: String |
排行榜模块通用状态 |
onLeaderboardShareSuccess |
local_path: String |
排行榜分享图片保存后的本地路径 |
onLeaderboardShareError |
message: String |
排行榜分享图片生成失败 |
onLeaderboardSubmitScores |
result_json: String |
提交分数成功 |
onLeaderboardScores |
result_json: String |
分页分数列表 |
onLeaderboardCurrentScore |
result_json: String |
当前用户分数 |
onLeaderboardPlayerCenteredScores |
result_json: String |
当前用户附近的分数列表 |
onLeaderboardError |
operation: String、code: int、message: String |
请求失败;operation 为 submitScores、loadScores、loadCurrentScore 或 loadPlayerCenteredScores |
onLeaderboardScores 与 onLeaderboardPlayerCenteredScores 的结果格式:
{
"leaderboard": {
"id": "weekly_score",
"name": "周榜",
"availablePeriods": [
{"display": "本周", "periodToken": "weekly"}
],
"period": {"display": "本周", "periodToken": "weekly"},
"score": {}
},
"scores": [
{
"rank": 1,
"rankDisplay": "1",
"score": 12800,
"scoreDisplay": "12,800",
"scoreSubmittedTime": 0,
"user": {
"avatar": "",
"name": "玩家名称",
"openId": "",
"unionId": ""
}
}
],
"nextPage": "下一页令牌"
}
onLeaderboardCurrentScore 的结果为:
{
"currentUserScore": {},
"leaderboard": {}
}
func _ready() -> void:
GodotTapTap.onLeaderboardScores.connect(_on_leaderboard_scores)
GodotTapTap.onLeaderboardError.connect(_on_leaderboard_error)
func submit_score() -> void:
GodotTapTap.submit_leaderboard_scores([
{"leaderboardId": "weekly_score", "score": 12800}
])
func load_scores() -> void:
GodotTapTap.load_leaderboard_scores("weekly_score", "public")
func _on_leaderboard_scores(result_json: String) -> void:
var result = JSON.parse_string(result_json)
var next_page = result.nextPage
func _on_leaderboard_error(operation: String, code: int, message: String) -> void:
push_error("排行榜 %s 失败: %s %s" % [operation, code, message])
插件会在 Android 导出时自动添加 TapSDK Maven 依赖及所需 Manifest 配置。项目无需额外添加 TapSDK 依赖;只需确保 Android 导出预设开启了 自定义构建。
云存档、成就、排行榜、好友、正版验证和 DLC 等服务,需要先在 TapTap 开发者中心完成对应服务配置后再进行真机测试。
.\gradlew.bat :plugin:assemble
构建后的 Debug 和 Release AAR 位于 plugin/demo/addons/GodotTapTapSDK/bin。