playgama-bridge-js-sdk
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePlaygama Bridge JS SDK
Playgama Bridge JS SDK
Playgama Bridge is a unified JS SDK that lets a single HTML5 build run on 20+ platforms (Playgama, Yandex Games, Crazy Games, Facebook Gaming, YouTube Playables, Telegram, Poki, Y8, Lagged, MSN, Microsoft Store, Discord, GameSnacks, Xiaomi, Huawei, JioGames, BitQuest, Reddit, Game Distribution, Playhop, Aha Games, etc.).
When the game runs on a supported platform, Bridge auto-loads that platform's native scripts and routes API calls to it. In unsupported environments (local dev, unknown hosts) Bridge uses a platform that returns safe defaults (, , etc.) instead of throwing — the same code works everywhere.
mockfalserejectThe JS SDK (JS Core) is the foundation; engine packages (Unity, Godot, Construct, GDevelop, GameMaker, Defold, Cocos Creator, Scratch) are thin wrappers. Use JS Core directly for web engines such as PlayCanvas, Phaser, LayaAir, Three.js, Babylon.js.
Playgama Bridge 是一个统一的 JS SDK,让单个 HTML5 构建版本能够在 20 多个平台上运行(Playgama、Yandex Games、Crazy Games、Facebook Gaming、YouTube Playables、Telegram、Poki、Y8、Lagged、MSN、Microsoft Store、Discord、GameSnacks、Xiaomi、Huawei、JioGames、BitQuest、Reddit、Game Distribution、Playhop、Aha Games 等)。
当游戏运行在受支持的平台上时,Bridge 会自动加载该平台的原生脚本,并将 API 调用转发给该平台。在不受支持的环境(本地开发、未知宿主)中,Bridge 会使用 平台返回安全默认值(、 等),而不是抛出异常,因此同一套代码可在所有环境中运行。
mockfalserejectJS SDK(JS Core)是整个 Bridge 的基础;引擎包(Unity、Godot、Construct、GDevelop、GameMaker、Defold、Cocos Creator、Scratch)都是轻量封装。对于 PlayCanvas、Phaser、LayaAir、Three.js、Babylon.js 等 Web 引擎,可直接使用 JS Core。
Installation
安装
Add the script tag to the of your before any game code:
<head>index.htmlhtml
<html>
<head>
<script src="https://bridge.playgama.com/v1/stable/playgama-bridge.js"></script>
</head>
<body>
...
</body>
</html>CDN options:
| URL | When to use |
|---|---|
| Recommended. Latest stable v1.x.x |
| Bleeding edge — for testing only |
| Pin a specific version |
The SDK exposes a global object after the script loads.
bridge在任何游戏代码之前,将 script 标签添加到 的 中:
index.html<head>html
<html>
<head>
<script src="https://bridge.playgama.com/v1/stable/playgama-bridge.js"></script>
</head>
<body>
...
</body>
</html>CDN 选项:
| URL | 使用场景 |
|---|---|
| 推荐。 最新的稳定版 v1.x.x |
| 最新开发版——仅用于测试 |
| 锁定特定版本 |
脚本加载后,SDK 会暴露一个全局 对象。
bridgeConfiguration file
配置文件
Bridge reads from the build root. It declares platform IDs, IAP catalog, ad placements, leaderboards and other settings. Generate or edit it with the config editor.
playgama-bridge-config.jsonSkeleton:
json
{
"platforms": {
"playgama": { "options": {} },
"yandex": { "options": {} }
},
"advertisement": {
"interstitial": { "minimumDelay": 60 },
"advancedBanners": {/* see Advanced Banners */}
},
"payments": [
{
"id": "coins_100",
"playgama": { "amount": 1 },
"playdeck": { "amount": 1, "description": "100 Coins" }
}
],
"leaderboards": [{ "id": "high_score" }]
}Bridge 会从构建根目录读取 。该文件用于声明平台 ID、IAP 商品目录、广告版位、排行榜及其他设置。可以使用配置编辑器生成或编辑此文件。
playgama-bridge-config.json模板:
json
{
"platforms": {
"playgama": { "options": {} },
"yandex": { "options": {} }
},
"advertisement": {
"interstitial": { "minimumDelay": 60 },
"advancedBanners": {/* see Advanced Banners */}
},
"payments": [
{
"id": "coins_100",
"playgama": { "amount": 1 },
"playdeck": { "amount": 1, "description": "100 Coins" }
}
],
"leaderboards": [{ "id": "high_score" }]
}Initialization
初始化
Call and wait for the promise before touching any other API.
bridge.initialize()javascript
bridge
.initialize()
.then(() => {
// SDK is ready, all bridge.* APIs are usable now
})
.catch((error) => {
// Initialization failed — handle gracefully (the SDK still falls back to mock)
});After init, send once the game is fully loaded and the player can interact:
game_readyjavascript
bridge.platform.sendMessage('game_ready');This dismisses the platform loader. It is required — without it most platforms keep showing their splash on top of the game.
调用 ,并且必须等待 Promise 完成后再使用其他任何 API。
bridge.initialize()javascript
bridge
.initialize()
.then(() => {
// SDK is ready, all bridge.* APIs are usable now
})
.catch((error) => {
// Initialization failed — handle gracefully (the SDK still falls back to mock)
});初始化完成后,在游戏完全加载且玩家可以进行交互时发送一次 :
game_readyjavascript
bridge.platform.sendMessage('game_ready');这会关闭平台加载界面。该消息是必需的,否则大多数平台会继续在游戏上方显示启动画面。
Required integration checklist
必需集成检查清单
For every game:
- Install SDK + create .
playgama-bridge-config.json - Wait for .
bridge.initialize() - Read and apply localization.
bridge.platform.language - Load saved progress with .
bridge.storage.get(...) - Send once the first playable frame is rendered.
bridge.platform.sendMessage('game_ready') - React to and
AUDIO_STATE_CHANGEDevents.PAUSE_STATE_CHANGED - Show interstitial ads at natural breakpoints (level transitions, game over, menu↔gameplay).
Interstitials are mandatory for revenue share. Without them games can be rejected during moderation.
对于每款游戏:
- 安装 SDK 并创建 。
playgama-bridge-config.json - 等待 完成。
bridge.initialize() - 读取 并应用本地化。
bridge.platform.language - 使用 加载已保存的进度。
bridge.storage.get(...) - 首个可玩帧渲染完成后,发送 。
bridge.platform.sendMessage('game_ready') - 响应 和
AUDIO_STATE_CHANGED事件。PAUSE_STATE_CHANGED - 在自然断点展示插屏广告(关卡切换、游戏结束、菜单与游戏场景之间切换)。
插屏广告是参与收入分成所必需的。缺少插屏广告时,游戏可能会在审核期间被拒绝。
Platform module — bridge.platform
bridge.platform平台模块——bridge.platform
bridge.platformProperties
属性
| Property | Type | Description |
|---|---|---|
| string | Current platform ID. One of: |
| object | null | Native platform SDK (when Bridge exposes it). Use only for things Bridge doesn't wrap |
| string | ISO 639-1 user language ( |
| string | null | URL launch payload (referral code, deep link). Format is platform-specific |
| string | null | Top-level domain ( |
| boolean | Whether the host platform currently allows game audio |
| boolean | Capability flag for |
| boolean | Capability flag for |
| 属性 | 类型 | 说明 |
|---|---|---|
| string | 当前平台 ID,取值包括: |
| object | null | Bridge 暴露时为原生平台 SDK。仅用于 Bridge 未封装的功能 |
| string | ISO 639-1 用户语言( |
| string | null | URL 启动载荷(推荐码、深层链接)。格式因平台而异 |
| string | null | 宿主提供时为顶级域名( |
| boolean | 宿主平台当前是否允许游戏音频 |
| boolean | 是否支持 |
| boolean | 是否支持 |
Methods
方法
javascript
// Server time — UTC ms — use for daily rewards / cooldowns instead of local clock
bridge.platform
.getServerTime()
.then((utcMs) => console.log(utcMs))
.catch((error) => {});
// All games by the same developer (for "More games" screens)
bridge.platform.getAllGames().then((games) => {
// platform-specific shape, on yandex: { appID, title, url, coverURL, iconURL }
});
// One game by ID (cross-promotion)
bridge.platform.getGameById({ gameId: '111111' }).then((game) => {
// { appID, title, url, coverURL, iconURL, isAvailable }
});
// Lifecycle messages — see table below
bridge.platform.sendMessage('game_ready');
// Custom messages — used by Advanced Banners config
bridge.platform.sendCustomMessage('shop_opened');javascript
// Server time — UTC ms — use for daily rewards / cooldowns instead of local clock
bridge.platform
.getServerTime()
.then((utcMs) => console.log(utcMs))
.catch((error) => {});
// All games by the same developer (for "More games" screens)
bridge.platform.getAllGames().then((games) => {
// platform-specific shape, on yandex: { appID, title, url, coverURL, iconURL }
});
// One game by ID (cross-promotion)
bridge.platform.getGameById({ gameId: '111111' }).then((game) => {
// { appID, title, url, coverURL, iconURL, isAvailable }
});
// Lifecycle messages — see table below
bridge.platform.sendMessage('game_ready');
// Custom messages — used by Advanced Banners config
bridge.platform.sendCustomMessage('shop_opened');Built-in lifecycle messages
内置生命周期消息
| Message | Payload | When to send |
|---|---|---|
| — | Game loaded, loading screen done, player can interact (required) |
| — | Sub-loading begins (e.g. level load) |
| — | Sub-loading finished |
| — | Active gameplay begins |
| — | Active gameplay ends (menu, pause, game over) |
| — | Significant moment (boss defeated, record set) |
| | Level begins |
| | Level won |
| | Level lost |
| | Pause menu opened |
| | Resumed from pause |
These also trigger Advanced Banners placements automatically.
| 消息 | 载荷 | 发送时机 |
|---|---|---|
| — | 游戏已加载、加载界面结束且玩家可以交互(必需) |
| — | 子加载开始(例如加载关卡) |
| — | 子加载结束 |
| — | 活跃游戏开始 |
| — | 活跃游戏结束(菜单、暂停、游戏结束) |
| — | 重要时刻(击败 Boss、刷新纪录) |
| | 关卡开始 |
| | 关卡胜利 |
| | 关卡失败 |
| | 打开暂停菜单 |
| | 从暂停中恢复 |
这些消息还会自动触发 Advanced Banners 版位。
Events
事件
javascript
// Audio mute/unmute requests from the host
bridge.platform.on(bridge.EVENT_NAME.AUDIO_STATE_CHANGED, (isEnabled) => {
if (!isEnabled) muteGameAudio();
else restoreGameAudio();
});
// Pause/resume requests (tab switch, platform overlay, etc.)
bridge.platform.on(bridge.EVENT_NAME.PAUSE_STATE_CHANGED, (isPaused) => {
if (isPaused) pauseGame();
else resumeGame();
});Both events are required to handle — many platforms enforce this during moderation.
javascript
// Audio mute/unmute requests from the host
bridge.platform.on(bridge.EVENT_NAME.AUDIO_STATE_CHANGED, (isEnabled) => {
if (!isEnabled) muteGameAudio();
else restoreGameAudio();
});
// Pause/resume requests (tab switch, platform overlay, etc.)
bridge.platform.on(bridge.EVENT_NAME.PAUSE_STATE_CHANGED, (isPaused) => {
if (isPaused) pauseGame();
else resumeGame();
});这两个事件都是必须处理的——许多平台会在审核期间强制检查这些事件。
Player module — bridge.player
bridge.player玩家模块——bridge.player
bridge.playerPlayer identity and platform authorization.
| Property | Type | Description |
|---|---|---|
| string | null | Platform player ID, only when authorized |
| string | null | Display name from platform |
| string[] | Avatar URLs sorted by increasing resolution; |
| object | Platform-specific auth/verification data |
| boolean | Whether host supports a login flow |
| boolean | Whether the player is currently authorized |
javascript
// Trigger only from a direct user action (button tap)
bridge.player
.authorize({})
.then(() => {
// bridge.player.id, .name, .photos now populated
})
.catch((error) => {
// Cancelled or failed
});Some platforms expose guest data even before authorization (, partial ). Don't treat those as verified — only post-authorization data is trustworthy.
nameid用于管理玩家身份和平台授权。
| 属性 | 类型 | 说明 |
|---|---|---|
| string | null | 平台玩家 ID,仅在授权后可用 |
| string | null | 平台显示名称 |
| string[] | 头像 URL,按分辨率从低到高排列;没有时为 |
| object | 平台特定的授权/验证数据 |
| boolean | 宿主是否支持登录流程 |
| boolean | 玩家当前是否已授权 |
javascript
// Trigger only from a direct user action (button tap)
bridge.player
.authorize({})
.then(() => {
// bridge.player.id, .name, .photos now populated
})
.catch((error) => {
// Cancelled or failed
});某些平台会在授权前提供访客数据(、部分 )。不要将这些数据视为已验证数据——只有授权后的数据才可信。
nameidBackend verification
后端验证
Use to verify player identity from your server. Currently only , , and are supported by Playgama's verification API:
bridge.player.extraplaygamamsnmicrosoft_storebash
curl -X POST 'https://playgama.com/api/bridge/v1/verify' \
-H 'Content-Type: application/json' \
-d '{"platform":"<PLATFORM_ID>","type":"authorization","data":{ <...bridge.player.extra> }}'使用 在你的服务器上验证玩家身份。目前,Playgama 的验证 API 仅支持 、 和 :
bridge.player.extraplaygamamsnmicrosoft_storebash
curl -X POST 'https://playgama.com/api/bridge/v1/verify' \
-H 'Content-Type: application/json' \
-d '{"platform":"<PLATFORM_ID>","type":"authorization","data":{ <...bridge.player.extra> }}'Response: { success, errorMessage?, playerId?, publisherPlayerId? }
Response: { success, errorMessage?, playerId?, publisherPlayerId? }
undefinedundefinedDevice module — bridge.device
bridge.device设备模块——bridge.device
bridge.devicejavascript
bridge.device.type; // 'mobile' | 'tablet' | 'desktop' | 'tv'Use for broad UI adaptation — touch vs. keyboard input, asset quality, layout. Don't make business logic decisions on device type.
javascript
bridge.device.type; // 'mobile' | 'tablet' | 'desktop' | 'tv'用于进行粗粒度 UI 适配——触控与键盘输入、素材质量、布局等。不要根据设备类型做出业务逻辑决策。
Storage module — bridge.storage
bridge.storage存储模块——bridge.storage
bridge.storagePersistent player data (progress, settings, currency). Almost every game needs this.
用于持久化玩家数据(进度、设置、货币)。几乎每款游戏都需要此功能。
Two storage types
两种存储类型
| Type | Persistence | Cross-device | When |
|---|---|---|---|
| Browser localStorage | No | Default for offline single-device save |
| Platform cloud servers | Yes | Player is authorized + you want cloud sync |
javascript
bridge.storage.defaultType;
// 'local_storage' or 'platform_internal' — host's preferred type
bridge.storage.isAvailable('local_storage');
bridge.storage.isAvailable('platform_internal');
// Check before forcing a specific type| 类型 | 持久化方式 | 跨设备 | 使用场景 |
|---|---|---|---|
| 浏览器 localStorage | 否 | 离线单设备存档的默认选择 |
| 平台云服务器 | 是 | 玩家已授权且希望使用云同步 |
javascript
bridge.storage.defaultType;
// 'local_storage' or 'platform_internal' — host's preferred type
bridge.storage.isAvailable('local_storage');
bridge.storage.isAvailable('platform_internal');
// Check before forcing a specific typeLoad
读取
javascript
// Single key
bridge.storage.get('level').then((value) => {
// value === null when key is missing — always have a default in your game state
});
// Multiple keys — returns array, same order
bridge.storage.get(['level', 'coins', 'settings']).then(([level, coins, settings]) => {});
// Force specific storage type (third arg)
bridge.storage.get('level', 'local_storage');javascript
// Single key
bridge.storage.get('level').then((value) => {
// value === null when key is missing — always have a default in your game state
});
// Multiple keys — returns array, same order
bridge.storage.get(['level', 'coins', 'settings']).then(([level, coins, settings]) => {});
// Force specific storage type (third arg)
bridge.storage.get('level', 'local_storage');Save
保存
javascript
bridge.storage.set('level', 'dungeon_3');
bridge.storage
.set(['level', 'coins', 'tutorial_done'], ['dungeon_3', 42, true])
.then(() => {
/* persisted */
})
.catch((error) => {});
bridge.storage.set('level', 'dungeon_3', 'platform_internal');Save on meaningful state change (level done, purchase, settings change). Do not save every frame.
javascript
bridge.storage.set('level', 'dungeon_3');
bridge.storage
.set(['level', 'coins', 'tutorial_done'], ['dungeon_3', 42, true])
.then(() => {
/* persisted */
})
.catch((error) => {});
bridge.storage.set('level', 'dungeon_3', 'platform_internal');在有意义的状态变化时保存(关卡完成、购买、设置变更)。不要每帧都保存。
Delete
删除
javascript
bridge.storage.delete('level');
bridge.storage.delete(['level', 'coins']);javascript
bridge.storage.delete('level');
bridge.storage.delete(['level', 'coins']);Advertisement module — bridge.advertisement
bridge.advertisement广告模块——bridge.advertisement
bridge.advertisementplaygama-bridge-config.json广告版位由 控制。游戏进行中绝不要展示广告——只在自然停顿期间展示。
playgama-bridge-config.jsonFormat overview
格式概览
| Format | Status | When |
|---|---|---|
| Interstitial | Required | Level transitions, game over, menu↔gameplay |
| Rewarded | Recommended | Player opts in for in-game reward |
| Banner | Recommended | Idle screens (menu, lobby, pause) |
| Advanced Banners | Recommended | Custom multi-position responsive banner layouts |
| AdBlock check | Optional | Adapt UI when ads are blocked |
| 格式 | 状态 | 使用场景 |
|---|---|---|
| 插屏广告 | 必需 | 关卡切换、游戏结束、菜单与游戏场景之间切换 |
| 激励广告 | 推荐 | 玩家主动选择观看广告以获得游戏内奖励 |
| Banner | 推荐 | 空闲界面(菜单、大厅、暂停) |
| Advanced Banners | 推荐 | 自定义多位置响应式 Banner 布局 |
| AdBlock 检查 | 可选 | 广告被屏蔽时调整 UI |
Interstitial — required
插屏广告——必需
javascript
bridge.advertisement.isInterstitialSupported; // boolean
bridge.advertisement.minimumDelayBetweenInterstitial; // default 60 seconds
bridge.advertisement.setMinimumDelayBetweenInterstitial(30); // override
bridge.advertisement.interstitialState;
// 'loading' | 'opened' | 'closed' | 'failed'
bridge.advertisement.on(bridge.EVENT_NAME.INTERSTITIAL_STATE_CHANGED, (state) => {
if (state === 'opened') {
muteGameAudio();
pauseGame();
} else if (state === 'closed' || state === 'failed') {
restoreGameAudio();
resumeGame();
}
});
// Optional placement string
bridge.advertisement.showInterstitial('level_complete');Do not call at game start — platforms that support a startup interstitial show it automatically. Calling it explicitly causes duplicate ads.
showInterstitial()Also check right after init: if it is already , mute and pause immediately.
interstitialStateopenedjavascript
bridge.advertisement.isInterstitialSupported; // boolean
bridge.advertisement.minimumDelayBetweenInterstitial; // default 60 seconds
bridge.advertisement.setMinimumDelayBetweenInterstitial(30); // override
bridge.advertisement.interstitialState;
// 'loading' | 'opened' | 'closed' | 'failed'
bridge.advertisement.on(bridge.EVENT_NAME.INTERSTITIAL_STATE_CHANGED, (state) => {
if (state === 'opened') {
muteGameAudio();
pauseGame();
} else if (state === 'closed' || state === 'failed') {
restoreGameAudio();
resumeGame();
}
});
// Optional placement string
bridge.advertisement.showInterstitial('level_complete');在游戏开始时不要调用 ——支持启动插屏广告的平台会自动展示它。显式调用会导致广告重复。
showInterstitial()初始化后还应立即检查 :如果它已经是 ,应立即静音并暂停游戏。
interstitialStateopenedRewarded
激励广告
javascript
bridge.advertisement.isRewardedSupported;
bridge.advertisement.rewardedState;
// 'loading' | 'opened' | 'closed' | 'rewarded' | 'failed'
bridge.advertisement.rewardedPlacement; // current placement string
bridge.advertisement.on(bridge.EVENT_NAME.REWARDED_STATE_CHANGED, (state) => {
switch (state) {
case 'opened':
muteGameAudio();
pauseGame();
break;
case 'rewarded':
// ONLY here grant the reward
grantReward(bridge.advertisement.rewardedPlacement);
break;
case 'closed':
case 'failed':
restoreGameAudio();
resumeGame();
break;
}
});
bridge.advertisement.showRewarded('double_coins');Critical: grant the reward only on the state. Granting on lets players claim rewards without watching the ad.
rewardedclosedjavascript
bridge.advertisement.isRewardedSupported;
bridge.advertisement.rewardedState;
// 'loading' | 'opened' | 'closed' | 'rewarded' | 'failed'
bridge.advertisement.rewardedPlacement; // current placement string
bridge.advertisement.on(bridge.EVENT_NAME.REWARDED_STATE_CHANGED, (state) => {
switch (state) {
case 'opened':
muteGameAudio();
pauseGame();
break;
case 'rewarded':
// ONLY here grant the reward
grantReward(bridge.advertisement.rewardedPlacement);
break;
case 'closed':
case 'failed':
restoreGameAudio();
resumeGame();
break;
}
});
bridge.advertisement.showRewarded('double_coins');**关键:**仅在 状态下发放奖励。如果在 时发放奖励,玩家不观看广告也能领取奖励。
rewardedclosedBanner
Banner
javascript
bridge.advertisement.isBannerSupported;
let position = 'bottom'; // 'top' | 'bottom', default 'bottom'
let placement = 'menu'; // optional
bridge.advertisement.showBanner(position, placement);
bridge.advertisement.hideBanner();
bridge.advertisement.bannerState;
// 'loading' | 'shown' | 'hidden' | 'failed'
bridge.advertisement.on(bridge.EVENT_NAME.BANNER_STATE_CHANGED, (state) => {});javascript
bridge.advertisement.isBannerSupported;
let position = 'bottom'; // 'top' | 'bottom', default 'bottom'
let placement = 'menu'; // optional
bridge.advertisement.showBanner(position, placement);
bridge.advertisement.hideBanner();
bridge.advertisement.bannerState;
// 'loading' | 'shown' | 'hidden' | 'failed'
bridge.advertisement.on(bridge.EVENT_NAME.BANNER_STATE_CHANGED, (state) => {});Advanced Banners
Advanced Banners
Multi-position, responsive banner layouts driven by config. Auto-hidden during interstitials/rewarded, auto-restored after. Re-evaluate on orientation/resize (200ms debounce).
javascript
bridge.advertisement.isAdvancedBannersSupported;
// Method 1 — explicit
bridge.advertisement.showAdvancedBanners('main_menu');
bridge.advertisement.hideAdvancedBanners();
// Method 2 — automatic via platform messages (recommended)
bridge.platform.sendMessage('gameplay_stopped'); // shows if configured
bridge.platform.sendMessage('gameplay_started'); // hides if action: 'hide'
bridge.platform.sendCustomMessage('shop_opened');
// State
bridge.advertisement.advancedBannersState;
bridge.advertisement.on('advanced_banners_state_changed', (state) => {});Config example:
json
{
"advertisement": {
"advancedBanners": {
"disable": false,
"placementFallback": "main_menu",
"main_menu": {
"default": [{ "width": "20%", "height": "25%", "top": "10%", "right": "10%" }]
},
"gameplay_stopped": {
"action": "show",
"default": [{ "width": "20%", "height": "25%", "bottom": "10%", "right": "5%" }],
"mobile:portrait": [
{ "width": "100%", "height": "8%", "bottom": "0", "left": "0" }
],
"desktop:landscape:w>1200": [
{ "width": "10%", "height": "60%", "top": "10%", "left": "1%" },
{ "width": "10%", "height": "60%", "top": "10%", "right": "1%" }
]
},
"gameplay_started": { "action": "hide" }
}
}
}由配置驱动的多位置响应式 Banner 布局。插屏广告或激励广告期间会自动隐藏,并在之后自动恢复。屏幕方向或尺寸变化时需要重新评估(200ms 防抖)。
javascript
bridge.advertisement.isAdvancedBannersSupported;
// Method 1 — explicit
bridge.advertisement.showAdvancedBanners('main_menu');
bridge.advertisement.hideAdvancedBanners();
// Method 2 — automatic via platform messages (recommended)
bridge.platform.sendMessage('gameplay_stopped'); // shows if configured
bridge.platform.sendMessage('gameplay_started'); // hides if action: 'hide'
bridge.platform.sendCustomMessage('shop_opened');
// State
bridge.advertisement.advancedBannersState;
bridge.advertisement.on('advanced_banners_state_changed', (state) => {});配置示例:
json
{
"advertisement": {
"advancedBanners": {
"disable": false,
"placementFallback": "main_menu",
"main_menu": {
"default": [{ "width": "20%", "height": "25%", "top": "10%", "right": "10%" }]
},
"gameplay_stopped": {
"action": "show",
"default": [{ "width": "20%", "height": "25%", "bottom": "10%", "right": "5%" }],
"mobile:portrait": [
{ "width": "100%", "height": "8%", "bottom": "0", "left": "0" }
],
"desktop:landscape:w>1200": [
{ "width": "10%", "height": "60%", "top": "10%", "left": "1%" },
{ "width": "10%", "height": "60%", "top": "10%", "right": "1%" }
]
},
"gameplay_started": { "action": "hide" }
}
}
}Condition keys
条件键
Colon-separated segments, all must match:
| Segment | Examples |
|---|---|
| Device type | |
| Orientation | |
| Width | |
| Height | |
| Aspect ratio | |
| Canvas mode | |
Priority: device type +4, orientation +2, canvas +1, each w/h/ar condition +1. Ties broken by more segments → closer threshold to actual value. is fallback (-1).
default以冒号分隔的片段,必须全部匹配:
| 片段 | 示例 |
|---|---|
| 设备类型 | |
| 屏幕方向 | |
| 宽度 | |
| 高度 | |
| 宽高比 | |
| Canvas 模式 | |
优先级:设备类型 +4、屏幕方向 +2、canvas +1,每个 w/h/ar 条件 +1。并列时,片段更多者优先,然后选择阈值更接近实际值的条件。 是回退项(-1)。
defaultBanner object
Banner 对象
CSS positioning: , , , , , — all strings (, , ).
widthheighttopbottomleftright"300px""100%""0"CSS 定位属性:、、、、、——全部为字符串(、、)。
widthheighttopbottomleftright"300px""100%""0"AdBlock detection
AdBlock 检测
javascript
bridge.advertisement.checkAdBlock().then((isBlocked) => {
// true | false — informational only, doesn't suppress ads
});javascript
bridge.advertisement.checkAdBlock().then((isBlocked) => {
// true | false — informational only, doesn't suppress ads
});Payments module — bridge.payments
bridge.payments支付模块——bridge.payments
bridge.paymentsIn-game purchases. Optional but often the highest-revenue feature.
用于应用内购买。可选,但通常是最具收益的功能。
Product types
商品类型
| Type | Example | Lifecycle |
|---|---|---|
| Permanent | "Remove ads", DLC | Bought once. Check ownership with |
| Consumable | Coins, gems, energy | Bought → granted → must be consumed with |
| 类型 | 示例 | 生命周期 |
|---|---|---|
| 永久型 | "Remove ads"、DLC | 一次性购买。每次启动时使用 |
| 可消耗型 | 金币、宝石、能量 | 购买 → 发放 → 必须消耗,调用 |
API
API
javascript
bridge.payments.isSupported;
// Catalog — localized prices, currencies, metadata
bridge.payments.getCatalog().then((items) => {
items.forEach((item) => {
console.log(item.id);
console.log(item.price); // formatted string
console.log(item.priceCurrencyCode); // ISO 4217
console.log(item.priceValue); // numeric value
});
});
// Purchase — only from a direct user action
bridge.payments
.purchase('coins_100')
.then((purchase) => {
// Platform-specific shape, always has .id
grantItems(purchase.id);
// For consumables, immediately consume:
return bridge.payments.consumePurchase(purchase.id);
})
.catch((error) => {
// Cancelled or failed
});
// Consume a consumable after granting items
bridge.payments.consumePurchase('coins_100').then((purchase) => {});
// Active purchases — call on every launch and after reconnect
// to recover items from purchases interrupted by disconnects
bridge.payments.getPurchases().then((purchases) => {
purchases.forEach((p) => {
// Permanent: check ownership here
// Consumable: grant items + consume
});
});javascript
bridge.payments.isSupported;
// Catalog — localized prices, currencies, metadata
bridge.payments.getCatalog().then((items) => {
items.forEach((item) => {
console.log(item.id);
console.log(item.price); // formatted string
console.log(item.priceCurrencyCode); // ISO 4217
console.log(item.priceValue); // numeric value
});
});
// Purchase — only from a direct user action
bridge.payments
.purchase('coins_100')
.then((purchase) => {
// Platform-specific shape, always has .id
grantItems(purchase.id);
// For consumables, immediately consume:
return bridge.payments.consumePurchase(purchase.id);
})
.catch((error) => {
// Cancelled or failed
});
// Consume a consumable after granting items
bridge.payments.consumePurchase('coins_100').then((purchase) => {});
// Active purchases — call on every launch and after reconnect
// to recover items from purchases interrupted by disconnects
bridge.payments.getPurchases().then((purchases) => {
purchases.forEach((p) => {
// Permanent: check ownership here
// Consumable: grant items + consume
});
});Catalog config
商品目录配置
json
{
"payments": [
{
"id": "coins_100",
"playgama": { "amount": 1 },
"playdeck": { "amount": 1, "description": "100 Coins" }
}
]
}idjson
{
"payments": [
{
"id": "coins_100",
"playgama": { "amount": 1 },
"playdeck": { "amount": 1, "description": "100 Coins" }
}
]
}idBackend verification
后端验证
bash
curl -X POST 'https://playgama.com/api/bridge/v1/verify' \
-H 'Content-Type: application/json' \
-d '{"platform":"<PLATFORM_ID>","type":"purchase","data":{ <...purchase> }}'bash
curl -X POST 'https://playgama.com/api/bridge/v1/verify' \
-H 'Content-Type: application/json' \
-d '{"platform":"<PLATFORM_ID>","type":"purchase","data":{ <...purchase> }}'Response: { success, errorMessage?, orderId?, productId?, externalId? }
Response: { success, errorMessage?, orderId?, productId?, externalId? }
Currently supports `playgama`, `msn`, `microsoft_store`.
目前支持 `playgama`、`msn` 和 `microsoft_store`。Leaderboards module — bridge.leaderboards
bridge.leaderboards排行榜模块——bridge.leaderboards
bridge.leaderboardsOptional. First check :
bridge.leaderboards.type| Type | Action |
|---|---|
| Hide all leaderboard UI |
| Call |
| Call |
| Call |
可选。首先检查 :
bridge.leaderboards.type| 类型 | 操作 |
|---|---|
| 隐藏所有排行榜 UI |
| 调用 |
| 仅调用 |
| 调用 |
API
API
javascript
bridge.leaderboards.type;
bridge.leaderboards.setScore('high_score', 1000).then(() => {});
// Only when type === 'in_game'
bridge.leaderboards.getEntries('high_score').then((entries) => {
entries.forEach((e) => {
console.log(e.id);
console.log(e.name);
console.log(e.photo);
console.log(e.score);
console.log(e.rank);
});
});
// Only when type === 'native_popup'
bridge.leaderboards.showNativePopup('high_score');javascript
bridge.leaderboards.type;
bridge.leaderboards.setScore('high_score', 1000).then(() => {});
// Only when type === 'in_game'
bridge.leaderboards.getEntries('high_score').then((entries) => {
entries.forEach((e) => {
console.log(e.id);
console.log(e.name);
console.log(e.photo);
console.log(e.score);
console.log(e.rank);
});
});
// Only when type === 'native_popup'
bridge.leaderboards.showNativePopup('high_score');Config
配置
json
{
"leaderboards": [
{
"id": "high_score",
"<PLATFORM_ID>": "<NATIVE_ID_FOR_PLATFORM>"
}
]
}Use the in-game in code; per-platform overrides map to native leaderboard IDs when they differ.
idjson
{
"leaderboards": [
{
"id": "high_score",
"<PLATFORM_ID>": "<NATIVE_ID_FOR_PLATFORM>"
}
]
}代码中使用游戏内 ;当各平台的原生排行榜 ID 不同时,可通过平台专属覆盖项进行映射。
idAchievements module — bridge.achievements
bridge.achievements成就模块——bridge.achievements
bridge.achievementsOptional. Achievement IDs are configured in each platform's developer dashboard, not in .
playgama-bridge-config.jsonjavascript
bridge.achievements.isSupported;
bridge.achievements.isGetListSupported;
bridge.achievements.isNativePopupSupported;
// Unlock — send each achievement only once
let options = {};
switch (bridge.platform.id) {
case 'y8':
options = { achievement: 'ACHIEVEMENT_NAME', achievementkey: 'ACHIEVEMENT_KEY' };
break;
case 'lagged':
options = { achievement: 'ACHIEVEMENT_ID' };
break;
}
bridge.achievements.unlock(options).then(() => {});
// List (platform-specific shape)
bridge.achievements.getList({}).then((list) => {});
// Native overlay
bridge.achievements.showNativePopup({});Keep a constants file in your game that maps internal milestones to per-platform achievement IDs.
可选。成就 ID 在每个平台的开发者控制台中配置,而不是在 中配置。
playgama-bridge-config.jsonjavascript
bridge.achievements.isSupported;
bridge.achievements.isGetListSupported;
bridge.achievements.isNativePopupSupported;
// Unlock — send each achievement only once
let options = {};
switch (bridge.platform.id) {
case 'y8':
options = { achievement: 'ACHIEVEMENT_NAME', achievementkey: 'ACHIEVEMENT_KEY' };
break;
case 'lagged':
options = { achievement: 'ACHIEVEMENT_ID' };
break;
}
bridge.achievements.unlock(options).then(() => {});
// List (platform-specific shape)
bridge.achievements.getList({}).then((list) => {});
// Native overlay
bridge.achievements.showNativePopup({});在游戏项目中维护一个常量文件,将内部里程碑映射到各平台的成就 ID。
Social module — bridge.social
bridge.social社交模块——bridge.social
bridge.socialAll optional. Each method has an flag — always check it and hide the button when unsupported. Trigger only from direct user action.
is*Supported所有功能均为可选。每个方法都有 标志——务必检查,并在不支持时隐藏按钮。仅可通过用户的直接操作触发。
is*SupportedCapability matrix
能力矩阵
| Feature | Flag | Typical platforms |
|---|---|---|
| Share | | VK, Facebook, MSN |
| Join community | | VK, OK, Facebook |
| Invite friends | | VK, OK, Facebook |
| Create post | | VK, OK |
| Add to favorites | | VK, Yandex |
| Add to home screen | | Yandex, GameSnacks |
| Rate game | | VK, GameSnacks, Yandex |
| External links | | Most non-portal platforms |
| 功能 | 标志 | 常见平台 |
|---|---|---|
| 分享 | | VK、Facebook、MSN |
| 加入社区 | | VK、OK、Facebook |
| 邀请好友 | | VK、OK、Facebook |
| 创建帖子 | | VK、OK |
| 添加到收藏 | | VK、Yandex |
| 添加到主屏幕 | | Yandex、GameSnacks |
| 评价游戏 | | VK、GameSnacks、Yandex |
| 外部链接 | | 大多数非门户平台 |
Share
分享
javascript
let options = {};
switch (bridge.platform.id) {
case 'vk':
options = { link: 'https://...' };
break;
case 'facebook':
options = { image: 'base64...', text: 'Check this game!' };
break;
case 'msn':
options = { title: '...', image: 'base64 or URL', text: '...' };
break;
}
bridge.social.share(options).then(() => {});javascript
let options = {};
switch (bridge.platform.id) {
case 'vk':
options = { link: 'https://...' };
break;
case 'facebook':
options = { image: 'base64...', text: 'Check this game!' };
break;
case 'msn':
options = { title: '...', image: 'base64 or URL', text: '...' };
break;
}
bridge.social.share(options).then(() => {});Join community
加入社区
javascript
let options = {};
switch (bridge.platform.id) {
case 'vk':
case 'ok':
options = { groupId: 123456 };
break;
case 'facebook':
options = { isPage: true };
break; // false → group
}
bridge.social.joinCommunity(options);javascript
let options = {};
switch (bridge.platform.id) {
case 'vk':
case 'ok':
options = { groupId: 123456 };
break;
case 'facebook':
options = { isPage: true };
break; // false → group
}
bridge.social.joinCommunity(options);Invite friends
邀请好友
javascript
let options = {};
switch (bridge.platform.id) {
case 'ok':
options = { text: 'Hello!' };
break;
case 'facebook':
options = { image: 'base64...', text: 'Join me!' };
break;
}
bridge.social.inviteFriends(options);javascript
let options = {};
switch (bridge.platform.id) {
case 'ok':
options = { text: 'Hello!' };
break;
case 'facebook':
options = { image: 'base64...', text: 'Join me!' };
break;
}
bridge.social.inviteFriends(options);Create post
创建帖子
javascript
// OK example with mixed media
bridge.social.createPost({
media: [
{ type: 'text', text: 'Hello!' },
{ type: 'link', url: 'https://...' },
{
type: 'poll',
question: 'Like the game?',
answers: [{ text: 'Yes' }, { text: 'No' }],
options: 'SingleChoice,AnonymousVoting'
}
]
});javascript
// OK example with mixed media
bridge.social.createPost({
media: [
{ type: 'text', text: 'Hello!' },
{ type: 'link', url: 'https://...' },
{
type: 'poll',
question: 'Like the game?',
answers: [{ text: 'Yes' }, { text: 'No' }],
options: 'SingleChoice,AnonymousVoting'
}
]
});Other social
其他社交功能
javascript
bridge.social.addToFavorites().then(() => {});
bridge.social.addToHomeScreen().then(() => {});
bridge.social.rate().then(() => {});javascript
bridge.social.addToFavorites().then(() => {});
bridge.social.addToHomeScreen().then(() => {});
bridge.social.rate().then(() => {});Remote Config module — bridge.remoteConfig
bridge.remoteConfig远程配置模块——bridge.remoteConfig
bridge.remoteConfigOptional. Read game settings (difficulty, drop rates, feature flags, A/B values) at runtime without rebuilding.
javascript
bridge.remoteConfig.isSupported;
let options = {};
switch (bridge.platform.id) {
case 'yandex':
options = {
clientFeatures: [{ name: 'levels', value: '5' }]
};
break;
}
bridge.remoteConfig
.get(options)
.then((data) => {
// e.g. { showFullscreenAdOnStart: 'no', difficulty: 'hard' }
})
.catch((error) => {});Always have hardcoded defaults for every key. The request can fail or return partial data.
可选。在运行时读取游戏设置(难度、掉率、功能开关、A/B 测试值),无需重新构建。
javascript
bridge.remoteConfig.isSupported;
let options = {};
switch (bridge.platform.id) {
case 'yandex':
options = {
clientFeatures: [{ name: 'levels', value: '5' }]
};
break;
}
bridge.remoteConfig
.get(options)
.then((data) => {
// e.g. { showFullscreenAdOnStart: 'no', difficulty: 'hard' }
})
.catch((error) => {});必须为每个键提供硬编码默认值。请求可能失败,也可能只返回部分数据。
Event names
事件名称
javascript
bridge.EVENT_NAME.AUDIO_STATE_CHANGED;
bridge.EVENT_NAME.PAUSE_STATE_CHANGED;
bridge.EVENT_NAME.INTERSTITIAL_STATE_CHANGED;
bridge.EVENT_NAME.REWARDED_STATE_CHANGED;
bridge.EVENT_NAME.BANNER_STATE_CHANGED;
bridge.EVENT_NAME.ADVANCED_BANNERS_STATE_CHANGED;bridge.platform.on(...)bridge.advertisement.on(...)javascript
bridge.EVENT_NAME.AUDIO_STATE_CHANGED;
bridge.EVENT_NAME.PAUSE_STATE_CHANGED;
bridge.EVENT_NAME.INTERSTITIAL_STATE_CHANGED;
bridge.EVENT_NAME.REWARDED_STATE_CHANGED;
bridge.EVENT_NAME.BANNER_STATE_CHANGED;
bridge.EVENT_NAME.ADVANCED_BANNERS_STATE_CHANGED;bridge.platform.on(...)bridge.advertisement.on(...)State value reference
状态值参考
| Module | States |
|---|---|
| Interstitial | |
| Rewarded | |
| Banner | |
| Advanced Banners | |
| Leaderboards type | |
| Storage type | |
| 模块 | 状态 |
|---|---|
| 插屏广告 | |
| 激励广告 | |
| Banner | |
| Advanced Banners | |
| 排行榜类型 | |
| 存储类型 | |
Best-practice checklist
最佳实践检查清单
- Wait for before using any API.
bridge.initialize() - Apply to localization.
bridge.platform.language - Load saved state from before showing gameplay.
bridge.storage.get(...) - Send when first playable frame is rendered.
bridge.platform.sendMessage('game_ready') - Subscribe to and
AUDIO_STATE_CHANGEDand react immediately.PAUSE_STATE_CHANGED - Show interstitials at natural breakpoints — never during gameplay.
- Never call at game start.
showInterstitial() - Mute + pause on ; restore on
opened/closed/failed.rewarded - Grant rewarded rewards only on the state.
rewarded - Save storage on meaningful events, not every frame.
- After granting items for a consumable purchase, call .
consumePurchase(id) - On launch and reconnect, call to recover interrupted purchases.
getPurchases() - Check every flag and hide UI for unsupported features.
is*Supported - Trigger social actions and only from direct user input.
payments.purchase() - Provide hardcoded defaults for every Remote Config key.
- Send platform messages (,
level_started, etc.) — they drive analytics and Advanced Banners.gameplay_stopped
- 使用任何 API 前,等待 完成。
bridge.initialize() - 将 应用于本地化。
bridge.platform.language - 显示游戏内容前,通过 加载已保存的状态。
bridge.storage.get(...) - 首个可玩帧渲染完成后,发送 。
bridge.platform.sendMessage('game_ready') - 订阅 和
AUDIO_STATE_CHANGED,并立即作出响应。PAUSE_STATE_CHANGED - 在自然断点展示插屏广告——游戏进行中绝不能展示。
- 在游戏开始时绝不调用 。
showInterstitial() - 状态变为 时静音并暂停;变为
opened、closed或failed时恢复。rewarded - 仅在 状态下发放激励广告奖励。
rewarded - 在有意义的事件发生时保存存储数据,而不是每帧都保存。
- 发放可消耗型商品后,调用 。
consumePurchase(id) - 启动和重新连接时,调用 以恢复因断线而中断的购买。
getPurchases() - 检查每个 标志,并隐藏不受支持功能的 UI。
is*Supported - 仅通过用户的直接输入触发社交操作和 。
payments.purchase() - 为每个 Remote Config 键提供硬编码默认值。
- 发送平台消息(、
level_started等)——它们会驱动分析统计和 Advanced Banners。gameplay_stopped
Local development
本地开发
In environments Bridge doesn't recognize it falls back to . APIs return safe defaults instead of throwing, so you can develop and debug locally before publishing. indicates this state.
mockbridge.platform.id === 'mock'在 Bridge 无法识别的环境中,会回退到 。API 会返回安全默认值而不是抛出异常,因此你可以在发布前进行本地开发和调试。 表示当前处于此状态。
mockbridge.platform.id === 'mock'Resources
资源
- Config editor:
https://playgama.github.io/bridge-config-editor/ - Wiki:
https://wiki.playgama.com/
- 配置编辑器:
https://playgama.github.io/bridge-config-editor/ - Wiki:
https://wiki.playgama.com/