Unet Spawnable prefab не следует за Player Prefab на стороне клиента - PullRequest
0 голосов
/ 18 октября 2018

У меня проблема с пистолетом, не привязанным к игроку.Это происходит только для клиента.Как вы можете видеть на экране, позиция орудия в порядке для хозяина (игрок справа).В Gun Prefab проверена сетевая идентичность с проверкой локальных прав доступа игрока, и сетевое преобразование - то же самое для игрока.Это мой код для игрока:

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

public class Player : NetworkBehaviour 
{

    [SerializeField] float speed = 10f;
    [HideInInspector] public GameObject playerGun;
    public GameObject gunPrefab;

    void Update() 
    {
        if (!isLocalPlayer)
        {
            return;
        }

        Movement();
        if (Input.GetKeyDown(KeyCode.I))
        {
            CmdGetGun();
        }

        if (playerGun)
            CarryGun();
    }

    private void Movement()
    {
        Vector3 position = new Vector3(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical")).normalized * Time.deltaTime * speed;
        transform.position += position;
        MouseMovement();
    }

    private void MouseMovement()
    {
        Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
        mousePosition.Normalize();
        float rotation_z = Mathf.Atan2(mousePosition.y, mousePosition.x) * Mathf.Rad2Deg;
        transform.rotation = Quaternion.Euler(0f, 0f, rotation_z);
    }

    [Command]
    public void CmdGetGun()
    {
        Debug.Log("SPAWNING A GUN");
        playerGun = (GameObject)Instantiate(gunPrefab, transform.position, transform.rotation);
        NetworkServer.SpawnWithClientAuthority(playerGun, connectionToClient);
    }

    public void CarryGun()
    {
        Debug.Log("carring A GUN");
        playerGun.transform.position = new Vector3(transform.position.x, transform.position.y, transform.position.z - 1);
        playerGun.transform.rotation = transform.rotation;
    }
}

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

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

enter image description here

1 Ответ

0 голосов
/ 19 октября 2018

Проблема

Этот метод Command всегда выполняется на сервере .., а также со свойствами на сервере .

Значение: Вы никогда не устанавливаете playerGun на стороне клиента только на сервере в

playerGun = Instantiate ...

Итак, поскольку ваш клиент никогда не получает, его playerGun значение, установленное CarryGun, никогда не выполняется на клиенте.


Решение 1

Чтобы избежать использования метода ClientRpc, установите значение также для всех клиентов.

[Command]
public void CmdGetGun()
{
    Debug.Log("SPAWNING A GUN");
    playerGun = (GameObject)Instantiate(gunPrefab, transform.position, transform.rotation);
    NetworkServer.SpawnWithClientAuthority(playerGun, connectionToClient);

    // Tell the clients to set the playerGun reference. 
    // You can pass it as GameObject since it has a NetworkIdentity
    RpcSetGunOnClients(playerGun);
}

// Executed on ALL clients
// (which does not mean on all components but just the one of 
// this synched GameObject, just to be clear)
[ClientRpc]
private void RpcSetGunOnClients (GameObject gun)
{
    playerGun = gun;
}

Решение 2

Это альтернативное решение, которое в основном выполняет те же действия, что и выше , но ему больше не потребуется метод CarryGun и NetworkTransform, что делает вашкод более эффективен, сохраняя как вызовы методов, так и пропускную способность сети:

Вместо того, чтобы вызывать оружие на верхнем уровне в иерархии на определенную глобальную позицию и вращение

 playerGun = (GameObject)Instantiate(gunPrefab, transform.position, transform.rotation);

и обновлять его позициюи вращать все время на трансферы игрока и передавать их отдельноС помощью NetworkTransform s вы можете просто сделать его дочерним по отношению к самому объекту Player, используя, например, Instantiate(Object original, Vector3 position, Quaternion rotation, Transform parent); на сервере:

 playerGun = (GameObject)Instantiate(gunPrefab, gunLocalPositionOffset, gunLocalRotationOffset, transform);

Это должно всегда держать его висправить положение без необходимости Update и все время синхронизировать его положение и вращение и без каких-либо дальнейших методов и значений передачи.

Все, что вам нужно сделать, это снова иметь ClientRpc, чтобы сообщить клиентама также сделать этот пистолет дочерним для вашего Игрока, используя, например, SetParent(Trasnform parent, bool worldPositionStays):

playerGun.transform.SetParent(transform, false);

и, если необходимо, примените местное смещение положения и смещения вращения.Опять же, вы можете использовать значение, у каждого есть доступ или передать его ClientRpc с сервера - ваш выбор;)

, чтобы ваши методы теперь могли выглядеть как

// In order to spawn the gun with an offset later
// It is up to you where those values should come from / be passed arround
// If you crate your Prefab "correctly" you don't need them at all
// 
// correctly means: the most top GameObject of the prefab has the 
// default values position(0,0,0) and rotation (0,0,0) and the gun fits perfect
// when the prefab is a child of the Player => You don't need any offsets at all
Vector3 gunLocalPositionOffset= Vector3.zero;
Quaternion gunLocalRotationOffset= Quaternion.identity;

[Command]
public void CmdGetGun()
{
    Debug.Log("SPAWNING A GUN");

    // instantiates the prefab as child of this gameObject
    // you still can spawn it with a local offset position
    // This will make its position be already synched in the Players own
    // NetworkTransform -> no need for a second one
    playerGun = (GameObject)Instantiate(gunPrefab, gunLocalPositionOffset, gunLocalRotationOffset, gameobject);

    // you wouldn't need this anymore since you won't change the spoition manually
    // NetworkServer.SpawnWithClientAuthority(playerGun, connectionToClient);
    NetworkServer.Spawn(playerGun);

    // Tell the clients to set the playerGun reference. 
    // You can pass it as GameObject since it has a NetworkIdentity
    RpcSetGunOnClients(playerGun);
}

// Executed on all clients
[ClientRpc]
private void RpcSetGunOnClients (GameObject gun)
{
    // set the reference
    playerGun = gun;

    // NetworkServer.Spawn or NetworkServer.SpawnWithClientAuthority doesn't apply 
    // the hierachy so on the Client we also have to make the gun a child of the player manually

    // use the flag worldPositionStays to avoid a localPosition offset
    playerGun.transform.SetParent(transform, false);

    // just to be very sure you also could (re)set the local position and rotation offset later
    playerGun.transform.localPosition = gunLocalPositionOffset;
    playerGun.transform.localrotation = gunLocalRotationOffset;
}

Update1

Для преодоления проблемы с подключенными позже клиентами см. этот ответ .

Он предлагает использовать [SyncVar] и переопределить OnStartClient.Принятая версия может выглядеть так:

 // Will be synched to the clients
 // In our case we know the parent but need the reference to the GunObject
 [SyncVar] public NetworkInstanceId playerGunNetId;

[Command]
public void CmdGetGun()
{
    Debug.Log("SPAWNING A GUN");
    playerGun = (GameObject)Instantiate(gunPrefab, gunLocalPositionOffset, gunLocalRotationOffset, transform);

    // Set the playerGunNetId on the Server
    // SyncVar will set on all clients including
    // newly connected clients
    playerGunNetId = playerGun.GetComponent<NetworkIdentity>().netId;

    NetworkServer.Spawn(playerGun);

    RpcSetGunOnClients(playerGun);
}

public override void OnStartClient()
{
    // When we are spawned on the client,
    // find the gun object using its ID,
    // and set it to be our child
    GameObject gunObject = ClientScene.FindLocalObject(playerGunNetId);
    gunObject.transform.SetParent(transform, false);
    gunObject.transform.localPosition = gunLocalPositionOffset;
    gunObject.transform.localrotation = gunLocalRotationOffset;
}

( Примечание , вам все еще нужно ClientRpc для уже подключенных клиентов.)


Update2

Метод из Update1 может не работать, потому что playetGunNetId может быть еще не установлен при выполнении OnStartPlayer.

Чтобы преодолеть это, вы можете использовать метод подключения для нашего SyncVar.Что-то вроде

// The null reference might somehow still come from
// ClientScene.FindLocalObject
[SyncVar(hook = "OnGunIdChanged"]
private NetworkInstanceId playerGunNetId;

// This method is executed on all clients when the playerGunNetId value changes. 
// Works when clients are already connected and also for new connections
private void OnGunIdChanged (NetworkInstanceId newValue)
{
    // Honestly I'm never sure if this is needed or not...
    // but in worst case it's just redundant
    playerGunNetId = newValue;

    GameObject gunObject = ClientScene.FindLocalObject(playerGunNetId);   
    gunObject.transform.SetParent(transform, false);
    gunObject.transform.localPosition = gunLocalPositionOffset;
    gunObject.transform.localrotation = gunLocalRotationOffset;
}

Теперь вам даже не нужно ClientRpc, поскольку все обрабатывается hook как для уже подключенных, так и для вновь подключенных клиентов.

...