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

基于Unity网络服务(高层API)的建筑Spawn权限控制问题

解决方案:基于Unity Netcode for GameObjects的建筑权限控制

核心思路是通过网络同步的所有者ID验证玩家身份,客户端做前置检查减少无效请求,服务器做最终权威验证防止作弊。下面是具体实现步骤:

1. 给建筑添加所有者身份标记

在你的建筑预制体上挂载一个继承自NetworkBehaviour的脚本(比如PlayerBuilding),用NetworkVariable同步所有者的客户端ID,确保所有客户端和服务器都能获取到这个信息:

using Unity.Netcode;
using UnityEngine;

public class PlayerBuilding : NetworkBehaviour
{
    // 同步建筑所属的客户端ID,服务器权威更新
    public NetworkVariable<ulong> OwnerClientId = new NetworkVariable<ulong>();

    // 当建筑在网络上生成时,由服务器设置所有者ID
    public override void OnNetworkSpawn()
    {
        // 只有服务器能设置这个值,避免客户端篡改
        if (IsServer)
        {
            // 这里假设你在生成建筑时,已经知道要分配给哪个玩家
            // 比如生成建筑时传入targetClientId:OwnerClientId.Value = targetClientId;
        }
    }
}

2. 客户端侧的点击权限前置检查

在处理建筑点击的逻辑里(比如通过Raycast检测点击,或者UI按钮事件),先在本地验证当前玩家是否是建筑的所有者,只有通过检查才向服务器发送Spawn请求:

using Unity.Netcode;
using UnityEngine;
using UnityEngine.EventSystems;

public class BuildingClickHandler : MonoBehaviour
{
    void Update()
    {
        // 忽略UI点击,只处理场景中的建筑点击
        if (Input.GetMouseButtonDown(0) && !EventSystem.current.IsPointerOverGameObject())
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ray, out RaycastHit hit))
            {
                var building = hit.collider.GetComponent<PlayerBuilding>();
                if (building != null)
                {
                    // 检查当前客户端ID是否和建筑所有者ID匹配
                    if (NetworkManager.Singleton.LocalClientId == building.OwnerClientId.Value)
                    {
                        // 向服务器发送Spawn单位的请求
                        building.SpawnUnitServerRpc();
                    }
                    else
                    {
                        // 可选:给玩家提示,比如"这不是你的建筑!"
                        Debug.Log("你不能操作不属于你的建筑");
                    }
                }
            }
        }
    }
}

3. 服务器侧的权威验证与单位生成

在PlayerBuilding脚本里添加一个ServerRpc方法,服务器收到请求后再次验证身份(防止客户端绕过本地检查作弊),验证通过后再生成单位:

// 在PlayerBuilding脚本中添加
[ServerRpc(RequireOwnership = false)] // 关闭默认所有权检查,我们自定义验证逻辑
public void SpawnUnitServerRpc(ServerRpcParams rpcParams = default)
{
    // 获取发送请求的客户端ID
    ulong requestingClientId = rpcParams.Receive.SenderClientId;

    // 再次验证:请求者是否是建筑的所有者
    if (requestingClientId == OwnerClientId.Value)
    {
        // 加载单位预制体(确保预制体已在NetworkManager的Prefabs列表中注册)
        GameObject unitPrefab = Resources.Load<GameObject>("YourUnitPrefab");
        if (unitPrefab != null)
        {
            // 在建筑位置附近生成单位
            Vector3 spawnPos = transform.position + Vector3.forward * 2f;
            GameObject newUnit = Instantiate(unitPrefab, spawnPos, Quaternion.identity);
            
            // 让单位归请求的玩家所有
            NetworkObject unitNetworkObj = newUnit.GetComponent<NetworkObject>();
            unitNetworkObj.SpawnAsPlayerObject(requestingClientId);
            
            // 这里可以添加单位向目标移动的逻辑
            // 比如获取目标点,设置导航目标
        }
    }
    else
    {
        // 可选:记录作弊行为日志
        Debug.LogWarning($"客户端 {requestingClientId} 尝试操作不属于自己的建筑!");
    }
}

4. 建筑生成时的所有者分配

当你在服务器上生成建筑时,记得给OwnerClientId赋值对应的玩家ID。比如在玩家加入时生成建筑:

// 在你的GameManager或NetworkManager的回调中
public void OnPlayerJoined(ulong clientId)
{
    // 加载建筑预制体
    GameObject buildingPrefab = Resources.Load<GameObject>("YourBuildingPrefab");
    // 给两个玩家分配不同的建筑生成位置
    Vector3 spawnPos = clientId == 0 ? new Vector3(-5, 0, 0) : new Vector3(5, 0, 0); 
    GameObject newBuilding = Instantiate(buildingPrefab, spawnPos, Quaternion.identity);
    
    NetworkObject buildingNetworkObj = newBuilding.GetComponent<NetworkObject>();
    buildingNetworkObj.Spawn(); // 先Spawn,再设置所有者
    
    // 获取建筑的PlayerBuilding组件,设置所有者ID
    PlayerBuilding building = newBuilding.GetComponent<PlayerBuilding>();
    building.OwnerClientId.Value = clientId;
}

关键注意事项

  • 服务器权威:永远不要信任客户端的输入,所有关键操作(比如生成单位)必须由服务器验证后执行,客户端只做请求和本地反馈。
  • NetworkVariable的同步:NetworkVariable会自动同步到所有客户端,确保每个玩家看到的建筑所有者信息都是一致的。
  • 自定义所有权验证:关闭ServerRpc的RequireOwnership是因为我们的建筑不属于玩家对象本身,只是归玩家所有,所以需要通过OwnerClientId做自定义身份验证。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:28:30