把 Project 连接到 Unity
最后更新:2026 年 8 月 5 日
在 PlayServ Backoffice 中创建好 Project 之后,下一步就是把它连接到 Unity。导入 SDK 包、填入 Project 凭据、把 Schema 同步成生成的 Model,再加一个很小的引导脚本 —— 做完这些,Unity 工程就可以连接 PlayServ 了。
开始之前
在 Unity 中接入 SDK 之前,请确认你已经有:
- Unity 2021.3 或更高版本(下面的截图用的是 Unity 6)
- 一个已在 PlayServ Backoffice 中创建的 Project —— 见 Creating a New Project
- 一个 Client key,用作 Game Access Token —— 见 API Key Management
- Project 的 Game ID
配置放在 Unity 内的 PlayServ 编辑器控制窗口中,使用的是在 Backoffice 生成的凭据。窗口通过 Project 的 config asset(Assets/Resources/PlayServConfig.asset)解析它们。
导入 SDK
把 PlayServ SDK 导入你的工程。导入后 Unity 会重新编译脚本 —— 请等 Compiling Scripts 进度条走完再继续。
- Unity 包
- OpenUPM
打开 Assets ▸ Import Package ▸ Custom Package…,选择 playserv-unity-sdk-<version>.unitypackage,在 Import Unity Package 对话框中保持全部勾选,然后点击 Import。

以这种方式导入时,SDK 位于 Assets/playserv-unity-sdk,Unity 不会读取包自带的 manifest。如果 com.unity.nuget.newtonsoft-json 没有被自动安装,请把 "com.unity.nuget.newtonsoft-json": "3.2.2" 加到 Packages/manifest.json 中。
也可以改用包管理器安装:
openupm add com.playserv.sdk
两条路径装的是同一个 SDK —— 选符合你工程依赖管理习惯 的那条。
配置 PlayServ 编辑器控制窗口
从 Tools ▸ PlayServ ▸ Settings 打开 PlayServ editor controls 窗口。运行时身份管理、Model 同步和服务端代码部署都集中在这一处,还有一个直达 Backoffice Dashboard 的快捷入口。窗口顶部显示当前的 Game ID 和已安装的 SDK Version。
窗口分为几个区块:
- Control Room → PlayServ Config —— 运行时身份、凭据、固定 endpoint,以及 Project 一侧的 config asset
- Server Code → Deployment —— 预览 RPC 代码闭包、同步已部署的版本,并把 ZIP 包发到部署 endpoint
- Schema → Model Sync —— 检查最新的 Schema、比对时间戳,并重新生成编辑器一侧的 Model
- Realtime → Events —— 生成强类型的 Events API,让 Event payload 的契约贴近运行时
- Automation → Code Generation —— 按需生成 DTO(也可以设成自动),需要重来时清空生成层
底部的开关 Show this window on Unity startup 控制窗口是否自动打开,Open Docs 则链回本文档。
本指南用到的是 PlayServ Config(见下)和 Model Sync(第 3 步)。其余区块讲的是服务端部署和代码生成。
在 Control Room → PlayServ Config 中填写 Project 凭据:
确认 config asset
Config Asset 字段应指向 Assets/Resources/PlayServConfig.asset。如果它还不存在,窗口会提示创建。用 Ping 可以在 Project 窗口中定位到它。
填入 Game Access Token
粘贴你在 Backoffice 创建的 Client key。Client key 可以安全地随游戏 build 分发 —— 见 API Key Management。
绝不要拿 Server key 当 Game Access Token。从游戏二进制中提取出的 Server key,会让攻击者获得你 Project 后端的完整访问权限。
填入 Game ID 和版本
把 Game Id 设为你 Project 的 Game ID,把 Game Version 设为你的 build 版本(例如 1.0.0)。如果同一台机器会开多个客户端 —— 比如编辑器内运行的同时又跑一个 build —— 请勾选 Allow Multiple Connections。
请确认 token 和 Game ID 属于同一个 PlayServ Project 和同一个 Environment。token 与 Project ID 对不上,SDK 就无法正常连接。
如果你的流程把 config asset 当作唯一可信来源,那就把凭据留在这个窗口里 ,不要在场景组件中再次覆盖它们 —— 除非你是有意为之。
同步 Schema 并生成 Model
编辑器一侧的 C# Model 是从 Project 的 Schema 生成的,Schema 才是唯一可信来源。写 gameplay 代码之前,先同步它们。
检查 Schema 是否有变化
在 Schema → Model Sync 中,窗口会向服务器查询 Schema 的变更。当它提示 A newer schema is available 时,请对照 Current schema 和 Latest available schema —— 比较两者的 hash 和时间戳。
应用最新的 Schema
点击 Apply New Schema,接受来自服务器的最新 Schema。
重新生成 Model
点击 Re-generate Models。生成的 Model 会写入 Assets/Shared/Generated/Models。
任何时候都可以用 Check Updates 看看本地 Model 是否落后于服务器上的 Schema。每当 Backoffice 中的 Schema 有变动,就重新跑一次 Re-generate Models。
给场景添加引导脚本
配置就绪之后,创建一个引导脚本,挂到场景中的某个 GameObject 上。这个脚本读取 PlayServ 配置、应用 SDK 设置、在场景启动时建立连接,并让连接跨场景加载保持可用。
创建一个常驻的引导对象
在工程的第一个场景中创建一个空 GameObject,命名为 PlayServBootstrap。一个常驻的引导对象很适合承载连接逻辑。
挂上引导脚本
把下面的脚本挂到该对象上。
using System;
using System.Threading.Tasks;
using Playserv.Proxy.Common;
using Playserv.Wrapper;
using UnityEngine;
using UnityEngine.Serialization;
namespace Playserv.Examples
{
/// <summary>
/// Persistent bootstrap component for configuring and connecting PlayServ in samples.
/// </summary>
public sealed class PlayServBootstrapSample : MonoBehaviour
{
private static PlayServBootstrapSample _instance;
[Header("Credentials")]
[SerializeField] private string gameAccessToken = "your-token";
[SerializeField] private string gameId = "game-001";
[SerializeField] private string userId = "player-001";
[SerializeField] private string gameVersion = "1.0.0";
[SerializeField] private bool overrideCredentialsFromInspector;
[Header("Resolved Endpoints (Read Only)")]
[FormerlySerializedAs("remoteEndpoint")]
[SerializeField] private string backendServerAddress = PlayServSettings.DefaultBackendServerAddress;
[SerializeField] private string deployApiServerAddress = PlayServSettings.DefaultDeployApiServerAddress;
[SerializeField] private string schemaApiServerAddress = PlayServSettings.DefaultSchemaApiServerAddress;
[Header("Behavior")]
[SerializeField] private bool autoConnect;
[SerializeField] private bool disconnectOnDestroy = true;
[Header("KeepAlive")]
[SerializeField] private int keepAlivePingIntervalMs = 5000;
[SerializeField] private int keepAlivePongTimeoutMs = 5000;
private bool _isOwner;
private void Awake()
{
if (_instance != null && _instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
_isOwner = true;
DontDestroyOnLoad(gameObject);
RefreshResolvedEndpointsPreview();
}
private void Start()
{
if (!_isOwner)
return;
Configure();
if (autoConnect &&
PlayServ.State != PlayServState.Online &&
PlayServ.State != PlayServState.Connecting &&
PlayServ.State != PlayServState.Handshaking)
{
_ = ConnectAsync();
}
}
private void OnEnable()
{
if (!_isOwner)
return;
PlayServ.OnTransportError += OnTransportError;
}
private void OnDisable()
{
if (!_isOwner)
return;
PlayServ.OnTransportError -= OnTransportError;
}
private void OnDestroy()
{
if (_instance == this)
_instance = null;
if (_isOwner && disconnectOnDestroy)
PlayServ.Disconnect();
}
private void OnValidate()
{
RefreshResolvedEndpointsPreview();
}
[ContextMenu("Configure SDK")]
public void Configure()
{
var settings = BuildSettingsFromConfig();
if (overrideCredentialsFromInspector)
{
settings.GameAccessToken = gameAccessToken;
settings.GameId = gameId;
settings.UserId = userId;
settings.GameVersion = gameVersion;
}
settings.KeepAlivePingIntervalMs = keepAlivePingIntervalMs;
settings.KeepAlivePongTimeoutMs = keepAlivePongTimeoutMs;
PlayServ.Config(settings);
ApplyResolvedEndpointsPreview(settings);
Debug.Log(
$"[PlayServ][Sample] Configured. gameId={settings.GameId}, credentialsSource={(overrideCredentialsFromInspector ? "inspector" : "config")}, backend={settings.BackendServerAddress}, pingInterval={settings.KeepAlivePingIntervalMs}ms, pongTimeout={settings.KeepAlivePongTimeoutMs}ms");
}
[ContextMenu("Connect SDK")]
public void Connect()
{
_ = ConnectAsync();
}
[ContextMenu("Disconnect SDK")]
public void Disconnect()
{
PlayServ.Disconnect();
Debug.Log("[PlayServ][Sample] Disconnected.");
}
public async Task ConnectAsync()
{
try
{
bool connected = await PlayServ.Connect();
Debug.Log(connected
? "[PlayServ][Sample] Connected."
: "[PlayServ][Sample] Connection failed.");
}
catch (Exception ex)
{
Debug.LogError($"[PlayServ][Sample] Connect error: {ex.Message}");
}
}
private void OnTransportError(TransportError error)
{
Debug.LogError($"[PlayServ][Sample] Transport error: {error}");
}
private void RefreshResolvedEndpointsPreview()
{
var settings = BuildSettingsFromConfig();
ApplyResolvedEndpointsPreview(settings);
}
private void ApplyResolvedEndpointsPreview(PlayServSettings settings)
{
if (settings == null)
return;
backendServerAddress = settings.BackendServerAddress;
deployApiServerAddress = settings.DeployApiServerAddress;
schemaApiServerAddress = settings.SchemaApiServerAddress;
}
private static PlayServSettings BuildSettingsFromConfig()
{
var config = Resources.Load<PlayServConfig>("PlayServConfig");
if (config == null)
return PlayServPackageDefaultsProvider.LoadSettingsOrDefault();
#if UNITY_EDITOR
return PlayServSettingsResolver.ResolveEditorSettings(config);
#else
return config.ToSettings();
#endif
}
}
}
在默认流程下,这个脚本从 PlayServ 的 config asset 读取凭据。只有当你确实想覆盖 PlayServ 编辑器控制窗口中设定的值时,才启用 overrideCredentialsFromInspector。
运行场景并验证连接
进入 Play Mode。如果配置无误,引导组件会配置好 SDK,在 autoConnect 启用时自动建立连接,并在该对象存在期间保持连接。
在 Unity Console 中验证连接,找这两行:
[PlayServ][Sample] Configured.[PlayServ][Sample] Connected.
出问题时,脚本同样会报告连接失败、传输错误和配置问题。
这套配置是怎么运作的
这个流程把配置和运行时行为分开了:
- PlayServ 编辑器控制窗口保存 SDK 配置,并让编辑器一侧的 Model 与 Schema 保持同步
- 引导脚本在运行时应用这份配置并建立连接
也就是说:Project 凭据集中在一处管理,数据 Model 由 Schema 生成而不是手写,场景里只需要一个小而可复用的引导组件。
整个工程用一个常驻的引导对象就够了,不要在多个场景里各放一份连接脚本。
常见排查点
如果 Project 没有按预期连上,请检查:
- SDK 包是否正确导入
- Game Access Token 是不是 Client key,以及是否复制完整
- Game ID 是否对应 Backoffice 中的同一个 Project
- PlayServ 的 config asset 是否存在并已填写
- 最近一次 Schema 变更之后是否重新生成过 Model
- 引导脚本是否挂在一个处于激活状态的 GameObject 上
- 如果你期望场景启动时就连上,
autoConnect是否已启用
如果凭据已经在 PlayServ 编辑器控制窗口中设定好了,注意不要在引导组件的 inspector 中用不同的值把它们无意覆盖掉。
下一步
- Building a Game Schema —— 为你的游戏数据建模
- Schema Object Reference —— Schema 的构成要素
- Working with Game Data —— 通过 SDK 读写数据