目标:用 Unity 6 + NGO 2.13.x 做出两个客户端可加入、各自控制 PlayerObject,并由服务器验证射击与同步弹药的最小示例。
1. 安装与场景配置2. 创建 Player Prefab3. 只让所有者读取输入4. 为什么这样分工IsOwnerNetworkVariable<int>RequestFireRpcPlayFireEffectRpc5. NetworkTransform 权威6. 服务器生成对象7. RPC 与 NetworkVariable 怎么选8. 本地测试排错清单系列导航参考资料
1. 安装与场景配置
在 Package Manager 安装:
com.unity.netcode.gameobjects
com.unity.services.multiplayer
com.unity.multiplayer.playmode(推荐)
在场景创建
NetworkManager GameObject:- 添加
NetworkManager组件。
- Network Transport 选择
UnityTransport。
- 保持 Enable Scene Management 开启。
- 把当前场景加入 Build Profiles 的 Scene List。
2. 创建 Player Prefab
创建一个 Capsule,并添加:
NetworkObject
NetworkTransform
- 自定义
NetworkPlayer
将它保存为 Prefab,并拖到
NetworkManager > Default Player Prefab。网络 Prefab 根节点只放一个 NetworkObject,不要嵌套另一个根级网络身份。3. 只让所有者读取输入
4. 为什么这样分工
IsOwner
每个客户端都会生成所有玩家对象,但只有对象所有者读取本机输入。否则一个键盘会驱动场景中的全部玩家。
NetworkVariable<int>
弹药是持续状态,晚加入客户端也需要拿到当前值,因此用
NetworkVariable。写权限设为 Server,客户端无法直接篡改。RequestFireRpc
客户端只发送“我要开火”的请求。方法在服务器执行,服务器检查弹药和参数,再修改权威状态。
NGO 2.13 提供通用
[Rpc]。旧教程常见的 [ServerRpc]、[ClientRpc] 仍可能出现在历史项目中,但新代码应优先使用明确目标的通用 RPC。PlayFireEffectRpc
开火特效是离散事件,适合 RPC。它不是需要长期保存的状态,晚加入玩家不必重放旧枪口火焰。
5. NetworkTransform 权威
移动示例为了直观直接修改所有者 Transform。要让
NetworkTransform 接受所有者更新,需要在 Inspector 将 Authority 配成 Client。这适合原型和低风险移动,但客户端权威更容易作弊。正式对抗游戏通常应发送输入给服务器,由服务器模拟移动,客户端使用预测和校正改善手感。
6. 服务器生成对象
运行时网络对象应由服务器生成:
Prefab 必须注册在 Network Prefabs 列表中。普通
Instantiate 只会创建本机对象,不会自动同步给其他客户端。7. RPC 与 NetworkVariable 怎么选
需求 | 选择 |
生命值、分数、门是否打开 | NetworkVariable |
开枪、播放一次音效、提交输入 | RPC |
角色持续移动 | NetworkTransform 或自定义快照 |
大量短生命周期对象 | 网络对象池与批量同步 |
不要每帧发送可靠 RPC 同步 Transform。高频数据要控制发送频率、选择合适可靠性,并用 Network Profiler 观察带宽。
8. 本地测试
- 打开
Window > Multiplayer > Multiplayer Play Mode。
- 启用 Player 2。
- 用 Sessions Building Block 或系列后续代码创建 Host。
- 第二个实例输入加入码。
- 验证每个窗口只能控制自己的对象。
- 模拟延迟和丢包,检查移动、RPC 和断线行为。
排错清单
- Player Prefab 是否包含
NetworkObject
- Prefab 是否注册到
NetworkManager
- 是否只在
IsOwner时读取输入
- 是否只由服务器写权威
NetworkVariable
- 动态对象是否调用
NetworkObject.Spawn()
- 网络场景是否加入 Build Profiles
OnValueChanged是否在 Despawn 时退订