Проблема преобразования сети Unity - объекты клиента появляются в неправильном месте - PullRequest
0 голосов
/ 29 января 2019

Я работаю над форнитской игрой для Unity.

Игрок появляется в игре, имеет способность порождать кубы, чтобы создать «базу».

В моно все отлично работает, но у меня странная проблема с сетью.На сервере мой игрок может отлично порождать кубы в соответствии с точкой попадания Raycast, находясь на клиенте, даже если игрок является клоном префаба Player, порожденные объекты всегда либо оказываются в начале координат мира, а не в точке попадания Raycast -или - если я удаляю if (!isPlayLocal) {return;} из скрипта игрока, содержащего информацию Raycast, куб появляется неточно и без соответствующего материала.

Я постараюсь уточнить код, чтобы разместить его здесь, но я думаю, что это может быть несколько вещей.

Аутентификация локального игрока отключена в префабах порождения, и все префабы зарегистрированы в Network Manager.

enter image description here

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

public class BuildingSystemNew : NetworkBehaviour
{

    [SerializeField] private Camera playerCamera; 
    [SerializeField] private GameObject blockTemplatePrefab; 
    [SerializeField] private GameObject blockPrefab; 
    [SerializeField] private Material templateMaterial;  
    [SerializeField] private LayerMask buildableSurfacesLayer;

    private bool buildModeOn = false;
    private bool canBuild = false;
    private bool crossHairOn = false;
    private BlockSystem bSys;
    public Texture2D crosshairImage;
    private int blockSelectCounter = 0;
    private GameObject weapon;
    private Vector3 buildPos;
    private GameObject currentTemplateBlock;  

    private void Start()
    {
        bSys = GetComponent<BlockSystem>();
    }

    private void Update()
    {
        if (isLocalPlayer == false)
            return;  

        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;

        if (Input.GetKeyDown("e"))
        {
            buildModeOn = !buildModeOn;

            if (buildModeOn)
            {
                // weapon.SetActive(false);
                crossHairOn = true;
            }
            else
            {
                // weapon.SetActive(true);
                crossHairOn = false;
            }
        }

        if (Input.GetKeyDown("r"))
        {
            blockSelectCounter++;
            if (blockSelectCounter >= bSys.allBlocks.Count) blockSelectCounter = 0;
        }

        if (buildModeOn)
        {
            RaycastHit buildPosHit;

            if (Physics.Raycast(playerCamera.ScreenPointToRay(new Vector3(Screen.width / 2, Screen.height / 2, 0)), out buildPosHit, 10, buildableSurfacesLayer))
            {
                Vector3 point = buildPosHit.point;
                buildPos = new Vector3(Mathf.Round(point.x), Mathf.Round(point.y), Mathf.Round(point.z));
                canBuild = true;
            }
            else
            {
                Destroy(currentTemplateBlock.gameObject);
                canBuild = false;
            }
        }

        if (!buildModeOn && currentTemplateBlock != null)
        {
            Destroy(currentTemplateBlock.gameObject);
            canBuild = false;
        }

        if (canBuild && currentTemplateBlock == null)
        {
            currentTemplateBlock = Instantiate(blockTemplatePrefab, buildPos, Quaternion.identity);
            currentTemplateBlock.GetComponent<MeshRenderer>().material = templateMaterial;
        }

        if (canBuild && currentTemplateBlock != null)
        {
            currentTemplateBlock.transform.position = buildPos;

            if (Input.GetMouseButtonDown(0))
            {
                CmdPlaceBlock();
            }
            else if (Input.GetMouseButtonDown(1))
            {
                CmdDestroyBlock();
            }
        }
    }

    [Command]
    public void CmdPlaceBlock()
    {
        GameObject newBlock = Instantiate(blockPrefab, buildPos, Quaternion.identity);
        Block tempBlock = bSys.allBlocks[blockSelectCounter];
        newBlock.name = tempBlock.blockName + "-Block";
        newBlock.GetComponent<MeshRenderer>().material = tempBlock.blockMaterial;
        NetworkServer.SpawnWithClientAuthority(newBlock, connectionToClient);  
    }

    [Command]
    private void CmdDestroyBlock()
    {
        RaycastHit hit;
        Ray ray = playerCamera.ScreenPointToRay(Input.mousePosition);

        if (Physics.Raycast(ray, out hit)) 
        {
            var objectHit = hit.collider.gameObject;

            if (hit.collider.gameObject.tag == "Block")
            {
                Destroy(objectHit);
            }
        }
    }

    void OnGUI()
    {
        if (crossHairOn == true) 
        {
            float xMin = (Screen.width / 2) - (crosshairImage.width / 2);
            float yMin = (Screen.height / 2) - (crosshairImage.height / 2);
            GUI.DrawTexture(new Rect(xMin, yMin, crosshairImage.width, crosshairImage.height), crosshairImage);
        }
    }
}

1 Ответ

0 голосов
/ 29 января 2019

Проблема

Вы звоните

[Command]
public void CmdPlaceBlock()
{
    GameObject newBlock = Instantiate(blockPrefab, buildPos, Quaternion.identity);
    Block tempBlock = bSys.allBlocks[blockSelectCounter];
    newBlock.name = tempBlock.blockName + "-Block";
    newBlock.GetComponent<MeshRenderer>().material = tempBlock.blockMaterial;
    NetworkServer.SpawnWithClientAuthority(newBlock, connectionToClient);
}

без параметра.

A Command вызывается на клиенте, но выполняетсяна сервере

=> с локальными переменными сервера !

Так, например, buildPos всегда будет иметь значение по умолчанию 0,0,0 на сервере, поскольку из-за

if(!isLocalPlayer) return;

позже строка

buildPos = new Vector3(Mathf.Round(point.x), Mathf.Round(point.y), Mathf.Round(point.z));

никогда не выполняетсяна сервере.То же самое относится, например, к blockSelectCounter и, возможно, к другим значениям, от которых зависит CmdPlaceBlock.


Решение

Вы должны передать клиенту buildPosзначение (а также все другие значения, которые различаются на клиенте и сервере) для команды сервера, поэтому сервер знает, в какую правильную позицию должен быть помещен новый объект:

//...
    if (Input.GetMouseButtonDown(0))
    {
        CmdPlaceBlock(buildPos);
    }
//...



[Command]
public void CmdPlaceBlock(Vector3 spawnPosition)
{

    GameObject newBlock = Instantiate(blockPrefab, spawnPosition, Quaternion.identity);
    Block tempBlock = bSys.allBlocks[blockSelectCounter];
    newBlock.name = tempBlock.blockName + "-Block";
    newBlock.GetComponent<MeshRenderer>().material = tempBlock.blockMaterial;
    NetworkServer.SpawnWithClientAuthority(newBlock, connectionToClient);

}

Я добавил только пример дляЗнание позиции всегда будет отличаться на клиенте и сервере.Но это также может относиться к другим вашим значениям, таким как, например, blockSelectCounter.

. Сделайте это изменение для всех значений, которые должны быть значениями клиента и , а не значениями сервера.

Обратите внимание, что типы, которые можно передавать между сетевыми методами, ограничены!Вы не можете передавать, например, любые ссылки на компоненты.

Допустимые типы аргументов:

  • Базовый тип (byte, int, float, string, UInt64 и т. Д.)
  • Встроенный математический тип Unity (Vector3, Quaternion и т. Д.),
  • Массивы основных типов
  • Структуры, содержащие допустимые типы
  • NetworkIdentity
  • NetworkInstanceId
  • NetworkHash128
  • GameObject с подключенным компонентом NetworkIdentity.

Дополнительные подсказки

Для удобства чтения и количества строк вы должны изменить такие вещи, как, например,

if (buildModeOn)
{
    // weapon.SetActive(false);
    crossHairOn = true;
}
else
{
    // weapon.SetActive(true);
    crossHairOn = false;
}

на просто

// weapon.SetActive(!buildModeOn);
crossHairOn = buildModeOn;

и проверять bools не как

if (isLocalPlayer == false)

, а скорее

if(!isLocalPlayer)

Просто читать / писать проще;)

...