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

Unity Netcode for GameObjects:玩家间交互异常问题求助

问题分析与解决

核心问题根源

  1. OwnerClientId匹配错误:你获取的p2p.OwnerClientId与玩家A一致,本质是PlayerToPlayerInteractions组件未正确关联玩家B的NetworkObject,或是该NetworkObject的Owner被错误赋值为玩家A。Netcode中只有NetworkObject组件能维护正确的OwnerClientId,脱离它获取的ID必然出错。
  2. 交互仅本地触发:直接调用interactableItem.Interact()是本地方法执行,没有通过Netcode的RPC(远程过程调用)同步到玩家B的客户端,导致只有发起端有反应。

具体修复步骤

1. 修正玩家对象的NetworkObject绑定

玩家B的游戏对象必须挂载NetworkObject组件,且该组件的Owner在玩家连接时被正确设置(Netcode默认的PlayerPrefab生成逻辑通常会自动处理,但需确认无自定义逻辑干扰)。同时修改PlayerToPlayerInteractions,直接从绑定的NetworkObject获取OwnerId:

public class PlayerToPlayerInteractions : MonoBehaviour
{
    private NetworkObject _networkObject;

    void Awake()
    {
        _networkObject = GetComponent<NetworkObject>();
        if (_networkObject == null)
        {
            Debug.LogError("PlayerToPlayerInteractions必须挂载在带有NetworkObject的对象上");
        }
    }

    public ulong GetOwnerClientId()
    {
        return _networkObject != null ? _networkObject.OwnerClientId : 0;
    }
}

2. 用RPC实现跨客户端交互

跨客户端的交互必须通过RPC触发,确保逻辑同步到目标玩家端。更新接口与实现:

调整IAmInteractable接口

public interface IAmInteractable
{
    void BeingLookedAt(Transform cameraTransform);
    [ServerRpc(RequireOwnership = false)]
    void InteractServerRpc(ulong initiatorClientId, ulong targetClientId);
}

玩家B端的交互实现

public class PlayerInteractable : MonoBehaviour, IAmInteractable
{
    private NetworkObject _networkObject;

    void Awake()
    {
        _networkObject = GetComponent<NetworkObject>();
    }

    public void BeingLookedAt(Transform cameraTransform)
    {
        // 本地视角反馈逻辑(如高亮提示)
    }

    public void InteractServerRpc(ulong initiatorClientId, ulong targetClientId)
    {
        // 服务器验证:确保目标对象的Owner与传入的targetClientId一致
        if (_networkObject.OwnerClientId != targetClientId) return;
        // 发送ClientRpc到目标玩家客户端执行交互逻辑
        InteractClientRpc(initiatorClientId);
    }

    [ClientRpc]
    private void InteractClientRpc(ulong initiatorClientId)
    {
        // 玩家B客户端执行的交互逻辑(如弹出交互UI、播放动画)
        Debug.Log($"玩家{initiatorClientId}发起交互,当前为玩家{NetworkManager.Singleton.LocalClientId}客户端");
    }
}

修改玩家A的Interact方法

private void Interact()
{
    bool interacting = PlayerInputManager.Instance.IsInteracting();

    if (InteractionUI.Instance.IsInteracting(interacting, lookedAtInteractableObject != null))
    {
        if (lookedAtInteractableObject.TryGetComponent<IAmInteractable>(out IAmInteractable interactableItem))
        {
            if (lookedAtInteractableObject.TryGetComponent<PlayerToPlayerInteractions>(out PlayerToPlayerInteractions p2p))
            {
                ulong targetClientId = p2p.GetOwnerClientId();
                // 调用ServerRpc,由服务器转发到目标客户端
                interactableItem.InteractServerRpc(playerScript.OwnerClientId, targetClientId);
            }
        }
    }
}

3. 优化射线检测逻辑

射线检测是本地执行,但需确保检测到的是远端玩家的合法NetworkObject:

Ray ray = new Ray(cameraTransform.position, cameraTransform.forward);
if (Physics.Raycast(ray, out RaycastHit hit, raycastMaxDistance, searchRaycastLM))
{
    Debug.DrawRay(cameraTransform.position, cameraTransform.forward * raycastMaxDistance, Color.green);
    if (hit.collider.gameObject.TryGetComponent<NetworkObject>(out NetworkObject netObj))
    {
        // 排除本地玩家,只处理远端玩家
        if (netObj.OwnerClientId != NetworkManager.Singleton.LocalClientId)
        {
            if (hit.collider.gameObject.TryGetComponent<IAmInteractable>(out IAmInteractable interactableItem))
            {
                lookedAtInteractableObject = hit.collider.gameObject;
                interactableItem.BeingLookedAt(cameraTransform);
            }
        }
    }
}

关键注意点

  • 所有跨客户端操作必须依赖Netcode的RPC(ServerRpc/ClientRpc),本地方法调用无法同步到其他客户端。
  • OwnerClientId只能从NetworkObject组件获取,切勿自行维护或从非NetworkObject对象读取。
  • ServerRpc中建议加入权限验证,避免非法客户端发起恶意交互。

内容的提问来源于stack exchange,提问作者M. Usman Khilji

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 13:03:17