コンテンツにスキップ

Unity GameKit 接入指南

このコンテンツはまだ日本語訳がありません。

本页由包内 Documentation~/DEVELOPER-GUIDE.zh-CN.md 自动同步生成;源文件:adapters/unity/Runtime/IGP.UnitySDK.GameKit/Documentation~/DEVELOPER-GUIDE.zh-CN.md

这份文档面向 Unity 游戏开发者,说明如何在 Unity 工程中接入和调用 IGP.UnitySDK.GameKit

当前 GameKit 包包含 Profile 当前用户资料、Leaderboard 排行榜和 Share 分享能力。它们走主包 IGP.UnitySDK 的 desktop session 路径;游戏侧不持有用户 JWT,用户上下文、API 转发或桌面自主流程由 desktop 处理。SDK 会从 IGPConfig.appId 读取当前游戏 AppID,并按 Runtime API 契约写入需要的 appId/gameId

先安装主包:

adapters/unity/Runtime/IGP.UnitySDK/package.json

再安装 GameKit 可选包:

adapters/unity/Runtime/IGP.UnitySDK.GameKit/package.json

正式发布包对应:

cn.indiegp.sdk.unity.game-kit-<version>.unitypackage

代码命名空间:

using IGP.UnitySDK.GameKit;

Leaderboard 调用通过下面路径完成:

Unity game
-> IGPLeaderboard
-> IGPRuntimeManager.LeaderboardForwardAsync
-> desktop session command LeaderboardForward
-> Curio desktop
-> Runtime Leaderboard API

游戏不需要直接访问后端 API,不需要持有用户 JWT,也不需要自己拼认证 headers。

  1. IGPConfig.appId 已配置。
  2. 场景里有且只有一个长期有效的 IGPRuntimeManager
  3. 游戏启动流程已调用 IGPRuntimeManager.InitializeAsync()
  4. desktop session attach 成功。
  5. 读取 Profile 时,desktop 当前用户已登录且返回的 capabilities.userContexttrue
  6. 查询 Players 时,desktop 当前用户已登录且返回的 capabilities.userContexttrue
  7. 使用排行榜时,desktop 返回的 capabilities.leaderboardtrue
  8. 使用分享时,desktop 返回的 capabilities.sharetrue

如果 capability 不可用,调用会失败,错误码为 DESKTOP_SESSION_CAPABILITY_MISSING

IGPProfile.GetProfileAsync(...) 通过 desktop session command GetDesktopUserProfile 主动获取当前登录用户最新的完整 SessionResponse.profile。desktop 负责认证和 session 刷新,游戏不持有平台 JWT。

using IGP.UnitySDK;
using IGP.UnitySDK.GameKit;
public sealed class ProfileDriver
{
private IGPRuntimeManager runtimeManager;
public async System.Threading.Tasks.Task LoadProfileAsync()
{
IGPUserProfile profile =
await IGPProfile.GetProfileAsync(runtimeManager);
UnityEngine.Debug.Log($"Avatar: {profile.avatarUrl}");
}
}

类型:IGPUserProfile

字段类型说明
avatarIGPProfileAvatarSelection?当前头像选择,包含 assetKeydisplayNameimageUrl
avatarUrlstring?当前头像 URL。
avatarFrameIGPProfileAvatarFrameSelection?当前头像框及 renderConfig
backgroundIGPProfileBackgroundSelection?当前主页背景选择。
backgroundUrlstring?当前主页背景 URL。
biostring?用户简介。
nameChangesint?已使用的改名次数。
lastChangedAtstring?上次改名时间,ISO 时间字符串。
lastActiveAtstring?最近活跃时间,ISO 时间字符串。
pendingNicknameReviewIGPPendingNicknameReview?当前待处理昵称审核。

avatarFrame.renderConfig 包含画布、头像区域、遮罩和前后景/动态效果图层。profile 中的头像是选择信息和 URL;需要统一的 256x256 PNG 字节时,调用主包已有的 runtimeManager.GetDesktopUserAvatarAsync()

失败统一抛出 IGPProfileException

Code说明
DESKTOP_SESSION_REQUIREDdesktop session 尚未 attach。
DESKTOP_USER_CONTEXT_REQUIREDdesktop 未登录或没有可用用户上下文。
PROFILE_UNAVAILABLE当前认证 session 未返回 profile。
PROFILE_INVALID_RESPONSEdesktop 返回的 profile JSON 为空或无法解析。

IGPPlayers.GetProfilesAsync(...) 是唯一的玩家资料查询入口,一次接受 1 到 50 个用户 ID。查询单个玩家时传入单元素数组。

IGPPlayerProfile[] profiles = await IGPPlayers.GetProfilesAsync(
runtimeManager,
new[] { "user-2", "user-3" });

重复 ID 按第一次出现的位置去重,不存在的用户从结果中省略。每个 IGPPlayerProfile 严格只包含 idnicknameavatarUrl 和静态 avatarFrameUrl

using IGP.UnitySDK;
using IGP.UnitySDK.GameKit;
public sealed class LeaderboardDriver
{
private IGPRuntimeManager runtimeManager;
public async void ListLeaderboards()
{
IGPListLeaderboardsByGameResult result =
await IGPLeaderboard.ListLeaderboardsByGameAsync(
runtimeManager,
new IGPListLeaderboardsByGameOptions
{
leaderboardId = "global-score",
});
foreach (IGPLeaderboardMetadataItem item in result.leaderboards)
{
// item.leaderboardId
// item.status
// item.displayName
}
}
public async void ShowLeaderboard(string leaderboardId)
{
IGPGetLeaderboardResult result =
await IGPLeaderboard.GetLeaderboardAsync(
runtimeManager,
leaderboardId,
new IGPGetLeaderboardQuery
{
start = 0,
count = 20,
includeSelf = true,
});
foreach (IGPLeaderboardEntry entry in result.entries)
{
// entry.rank
// entry.user.id
// entry.user.nickname
// entry.value
}
}
public async void SubmitScore(string leaderboardId, long score)
{
IGPSubmitLeaderboardScoreResult result =
await IGPLeaderboard.SubmitLeaderboardScoreAsync(
runtimeManager,
leaderboardId,
score,
new IGPSubmitLeaderboardScoreOptions
{
eventId = "match-20260626-0001",
sessionId = "session-abc",
});
// result.success
// result.duplicated
// result.value
}
public async void QueryFriends(string leaderboardId, string[] userIds)
{
IGPQueryLeaderboardScoresResult result =
await IGPLeaderboard.QueryLeaderboardScoresAsync(
runtimeManager,
leaderboardId,
userIds);
foreach (IGPLeaderboardScoreItem score in result.scores)
{
// score.userId
// score.value: null means the user has never submitted a score
}
}
}
方法HTTPRoute请求响应
ListLeaderboardsByGameAsyncGETgames/:gameIdIGPListLeaderboardsByGameOptionsIGPListLeaderboardsByGameResult
GetLeaderboardAsyncGET:leaderboardIdIGPGetLeaderboardQueryIGPGetLeaderboardResult
SubmitLeaderboardScoreAsyncPOST:leaderboardId/scoreIGPSubmitLeaderboardScoreRequestIGPSubmitLeaderboardScoreResult
QueryLeaderboardScoresAsyncPOST:leaderboardId/scores/queryIGPQueryLeaderboardScoresRequestIGPQueryLeaderboardScoresResult

Leaderboard 转发 payload 由 SDK 生成,游戏侧只传排行榜业务参数。下面的 appId/gameId 由 SDK 从 IGPConfig.appId 写入,不需要游戏业务层传入:

{
"method": "POST",
"appId": 10010,
"route": ":leaderboardId/score",
"pathParams": {
"leaderboardId": "global-score"
},
"body": {
"gameId": 10010,
"value": 100,
"eventId": "match-20260626-0001"
}
}

类型:IGPListLeaderboardsByGameOptions

字段类型说明
leaderboardIdstring?可选过滤条件;不传时返回该游戏下全部排行榜元数据。
periodstring?当前只支持 PERMANENT。为空时 SDK 默认填 PERMANENT

类型:IGPGetLeaderboardQuery

字段类型说明
periodstring?当前只支持 PERMANENT。为空时 SDK 默认填 PERMANENT
startint?查询起始位置,0 表示第一名。为空时 SDK 默认填 0
countint?返回条目数量,范围 1 到 100。为空时 SDK 默认填 20
includeSelfbool?true 时请求 API 返回当前用户自己的排名;无成绩时 selfEntrynull

类型:IGPSubmitLeaderboardScoreRequest

字段类型说明
gameIdint游戏 AppID,SDK 会从 IGPConfig.appId 写入请求 body。
valuelong当前玩家提交的整数分数,分数越高排名越靠前。
eventIdstring?可选幂等键。同一次结算重试时应复用同一个值。
sessionIdstring?可选游戏会话 id,透传给后端。

类型:IGPQueryLeaderboardScoresRequest

字段类型说明
gameIdint游戏 AppID,SDK 会从 IGPConfig.appId 写入请求 body。
userIdsstring[]要查询的用户 id,长度 1 到 50。
periodstring?当前只支持 PERMANENT。为空时 SDK 默认填 PERMANENT

类型:IGPListLeaderboardsByGameResult

字段类型说明
gameIdint游戏 id。
leaderboardsIGPLeaderboardMetadataItem[]排行榜元数据列表。

类型:IGPLeaderboardMetadataItem

字段类型说明
gameIdint游戏 id。
leaderboardIdstring排行榜 id。
periodstring当前为 PERMANENT
statusstring排行榜状态,例如 ACTIVE
displayNamestring?展示名。
descriptionstring?描述。
configobject?服务端配置摘要。
createdAtstringISO 时间字符串。
updatedAtstringISO 时间字符串。

类型:IGPGetLeaderboardResult

字段类型说明
gameIdint游戏 id。
leaderboardIdstring排行榜 id。
periodstring当前为 PERMANENT
entriesIGPLeaderboardEntry[]榜单条目。返回条目数量不保证一定等于请求的 count
selfEntryIGPLeaderboardEntry?请求 includeSelf=true 时返回当前用户排名;未上榜时为 null
generatedAtstringISO 时间字符串。

类型:IGPLeaderboardEntry

字段类型说明
rankint排名。
userIGPLeaderboardUser用户摘要。
valuelong分数。

类型:IGPSubmitLeaderboardScoreResult

字段类型说明
successbool是否成功。
duplicatedbooleventId 命中已有提交时为 true
valuelong本次提交分数。

类型:IGPQueryLeaderboardScoresResult

字段类型说明
gameIdint游戏 id。
leaderboardIdstring排行榜 id。
periodstring当前为 PERMANENT
scoresIGPLeaderboardScoreItem[]查询结果。未提交过成绩的用户 valuenull
generatedAtstringISO 时间字符串。

调用前 SDK 会做基础校验,失败时不会发送 desktop command:

  • leaderboardId:非空,trim 后最大 96 字符。
  • IGPConfig.appId:必填,SDK 会用它填充排行榜转发 payload 的 appId/gameId
  • start:大于等于 0 的整数。
  • count:1 到 100 的整数。
  • eventId / sessionId:如果传入,trim 后非空。
  • userIds:长度 1 到 50,每项非空。
  • period:只能是 PERMANENT

后端仍然是最终规则来源;SDK 本地校验只负责提前拦截明显错误。

失败统一抛出 IGPLeaderboardException

try
{
await IGPLeaderboard.GetLeaderboardAsync(runtimeManager, "global-score");
}
catch (IGPLeaderboardException ex)
{
UnityEngine.Debug.LogError(
$"Leaderboard failed: code={ex.Code}, status={ex.HttpStatus()}, upstream={ex.UpstreamJson}");
}

常见错误码:

错误码说明
DESKTOP_SESSION_CAPABILITY_MISSINGdesktop session 没有启用 leaderboard capability。
LEADERBOARD_HTTP_<status>Runtime Leaderboard API 返回非 2xx。
APP_ID_MISMATCH当前 appId 与 desktop/session 上下文不一致。

UpstreamJson 会保留 API 返回的原始错误 body,便于游戏侧日志和排查。

  • 只支持四个 Runtime Leaderboard 接口:查询游戏排行榜元数据、读取排行榜区间、批量查询用户分数、提交当前玩家成绩。
  • 不支持创建、审核、启用排行榜。
  • 不支持 cursor、includeMe。
  • period 当前只支持 PERMANENT
  • 不保证同分顺序。
  • 不保证读取榜单时返回条目数量一定等于请求的 count

Share 调用通过下面路径完成:

Unity game
-> IGPShare
-> IGPRuntimeManager.RequestDesktopShareAsync
-> desktop session command RequestDesktopShare
-> Curio desktop
-> desktop share handler / platform share API

SDK 不直接发布内容,只提交版本化 payload。文本和扩展数据放在 contentJson;图片是二进制字节,放在同一个 desktop command 的 contentBytes。登录、确认面板、发布、取消和失败流程由 desktop 自主控制,并通过 IGPShareResult 返回。

using IGP.UnitySDK;
using IGP.UnitySDK.GameKit;
public sealed class ShareDriver
{
private IGPRuntimeManager runtimeManager;
public async void ShareScore(byte[] posterPngBytes)
{
IGPShareResult result = await IGPShare.ShareAsync(
runtimeManager,
new IGPShareRequest
{
contentType = "score-card",
title = "New record",
text = "I scored 12345",
templateId = "score",
images = new[]
{
new IGPShareImage
{
mimeType = "image/png",
bytes = posterPngBytes,
width = 1280,
height = 720,
fileName = "score.png",
},
},
data = new
{
score = 12345,
},
metadata = new
{
matchId = "match-20260709-0001",
},
});
UnityEngine.Debug.Log($"Share status: {result.status}, id={result.shareId}");
}
}

类型:IGPShareRequest

字段类型说明
schemaVersionint固定为 1
contentTypestring?游戏自定义内容类型,例如 score-cardlevel-share
titlestring?分享标题。
textstring?分享文本。
imagesIGPShareImage[]多张图片,图片字节不进入 JSON。
templateIdstring?游戏和 desktop 约定的模板 id。
dataobject?游戏自定义结构化数据。
metadataobject?游戏自定义元数据,例如 matchId、levelId、traceId。

类型:IGPShareImage

字段类型说明
mimeTypestringimage/pngimage/jpeg
bytesbyte[]图片原始字节;SDK 会写入 contentBytes
widthint?图片宽度。
heightint?图片高度。
fileNamestring?可选文件名。
altTextstring?可选替代文本。

IGPShareImage 只定义数据字段;调用侧按图片实际格式填入 mimeTypebytes

SDK 发送给 desktop 的 contentJson 中,图片只保留描述符:

{
"schemaVersion": 1,
"appId": 10010,
"contentType": "score-card",
"text": "I scored 12345",
"images": [
{
"mimeType": "image/png",
"byteOffset": 0,
"byteLength": 1024,
"width": 1280,
"height": 720,
"fileName": "score.png"
}
],
"data": {
"score": 12345
}
}

图片原始字节拼接在 contentBytes 中。desktop 用 byteOffsetbyteLength 读取每张图片。

类型:IGPShareResult

字段类型说明
successbool分享流程结果。
codestringdesktop 分享结果码,例如 SHARE_COMPLETEDSHARE_CANCELLEDSHARE_LOGIN_REQUIRED
shareIdstringdesktop 或平台返回的分享 id。
statusstring分享状态,例如 completedcancelledloginRequired
messagestring可展示或记录的结果说明。
dataobject?desktop 返回的扩展结果。
contentJsonstringdesktop 原始结果 JSON,便于排查问题。

用户取消是正常结果,例如 success=falsestatus=cancelledcode=SHARE_CANCELLED,不会抛异常。

  • IGPConfig.appId 必填。
  • schemaVersion 必须是 1
  • 请求至少包含文本、图片、模板 id、datametadata 之一。
  • 图片 MIME 只支持 image/pngimage/jpeg
  • 所有图片原始字节合计最大 10 MiB
  • 超过 10 MiB 时抛出 IGPShareExceptionCode = SHARE_IMAGES_TOO_LARGE,不会发送 desktop command。

desktop 分享流程的业务结果通过 IGPShareResult 返回。桥协议或 desktop 命令失败才会抛出 IGPShareException

  • Code:desktop 返回的稳定错误码。
  • UpstreamJson:desktop 原始错误 body(如果有)。