首页 > 解决方案 > Unity Mirror Multiplayer RTS,客户端忽略 hasAuthority 检查

问题描述

我正在使用镜像开发 RTS 游戏并遇到问题。我正在研究单位的自动攻击功能。一切都适用于主机,但不适用于客户端。任何帮助将不胜感激,我已经在这几天了..帮助!或者至少在这里为我指明正确的方向。稍后我将对此进行更好的优化,我只需要了解为什么它在客户端上不起作用。我还注意到客户端会将自己添加到敌人列表中,它似乎忽略了“hasAuthority”检查

这有点写,所以我会尽量让它尽可能容易理解。

单元被实例化,这是附加到它的脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
using System.Linq;

public class UnitFiring : NetworkBehaviour
{

    [SerializeField] private Targeter targeter = null;
    [SerializeField] private GameObject projectilePrefab = null;
    [SerializeField] private Transform projectileSpawnPoint = null;
    [SerializeField] private float fireRange = 10f;
    [SerializeField] private float fireRate = 1f;
    [SerializeField] private float rotationSpeed = 20f;

    private float lastFireTime;


    //auto attack 
    [SerializeField] private Transform ownAimAtPoint = null;
    [SerializeField] private LayerMask layerMask;
    [SerializeField] private int updateFunctionFrequency = 60; //in frames
        

    [ServerCallback]
        private void Update()
        {
    
            
            if (Time.frameCount % this.updateFunctionFrequency != 0) return;
//runs update every 60 frames
            enemyColliders.RemoveAll(Collider => Collider == null);
    //if enemyCollider List has GameObject that was destroyed or null, it removes it from the list.
            
    
        Targetable target = targeter.GetTarget();
               

        if(target == null) 
        {
            ArrayDetect();
                        
            AttackUnit();
            return;  
        }
         

        if (!CanFireAtTarget()) { return; }

        //look at target
        Quaternion targetRotation =
            Quaternion.LookRotation(target.transform.position - transform.position);

        //Rotate
        transform.rotation = Quaternion.RotateTowards
            (transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);

        if(Time.time > (1 / fireRate) +lastFireTime)
        {
            Quaternion projectileRotation = Quaternion.LookRotation(
                target.GetAimAtPoint().position - projectileSpawnPoint.position);

            GameObject projectileInstance = Instantiate(
                projectilePrefab, projectileSpawnPoint.position, projectileRotation);

          
            NetworkServer.Spawn(projectileInstance, connectionToClient);
            
            
            lastFireTime = Time.time;

        }
    }
    [Client]
    private bool CanFireAtTarget()
    {
        return (targeter.GetTarget().transform.position - transform.position).sqrMagnitude 
            <= fireRange * fireRange;
    }

[SerializeField] private Collider[] colliderArray;
[SerializeField] private List<GameObject> enemyColliders;

接下来是带有 Physics.OverlapSphere 的 ArrayDetect 功能检测单元,确保它具有权限并将其添加到enemyCollider 列表中,我还添加了 layerMask 以确保它只检查单元。

[Server]
    private void ArrayDetect()
    {
       colliderArray = Physics.OverlapSphere(ownAimAtPoint.position, fireRange, layerMask);
       
              
       foreach (Collider collider in colliderArray)
        {
           
                Debug.Log("we hit a", collider);

                if (!collider.TryGetComponent<Targetable>(out Targetable potentialTarget))
                {

                    return;

                }


                if (potentialTarget.hasAuthority)
                {

                    return;  //if the hit target is the players, do nothing

                }

                else
                {
                    
                    enemyColliders = enemyColliders.Distinct().ToList();
                    enemyColliders.Add(collider.gameObject);
                    Debug.Log("Found an enemy", potentialTarget);
                }
            
        }
    }

现在 AttackUnit 函数,它会去考虑enemyColliders List 并将它们设置为攻击目标

[ServerCallback]
private void AttackUnit()
{   

    foreach (GameObject enemy in enemyColliders)
    {
    
        Debug.Log("We got confirmed enemy", enemy);
        //GetComponent<Targeter>().CmdSetTarget(enemy);
        targeter.CmdSetTarget(enemy);
            //attack the enemy

    }    
}

这是 Targetable Script,它只返回 AimAtPoint:

public class Targetable : NetworkBehaviour
{
    [SerializeField] private Transform aimAtPoint = null;

    public Transform GetAimAtPoint()
    {
        return aimAtPoint;
    }
}

这是 Targeter 脚本中的 CmdSetTarget 命令:

[Command]
   public void CmdSetTarget(GameObject targetGameObject)
    {
        if(!targetGameObject.TryGetComponent<Targetable>(out Targetable newTarget)) { return; }
        //if game object does not have a target, return

        target = newTarget;
    }

这是我在控制台中遇到的错误,但仅在客户端单元上,主机按应有的方式运行:

Trying to send command for object without authority. Targeter.CmdSetTarget

感谢您花时间阅读本文

标签: c#unity3dmultiplayermirror

解决方案


推荐阅读