You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity Netcode多人游戏:玩家靠近行星时如何重设父对象使其随行星移动?

Unity Netcode 实现玩家随行星移动的正确方案

核心原则:Netcode 中对象层级变更必须由服务器权威控制,客户端不能私自修改父对象,否则会出现跨客户端状态不同步的问题。以下是两种可靠的实现方式:

方式一:服务器判定 + ClientRpc 同步

1. 行星端服务器逻辑(检测距离并触发同步)

在行星的 NetworkBehaviour 脚本中,仅让服务器执行距离检测,当玩家进入/离开范围时调用 ClientRpc 通知所有客户端更新父子关系:

public class Planet : NetworkBehaviour
{
    public float captureRange = 5f;

    private void Update()
    {
        if (!IsServer) return; // 仅服务器执行检测

        foreach (var client in NetworkManager.Singleton.ConnectedClientsList)
        {
            var playerObj = client.PlayerObject;
            if (playerObj == null) continue;

            float distance = Vector3.Distance(transform.position, playerObj.transform.position);
            bool isInRange = distance <= captureRange;
            bool isAlreadyChild = playerObj.transform.parent == transform;

            if (isInRange && !isAlreadyChild)
            {
                SetPlayerAsChildRpc(playerObj.NetworkObject, transform);
            }
            else if (!isInRange && isAlreadyChild)
            {
                RemovePlayerFromChildRpc(playerObj.NetworkObject);
            }
        }
    }

    [ClientRpc]
    private void SetPlayerAsChildRpc(NetworkObject playerNetObj, Transform planetTransform)
    {
        playerNetObj.transform.SetParent(planetTransform);
        playerNetObj.transform.localPosition = Vector3.zero; // 避免穿模,可根据需求调整
    }

    [ClientRpc]
    private void RemovePlayerFromChildRpc(NetworkObject playerNetObj)
    {
        playerNetObj.transform.SetParent(null);
    }
}

2. 注意事项

  • 必须通过 IsServer 限制检测逻辑仅在服务器运行,防止客户端私自触发同步。
  • 加状态判断(isAlreadyChild)避免重复调用 RPC,减少网络开销。

方式二:NetworkVariable 状态同步(更稳定)

用 NetworkVariable 同步玩家的父行星 ID,客户端监听变量变化自动更新层级,适合需要持续同步的状态:

1. 玩家端脚本

public class Player : NetworkBehaviour
{
    // 存储父行星的 NetworkObjectId,0 表示无父对象
    private readonly NetworkVariable<ulong> _parentPlanetNetId = 
        new NetworkVariable<ulong>(0, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server);

    private void Awake()
    {
        // 监听父行星 ID 变化,自动更新层级
        _parentPlanetNetId.OnValueChanged += OnParentPlanetUpdated;
    }

    private void OnParentPlanetUpdated(ulong oldId, ulong newId)
    {
        if (newId == 0)
        {
            transform.SetParent(null);
            return;
        }

        // 通过 ID 获取行星对象并设置父级
        if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(newId, out var planetNetObj))
        {
            transform.SetParent(planetNetObj.transform);
            transform.localPosition = Vector3.zero;
        }
    }

    // 服务器调用:设置父行星
    [ServerRpc(RequireOwnership = false)]
    public void SetParentPlanetServerRpc(ulong planetNetId)
    {
        _parentPlanetNetId.Value = planetNetId;
    }

    // 服务器调用:解除父行星
    [ServerRpc(RequireOwnership = false)]
    public void ClearParentPlanetServerRpc()
    {
        _parentPlanetNetId.Value = 0;
    }
}

2. 行星端脚本(服务器检测并更新玩家状态)

public class Planet : NetworkBehaviour
{
    public float captureRange = 5f;

    private void Update()
    {
        if (!IsServer) return;

        foreach (var client in NetworkManager.Singleton.ConnectedClientsList)
        {
            var player = client.PlayerObject.GetComponent<Player>();
            if (player == null) continue;

            float distance = Vector3.Distance(transform.position, client.PlayerObject.transform.position);
            bool isInRange = distance <= captureRange;

            if (isInRange && player._parentPlanetNetId.Value != NetworkObject.NetworkObjectId)
            {
                player.SetParentPlanetServerRpc(NetworkObject.NetworkObjectId);
            }
            else if (!isInRange && player._parentPlanetNetId.Value == NetworkObject.NetworkObjectId)
            {
                player.ClearParentPlanetServerRpc();
            }
        }
    }
}

通用注意事项

  • 玩家移动逻辑需适配父对象坐标系:如果玩家原本用世界坐标移动,设为行星子对象后需改为局部坐标移动,否则会出现移动异常。
  • 测试必须用多客户端模式:比如在 Unity Editor 中开启「Multiplayer Mode」,同时运行 Host 和 Client 实例,验证状态同步是否一致。
  • 避免频繁同步:通过状态判断(如是否已经是子对象)减少 RPC 或 NetworkVariable 的更新次数,降低网络负载。

内容的提问来源于stack exchange,提问作者Podhakkin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 03:52:36