从零制作一个 Combos TikTok 互动游戏
这篇教程带你做出 Gift Arena(礼物竞技场):主播攻击对手,观众通过礼物为主播充能、回血,或直接攻击对手。你会拿到完整项目、本地礼物测试台、Combos 发布步骤,以及真实 TikTok 接入所需的登记清单。适用 Combos SDK 1.1.0。
目前没有可用的真实 TikTok 互动开通路径。你现在可以完成本地开发、礼物模拟和 Combos 发布;发布作品不会自动获得真实礼物。后面的接入材料和真实 LIVE 验收用于未来平台开放接入后的准备,不是现在就能申请完成的流程。
1. 先声明礼物,再设计游戏效果
先确定玩法,不急着连网络。这个示例中,主播初始生命 70、能量 40,对手生命 100。每次攻击消耗 10 能量,造成 10 伤害;对手存活时反击 8 点。观众用下面三种效果帮助主播:
| 游戏内礼物 | 固定的 instruction | count = "1" | count = "3" |
|---|---|---|---|
| 电池 | gift_battery | +20 能量 | +60 能量 |
| 治疗 | gift_heal | +15 生命 | +45 生命 |
| 闪击 | gift_flash | 对手 −10 生命 | 对手 −30 生命 |
// rules.js — this game's contract
export const GIFT_CATALOG = Object.freeze({
gift_battery: { target: 'energy', perCount: 20, max: 100 },
gift_heal: { target: 'hp', perCount: 15, max: 100 },
gift_flash: { target: 'bossHp', perCount: -10, max: 100 },
});这份声明写在你自己的游戏代码里。它不是 SDK 的注册调用,也不是 TikTok 礼物目录。电池、治疗、闪击只是示例里的效果名称,不代表真实 TikTok 礼物的名称或 ID;不能假定玫瑰或某个价格会自动对应其中一种效果。
接入前还要定清楚边界:本示例只接受 1–1000 的整数数量,生命和能量上限 100,对手生命下限 0;礼物不消耗玩家回合。到达上限时,效果即使没有进一步增加数值,也视为成功。胜利或失败之后的新指令返回 false,开始新一局才能继续接收。这些都是示例的玩法规则,不是平台限制。替换成自己的游戏时,要明确连发叠加、冷却、跨局处理和高成本效果,并把同一套规则交给礼物配置方。
2. 搞清楚平台和游戏各自负责什么
观众送礼 → TikTok 礼物映射 → Combos → SDK 的 onCommand → 游戏效果 → 布尔执行结果 → Combos → TikTok 回执。
| 谁负责 | 要完成的工作 |
|---|---|
| 游戏开发者 | 声明支持的指令,执行并保存游戏效果,去重,返回真实执行结果 |
| Combos 宿主与 SDK | 登录和启动绑定、会话续期、指令传输、心跳、重连、结果回传 |
| Combos / TikTok 接入方 | 登记游戏入口和允许使用的账号,配置启动链路及真实礼物到指令的映射 |
例如,一次已配置的互动产生 instruction: "gift_flash", count: "3",游戏就扣除对手 30 点生命。count 是映射后的指令数量,不是金币价格,也不一定等于送礼件数。连击如何换算数量,需要在登记时确认。游戏本身不用读取 launchData、给 TikTok 请求签名、另建 WebSocket 或实现 webhook。
3. 再实现游戏收到的协议
type TTLiveCommand = {
messageId: string;
instruction: string;
count: string;
timestamp: string;
};| 字段 | 游戏应该怎么处理 |
|---|---|
messageId | 当作不透明的去重键;同一条消息不能执行两次效果 |
instruction | 精确匹配自己声明的指令;不支持的指令明确拒绝 |
count | 十进制整数字符串;先校验并限制安全范围,再转成 Number |
timestamp | 来源指令的时间字符串;不能当作身份、去重键或严格顺序保证 |
当前公开指令里没有观众 ID、昵称或真实礼物 ID,不能基于这些不存在的字段直接做“送礼观众排行榜”。
效果确认保存后返回 true;确定拒绝执行时返回 false;结果无法确认时抛错,例如存储失败。未知结果不会被确认,后续可能重投。不能一遇到异常就返回成功,也不能在效果可能已经部分生效时直接返回失败。
SDK 会在当前页面内重发待确认的结果,游戏还要处理刷新后的重复消息。示例把游戏状态和消息执行结果放在同一笔 IndexedDB 事务里保存,事务提交后才返回布尔值。重复消息读取原结果,不再施加效果;新一局保留旧回执。LIVE 存档按 Combos 账号和游戏隔离,也与演练存档隔离。
怎么看连接状态
getTTLiveClient() 在每个游戏页面中返回同一个客户端。getLiveStatus() 查询状态;onStatusChange() 先立即通知当前状态,再通知变化。状态包括 state、reason、action 和 confirmedAtMs,最后一个字段是最近确认启动或心跳的毫秒时间,0 表示未知。
connecting:正在建立连接。connected:互动会话已确认。recovering:让 SDK 自动重连。unknown:暂时无法确认状态,不等于主播下播。ended:这次游戏互动会话结束。按action的wait、none或relaunch_game处理。需要relaunch_game时,让主播从 LIVE Studio 重新打开这个游戏,不要因连接异常要求重启直播。
4. 运行完整 demo
准备与启动
下载 ZIP 并解压。准备一个较新的浏览器和 Python 3,用它启动本地静态服务器;不需要 Node 或 npm。保留解压出的 tiktok-arena 文件夹结构:
cd tiktok-arena
python3 -m http.server 8080 --bind 127.0.0.1打开 http://127.0.0.1:8080/,点击开始演练。不要双击 index.html,JavaScript 模块需要通过 HTTP 加载。直接玩在线示例则不用安装任何东西。游戏内语言切换支持与本站相同的十种语言。
| 文件 | 需要修改或了解的内容 |
|---|---|
rules.js | 礼物声明、战斗规则、数量校验、消息去重 |
storage.js | 原子保存游戏状态与回执 |
connection.js | SDK 能力检查、登录、连接、取消 |
game.js | LIVE 与演练共用的处理函数及界面 |
index.html、style.css、draw.js、i18n.js | 布局、画布绘制、十语言文案 |
三个可以照着复现的 demo
前两行测试各自开始前先点“新一局”;第三行接着第二行做,不重置。初始数值为生命 70、能量 40、对手生命 100。每次点礼物生成新消息;重复上一条消息沿用原来的 ID。
| 场景 | 操作 | 应该看到什么 |
|---|---|---|
| 单次礼物 | 数量填 1,依次点电池、治疗、闪击 | 能量 60、生命 85、对手生命 90,三次结果都是 true |
| 连发与重投 | 数量填 3,点闪击,再点重复 | 两次投递后对手生命都为 70;第二次 true、duplicate: true |
| 刷新恢复与拒绝 | 接着刷新页面,开始演练,再点重复;然后点未知指令 | 对手生命仍为 70;未知指令返回 false,状态不变 |
还可以把数量改成 0、1.5、1001、9007199254740993:都应没有效果且返回 false。新一局用闪击 ×10 获胜后,再送一条新礼物,应被拒绝,直到开始下一局。用攻击和充能按钮体验普通战斗。“最近一次投递”展示输入、结果和最终数值。这些都是本地合成指令,不会购买礼物或向 TikTok 发送 ACK。
5. 在 Combos 里连接同一个游戏
发布时保留项目中的 connection.js 与 game.js。Combos 宿主注入 window.combos,不需要安装一个额外的 npm SDK,也不要重复加载 SDK 脚本。连接 Combos LIVE 按钮已经实现以下流程:
- 调用
init(),检查getTTLiveSession能力和客户端 getter。这个能力名称不是让游戏直接调用的公开方法。 - 从按钮点击发起
checkSession(),未登录则调用login()。取消登录就结束;登录成功继续原来的操作。 - 用
getUserInfo()为本地存档分区。这些 ID 不等于凭据,也不能作为服务端鉴权证明。 - 订阅状态,再调用
getTTLiveClient().connect({ onCommand }),使用与演练相同的处理函数。 - 连接重试期间仍可断开 / 取消;取消或离开页面时取消订阅并调用
disconnect()。
下面的核心处理函数已经包含在下载项目中,完整版还会刷新界面。store.update() 是 storage.js 提供的事务方法,不是 SDK API:
import { executeCommand } from './rules.js';
// `store` is the account/game-scoped store opened by prepareStore().
function commandHandler(store) {
return async (command) => {
const { result } = await store.update((saved) =>
executeCommand(saved, command),
);
return result; // State + receipt committed; no catch-and-return-false.
};
}connect() 重试时可能一直等待;它返回成功也不证明真实礼物已经生效。LIVE 模式会隐藏模拟礼物按钮,实际联调要同时核对游戏效果和执行结果。
6. 把这个项目发布到 Combos
安装官方 Combos CLI 。在 demo 中点击保存 PNG 封面,把下载的 cover.png 放在 tiktok-arena 文件夹旁边,然后在这个父目录执行:
combos --version
combos account login
combos post create --dir ./tiktok-arena --title 'Gift Arena' --cover ./cover.png --platform pc --screen-orientation landscape --allow-recommend=false
combos post get在浏览器里用准备发布游戏的账号完成登录。打开命令返回的分享链接,在 Combos 播放器内测试游戏。保留生成的 combos.json 绑定文件,以后仍在该目录更新同一个作品:
combos post update --dir ./tiktok-arena --platform pc这个示例是 PC 横屏静态游戏。上传目录的顶层必须有 index.html,所有相对路径资源也要包含进去。发布不会自动开通 TT;普通分享链接如果没有已登记的 LIVE Studio 启动上下文,可以演练,但不能自行生成真实互动会话。完整发布说明见 H5 接入。
7. 准备未来接入所需的材料
目前没有可用的真实 TikTok 互动开通入口,也没有已确认的申请流程。游戏与账号登记、真实礼物映射方式需要等待平台提供接入说明;SDK 和 CLI 都不提供公开的自助注册 API。可以先准备下面的材料,供未来开放接入时使用。这是准备清单,不是正在开放的申请流程:
| 提供什么 | 写清楚哪些内容 |
|---|---|
| 游戏信息 | 已发布的 Combos 分享链接、绑定的 Post / 项目 ID、游戏名、PC 横屏 |
| 联调账号 | 通过接入方指定渠道提供测试 / 主播账号;不提供密码或会话 token |
| 效果声明 | 三个固定指令、每个 count 对应的效果、1–1000 数量范围、数值上限和局末行为 |
| 映射需求 | 从获准使用的真实 TikTok 礼物中选择,逐项对应指令,明确数量 / 连击换算与预期效果 |
| 验收场景 | 下文的单礼物、连发、重投 / 恢复、会话结束测试 |
开始测试前,请接入方确认具体的已登记启动入口、游戏及账号资格、礼物映射已经生效。真实礼物 ID、名称、价格要来自已配置的礼物目录。假设约定一次互动产生闪击 ×3,游戏应收到:
{
"instruction": "gift_flash",
"count": "3"
}不要把 TikTok 密钥、CAS token 或手工拼接的 launchData 放进游戏,这些由平台处理。登记未完成时,成果是“已发布的 Combos 游戏 + 可运行的模拟器”,真实 TikTok 验收仍待完成。
8. 开通后的真实 LIVE 验收
只有在平台提供开通方式,并确认游戏入口、可用账号和礼物映射之后,才能执行下面的真实验收。目前可以先完成上面的本地模拟与 Combos 发布检查;这些检查不能代替真实 LIVE 验收。
- 使用获准账号,从 TikTok LIVE Studio 打开已登记的游戏入口;在游戏中连接 Combos LIVE,确认
connected。 - 在测试参与者知情同意的前提下,触发一次已配置的测试互动。记录
messageId、指令 / 数量、效果前后的数值及返回结果,并请接入方确认平台收到对应 ACK。记录中不放凭据或观众个人信息。 - 测试约定的连发数量,核对“数量 × 效果”、上限及重复投递不重复生效,与上面的本地 demo 对照。
- 模拟短暂断网再恢复,同一已执行指令不能再生效一次。在宿主支持的启动流程内刷新,核对状态与回执恢复;如宿主要求重新拉起,按提示操作。
- 结束互动,检查
ended/relaunch_game;测试连接中取消和退出游戏。旧消息不得再造成额外效果。
浏览器存档只覆盖这个示例在同一浏览器、来源、账号和游戏内的恢复,不提供跨设备恢复或可信的经济系统。LIVE 期间不要清除它。累计 10,000 条回执后,示例停止接受新指令,不会擅自淘汰旧结果。有持久奖励或正式规模需求时,应把效果和回执一起提交到自己的可信后端,并按已确认结果制定保留策略。
卡住时从哪里查
| 现象 | 下一步 |
|---|---|
unsupported_host | 在支持的 Combos 播放器内打开,检查 init() 能力和 SDK 是否可用 |
login_cancelled / missing_profile | 用正确账号完成登录,再发起连接 |
tt_not_launched、ended 或 relaunch_game | 核实登记和账号资格,从 LIVE Studio 重新打开已登记的游戏 |
| 已连接但没有效果 | 核对真实礼物映射、精确指令 / 数量及当前是否已结局,请接入方按消息 ID 排查 |
返回 false | 检查未知指令、非法数量或已结束的对局;这是游戏的明确拒绝 |
invalid_save、receipt_capacity、storage_blocked 或 operation_failed | 暂停 LIVE 联调,在本地检查存储是否可用和控制台错误;保留回执、定位后再恢复,不能把未知结果改成成功 ACK |