Unity UNet点击时Transform更新异常:双人联机绘图游戏问题排查
问题分析与解决方案
核心问题拆解
你的代码里有几个关键问题导致客户端绘图的Transform无法同步:
- 服务器端无Camera导致Raycast失败:
CmdTrailUpdate和CmdSpawn里直接使用Camera.main,但服务器运行时没有场景Camera,这会导致mRay无效,无法计算出正确的位置,自然无法更新Transform。 - Local Player Authority的使用错误:你给预制体勾选了
Local Player Authority,意味着客户端拥有该对象的控制权,应该由客户端直接修改对象位置,再通过NetworkTransform同步到服务器和其他客户端,而不是通过Command让服务器修改位置。 - 客户端与服务器的
currentTrail引用不一致:CmdSpawn在服务器上实例化并赋值了currentTrail,但客户端本地的currentTrail变量并没有关联到同步生成的本地对象,导致后续客户端调用CmdTrailUpdate时,服务器操作的可能不是该客户端对应的trail对象。
修正后的代码实现
1. 调整TrailDrawer逻辑,客户端本地计算位置
把Raycast操作放在客户端完成,只把计算好的位置传给服务器,同时利用Local Player Authority让客户端直接控制trail对象:
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Networking; public class TrailDrawer : NetworkBehaviour { private Plane objPlane; private GameObject currentTrail; public GameObject trailPrefab; // 重命名变量更清晰 public override void OnStartLocalPlayer() { // 只在本地玩家初始化时设置Plane,避免服务器执行 objPlane = new Plane(Camera.main.transform.forward * -1, transform.position); } void FixedUpdate() { if (!isLocalPlayer) return; // 非本地玩家直接跳过 if (Input.GetMouseButtonDown(0)) { // 客户端本地计算初始位置 Ray mRay = Camera.main.ScreenPointToRay(Input.mousePosition); if (objPlane.Raycast(mRay, out float rayDistance)) { Vector3 spawnPos = mRay.GetPoint(rayDistance); CmdSpawnTrail(spawnPos); // 把位置传给服务器 } } else if (Input.GetMouseButton(0) && currentTrail != null) { // 客户端直接修改trail位置,Local Player Authority会自动同步 Ray mRay = Camera.main.ScreenPointToRay(Input.mousePosition); if (objPlane.Raycast(mRay, out float rayDistance)) { currentTrail.transform.position = mRay.GetPoint(rayDistance); } } } [Command] private void CmdSpawnTrail(Vector3 spawnPos) { // 服务器实例化trail,因为客户端已经计算好位置,不需要再Raycast GameObject newTrail = Instantiate(trailPrefab, spawnPos, Quaternion.identity); NetworkServer.SpawnWithClientAuthority(newTrail, connectionToClient); // 明确赋予客户端权限 RpcAssignLocalTrail(newTrail.netId); // 通知客户端关联本地对象 } [ClientRpc] private void RpcAssignLocalTrail(NetworkInstanceId trailNetId) { // 只有本地玩家才需要关联currentTrail if (!isLocalPlayer) return; currentTrail = ClientScene.FindLocalObject(trailNetId); } }
2. 预制体配置确认
确保你的trail预制体满足以下配置:
- 挂载
NetworkIdentity组件,勾选Local Player Authority - 挂载
NetworkTransform组件,保持默认配置即可(因为Local Player Authority下,客户端的位置修改会自动同步)
关键修改说明
- 客户端本地计算位置:所有和Camera相关的Raycast都在客户端完成,避免服务器无Camera的问题,同时减少网络传输的数据量。
- Spawn时明确赋予权限:使用
NetworkServer.SpawnWithClientAuthority替代NetworkServer.Spawn,确保生成的trail对象归当前客户端所有,拥有修改权限。 - ClientRpc关联本地对象:通过
RpcAssignLocalTrail把服务器生成的trail的NetworkID传给客户端,客户端通过ID找到本地实例并赋值给currentTrail,解决引用不一致的问题。 - 直接修改客户端对象:因为拥有Local Player Authority,客户端修改trail的位置后,
NetworkTransform会自动把位置同步到服务器和其他客户端,不需要再用Command传递位置。
这样修改后,客户端玩家的绘图操作就能正确同步到主机和其他客户端了。
内容的提问来源于stack exchange,提问作者Le olde Wire
相关产品推荐
相关产品推荐

