Unity 对接指南
本页面向已经完成 Unity 安装 的游戏项目。它不是最短验证路径;如果你只是第一次跑通 SDK,请先看 Unity Quick Start。
- 在场景中保留唯一的
IGPRuntimeManager。 - 在
Assets/IGP/IGPConfig.asset填写appId。 - 游戏启动流程主动调用
await runtimeManager.InitializeAsync()。 - 按目标能力判断连接状态:桌面能力看
IsDesktopSessionAttached,房间能力看onRoomJoined和IsHostedSessionAttached。 - 再接入正版校验、成就、房间、实时消息、状态或 RPC。
常规接入不要求游戏手动填写运行目录或可执行文件路径。Desktop Executable Path Debug Override 只用于模拟正式安装路径或排查 exe 绑定问题,填写的是游戏自己的 Windows 可执行文件路径,不是桌面客户端路径。
| 参数 | 位置 | 是否必填 | 含义 |
|---|---|---|---|
appId | IGPConfig | 必填 | IGP 为当前游戏分配的应用 ID。Unity 接入里最重要的配置,桌面能力、正版校验、成就和房间启动都会用它确认游戏身份。 |
sdkEnvironment | IGPConfig | 可选 | SDK 连接的桌面端环境。正式发布保持 PROD;预览联调可按团队要求使用 PREVIEW;本地开发调试才使用 DEV。 |
debugLogging | IGPConfig | 可选 | 打印 hosted session、KCP、P2P、Mirror Transport 等诊断日志。排查时开启,正式发布建议关闭。 |
desktopSessionAutoAttach | IGPConfig | 通常保持默认 | 是否在初始化时自动附着 desktop session。常规接入保持默认开启。 |
desktopExecutablePathDebugOverride | IGPConfig | 可选 | 仅用于 Editor 调试特殊路径绑定。留空时 SDK 会按环境自动找对应桌面端。 |
desktopLaunchCommand | IGPConfig | 可选 | 仅用于 DEV 环境特殊调试,不是常规接入项。 |
最小初始化案例
Section titled “最小初始化案例”using UnityEngine;using IGP.UnitySDK;
public sealed class IGPGameBootstrap : MonoBehaviour{ [SerializeField] private IGPRuntimeManager runtimeManager;
private async void Start() { runtimeManager ??= FindObjectOfType<IGPRuntimeManager>();
var initialized = await runtimeManager.InitializeAsync(); if (!initialized) { Debug.LogError("[IGP] SDK 初始化失败。"); return; }
if (runtimeManager.IsDesktopSessionAttached) { Debug.Log("[IGP] Desktop session 已连接,可以使用正版校验、成就等桌面能力。"); } }}关键点:
InitializeAsync()是显式启动入口。只把IGPRuntimeManager放进场景不会自动启动 SDK。IsDesktopSessionAttached为true表示桌面能力链路已附着。- 房间链路需要额外等待
onRoomJoined;不要把“没有房间”误判为桌面能力失败。
正版校验在初始化过程中由 IGPRuntimeManager 处理。业务侧通常监听状态变化,并在失败时决定是否展示自定义 UI。
using UnityEngine;using IGP.UnitySDK;
public sealed class IGPAuthorizationGate : MonoBehaviour{ [SerializeField] private IGPRuntimeManager runtimeManager;
private void OnEnable() { runtimeManager.onAuthorizationStateChanged.AddListener(OnAuthorizationChanged); runtimeManager.onAuthorizationFailed.AddListener(OnAuthorizationFailed); }
private void OnDisable() { runtimeManager.onAuthorizationStateChanged.RemoveListener(OnAuthorizationChanged); runtimeManager.onAuthorizationFailed.RemoveListener(OnAuthorizationFailed); }
private void OnAuthorizationChanged(IGPAuthorizationState state) { if (runtimeManager.IsAuthorized) { // 允许进入主菜单或继续游戏。 } }
private void OnAuthorizationFailed(string message) { Debug.LogError($"[IGP] 正版校验失败:{message}"); }}| 参数 / 字段 | 含义 |
|---|---|
onAuthorizationStateChanged | 授权状态变化事件。常见成功状态是 AuthorizedOnline 和 AuthorizedOffline。 |
onAuthorizationFailed | 授权失败事件,参数是失败说明。 |
IsAuthorized | 当前是否处于已授权状态。 |
IGPConfig.showAuthorizationFailureFallback | 是否显示 SDK 自带失败兜底提示。关闭后应由游戏自己展示失败 UI。 |
成就当前走 desktop session。调用前至少需要 InitializeAsync() 成功,并且 IsDesktopSessionAttached == true。
using UnityEngine;using IGP.UnitySDK;using IGP.UnitySDK.Models;
public sealed class IGPAchievementDriver : MonoBehaviour{ [SerializeField] private IGPRuntimeManager runtimeManager;
public async void UnlockFirstSession() { var result = await IGPSDK.UnlockAchievementAsync( runtimeManager, achievementKey: "first_session");
Debug.Log($"unlock success={result.success}, duplicated={result.duplicated}"); }
public async void AddMatchProgress() { var result = await IGPSDK.ReportAchievementProgressAsync( runtimeManager, achievementKey: "matches_played", progressValue: 1, progressSourceKey: "match_complete", progressValueMode: ProgressValueMode.Increment);
Debug.Log($"progress success={result.success}, duplicated={result.duplicated}"); }}| 参数 | 含义 |
|---|---|
achievementKey | 后台配置的成就 key,必须与平台配置一致。 |
progressValue | 进度值。具体含义由后台成就类型决定。 |
progressSourceKey | 进度来源标识,例如 match_complete、level_clear,用于排查和归因。 |
progressValueMode | Set 表示设置为当前值,Increment 表示在当前进度上累加。 |
eventId | 幂等键。同一次业务事件重试时复用;不传时 SDK 会自动生成。 |
occurredAtUnixMs | 事件发生时间,Unix 毫秒;不传时由链路使用当前时间。 |
appId | 可选覆盖值。常规接入不传,SDK 使用 IGPConfig.appId。 |
返回值:
| 字段 | 含义 |
|---|---|
success | 是否处理成功。 |
duplicated | 是否命中幂等去重。重复解锁或重复上报不一定是错误。 |
message | 平台返回的说明文本。 |
房间生命周期
Section titled “房间生命周期”房间、realtime、状态、RPC 和 KCP 都需要 hosted session。游戏通常由 IGP 桌面客户端按联调或正式流程启动,SDK 再通过启动信息进入房间。
using UnityEngine;using IGP.UnitySDK;using IGP.UnitySDK.Models;
public sealed class IGPRoomFlow : MonoBehaviour{ [SerializeField] private IGPRuntimeManager runtimeManager;
private void OnEnable() { runtimeManager.onRoomJoined.AddListener(OnRoomJoined); runtimeManager.onRoomUpdated.AddListener(OnRoomUpdated); runtimeManager.onRoomLeft.AddListener(OnRoomLeft); runtimeManager.onMapChanged.AddListener(OnMapChanged); }
private void OnDisable() { runtimeManager.onRoomJoined.RemoveListener(OnRoomJoined); runtimeManager.onRoomUpdated.RemoveListener(OnRoomUpdated); runtimeManager.onRoomLeft.RemoveListener(OnRoomLeft); runtimeManager.onMapChanged.RemoveListener(OnMapChanged); }
private async void OnRoomJoined(Room room) { Debug.Log($"[IGP] joined room={room.id}, code={room.code}"); await runtimeManager.SetReadyAsync(true); }
private void OnRoomUpdated(Room room) { // 刷新房间 UI、玩家列表、队伍状态等。 }
private void OnRoomLeft(Room room) { // 清理房间内状态。 }
private void OnMapChanged(IGPMapChangeData mapChange) { Debug.Log($"[IGP] map changed to {mapChange.currentMapPublicId}"); }}| 方法 / 事件 | 含义 |
|---|---|
onRoomJoined(Room room) | 成功进入房间。常见做法是在这里初始化房间 UI 并调用 SetReadyAsync(true)。 |
onRoomUpdated(Room room) | 房间快照更新。room.players、room.globalState、room.teams 等都以最新快照为准。 |
onRoomLeft(Room room) | 离开房间。用于清理 UI 和本地状态。 |
onMapChanged(IGPMapChangeData mapChange) | 同一房间内运行中换地图。使用 currentMapPublicId / currentMapVersionId 映射游戏自己的地图资源。 |
SetReadyAsync(bool isReady) | 设置当前玩家 ready 状态。 |
StartHostedGameAsync() | 请求开始游戏,通常由房主或具备权限的一端触发。 |
FinishHostedGameAsync() | 请求结束当前对局。 |
LeaveHostedRoomAsync() | 离开当前房间并断开 hosted 链路。 |
实时消息适合房间内自定义业务消息,例如聊天、表情、轻量玩法事件。发送前必须已经进入房间。
using System;using UnityEngine;using IGP.UnitySDK;using IGP.UnitySDK.Models;
public sealed class IGPRealtimeDriver : MonoBehaviour{ [SerializeField] private IGPRuntimeManager runtimeManager;
private void OnEnable() { runtimeManager.onMessageReceived.AddListener(OnMessageReceived); }
private void OnDisable() { runtimeManager.onMessageReceived.RemoveListener(OnMessageReceived); }
public async void SendChat(string text) { await runtimeManager.SendMessageAsync(new Message { type = "chat.message", roomId = runtimeManager.CurrentRoomId, playerId = runtimeManager.PlayerId, reliable = true, content = new { text, sentAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), }, }); }
private void OnMessageReceived(string messageType, object content) { Debug.Log($"[IGP] message={messageType}, payload={content}"); }}Message 字段 | 含义 |
|---|---|
type | 业务消息类型。不要使用 SDK 保留类型,例如 p2p_data、state_set、rpc_call。建议使用稳定命名,如 chat.message。 |
roomId | 当前房间 ID,通常填 runtimeManager.CurrentRoomId。 |
playerId | 当前玩家 ID,通常填 runtimeManager.PlayerId。 |
targetPlayerId | 可选目标玩家。为空通常表示房间广播。 |
content | 业务 payload。保持结构稳定,便于不同版本客户端兼容。 |
reliable | 是否使用可靠发送。当前房间实时消息建议保持 true。 |
状态与 RPC
Section titled “状态与 RPC”状态用于同步房间内共享数据或玩家数据;RPC 用于触发房间内命名逻辑。接收状态和 RPC 事件时,建议场景中同时放置 IGPEventManager。
using System;using UnityEngine;using IGP.UnitySDK;
public sealed class IGPStateRpcDriver : MonoBehaviour{ [SerializeField] private IGPRuntimeManager runtimeManager;
public async void SetScore() { await runtimeManager.SetGlobalStateAsync("match.score", new { red = 1, blue = 0, updatedAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), }); }
public async void SetLocalPlayerLoadout(string weaponId) { await runtimeManager.SetPlayerStateAsync("loadout.weapon", weaponId); }
public async void RegisterEcho() { await runtimeManager.RegisterRPCAsync("sample.echo"); }
public async void CallEcho() { var requestId = await runtimeManager.CallRPCAsync( "sample.echo", new { text = "hello" }, mode: "all");
Debug.Log($"[IGP] rpc request={requestId}"); }}| 方法 / 参数 | 含义 |
|---|---|
SetGlobalStateAsync(key, value) | 设置房间全局状态。适合比分、局内阶段等所有玩家共享的数据。 |
SetPlayerStateAsync(key, value) | 设置当前玩家状态。适合载入进度、角色选择、装备等玩家维度数据。 |
SetStateAsync(scope, key, value, targetPlayerId) | 通用状态入口。scope 常用 global / player。 |
GetStateAsync(scope, key, targetPlayerId) | 请求读取指定状态。响应会通过状态消息 / 事件返回。 |
ResetStateAsync(scope, keysToExclude) | 重置指定作用域状态,可保留部分 key。 |
RegisterRPCAsync(name) | 注册当前客户端可处理的 RPC 名称。 |
CallRPCAsync(name, data, mode, requestId) | 调用 RPC。mode 常用 all;requestId 不传时 SDK 会生成请求 ID。 |
UnregisterRPCAsync(name) | 取消注册 RPC。 |
P2P / KCP 数据面
Section titled “P2P / KCP 数据面”如果项目使用主包的低层数据面,入口在 runtimeManager.Network。这条链路需要已进入房间并完成 hosted data plane attach。
using System.Text;using UnityEngine;using IGP.UnitySDK;using IGP.UnitySDK.Network;
public sealed class IGPP2PDriver : MonoBehaviour{ [SerializeField] private IGPRuntimeManager runtimeManager;
private void OnEnable() { runtimeManager.Network.OnDataReceived.AddListener(OnDataReceived); }
private void OnDisable() { runtimeManager.Network.OnDataReceived.RemoveListener(OnDataReceived); }
public void BroadcastBytes() { var bytes = Encoding.UTF8.GetBytes("hello"); var result = runtimeManager.Network.SendReliableData( new IGPPlayerID(runtimeManager.PlayerId), new IGPPlayerID(""), bytes, (uint)bytes.Length, message_type: 100);
Debug.Log($"[IGP] send result={result}"); }
private void OnDataReceived(IGPDataReceived data) { Debug.Log($"[IGP] p2p from={data.remote_peer}, type={data.message_type}, bytes={data.data.Length}"); }}| 参数 | 含义 |
|---|---|
local_peer | 当前玩家 ID,通常是 runtimeManager.PlayerId。 |
remote_peer | 目标玩家 ID;空字符串表示广播到房间。 |
data_buf / data_len | 要发送的字节数据和有效长度。 |
message_type | 业务自定义数字类型,用于接收端分发。 |
SendData | 适合较小 payload。超过当前数据面限制会返回 kErrorInvalidParam。 |
SendReliableData | 允许 SDK 对较大 payload 自动分片,是大消息的优先入口。 |
多数运行时错误会通过两种方式暴露:
- 抛出
IGPSDKException - 触发
runtimeManager.onError
try{ await IGPSDK.UnlockAchievementAsync(runtimeManager, "first_session");}catch (IGPSDKException ex){ Debug.LogError( $"[IGP] code={ex.ErrorCode}, category={ex.Category}, " + $"channel={ex.DesktopChannelState}, attach={ex.DesktopAttachState}, message={ex.Message}");}| 字段 | 含义 |
|---|---|
ErrorCode | SDK、desktop session 或 hosted 链路返回的错误码。 |
Category | 错误分类,例如 validation、authorization、channel、runtime。 |
DesktopChannelState | desktop session 通道状态快照,仅 desktop session 错误常见。 |
DesktopAttachState | desktop attach 校验状态快照,仅 desktop session 错误常见。 |
Message | 可读错误说明。 |
排查建议:
- 初始化失败时,先确认
appId和本机 IGP 桌面客户端。 - 桌面能力失败时,先看
IsDesktopSessionAttached、CurrentDesktopCapabilities和授权状态。 - 房间能力失败时,先看
onRoomJoined是否触发,再看IsHostedSessionAttached和CurrentHostedConnectionState。 - 数据面失败时,临时开启
debugLogging,观察[IGP SDK]日志。
接入检查清单
Section titled “接入检查清单”- 已导入
cn.indiegp.sdk.unity主包。 - 如需 Mirror 或实名认证与防沉迷,已导入对应可选包。
- 场景里只有一个长期有效的
IGPRuntimeManager。 IGPConfig.appId已填写。- 游戏启动流程已调用
InitializeAsync()。 - 单机 / 非房间能力只验收 desktop session,不强等房间。
- 房间、realtime、state、RPC、KCP 能力只在进入房间后调用。
- 成就 key、状态 key、RPC 名称、消息 type 都使用稳定命名,并与后台配置或业务协议一致。
- 幂等参数
eventId/requestId在同一次业务重试时复用,新业务事件重新生成。 - 正式发布前关闭
debugLogging,并按 联调与测试 完成检查。