Noticias

¡RECUERDA QUE SI ERES UN NUEVO USUARIO, DEBES PRESENTARTE PARA PODER PUBLICAR MENSAJES! | TENEMOS CANAL OFICIAL DE TELEGRAM: t.me/unity3dspain

Problema delay en spawn

Iniciado por Portief, Febrero 16, 2017, 07:58:15 PM

Tema anterior - Siguiente tema
Febrero 16, 2017, 07:58:15 PM Ultima modificación: Febrero 17, 2017, 06:05:33 PM por pioj
Buenas a todos, os comento:
 
Soy programador nivel intermedio avanzado y con escaso conocimiento de redes, me he leído la documentación de unity y hay cosas que creo que no he llegado a comprender del todo bien. Estoy utilizando el código de unity para montarme un juego multijugador en el que un jugador hace de host y he seguido un par de tutos y como ya he comentado con la información de la pagina. Problema:
 
A la hora de disparar y moverse el cliente no spawnea la bala en la posición correcta sino unos centímetros a la derecha o izquierda depende en que sentido se mueva. Esto en el que hace de host no pasa como es de esperar. Estoy utilizando una funcion con command y le paso como parámetros posición velocidad y rotación de la bala la spawneo y la envió con networkServer.spawn a todos. Como ya he comentado esto hace que el cliente reciba la bala tarde si se esta moviendo, puedo llegar a entender que al serializar los datos y enviarlos por red haya cierto delay, pero si lo estoy probando en mi ordenador debería de ser cero y no lo es ademas de que unos centímetros tienen demasiada repercusión en la jugabilidad. He cambiado de canal el script del disparo (disparo y andar no están en el mismo script aunque no creo que importe) y sigue estando el problema, tambien he hecho un monton de pruebas sin exito. Alguno sabe si hay alguna forma de reducir eso? como se hace en la realidad? Estaría muy agradecido con cualquier ayuda. 
 
Adjunto el código, es código de testeo, nada depurado:
 

using UnityEngine;
using UnityEngine.Networking;
//[NetworkSettings(channel = 1)]
public class Disparo : NetworkBehaviour {
    //Animaciones
    public bool SacarGuardar;
    //Disparo
    public Rigidbody Bala;
    public Transform SpawnBala;
    public int VelocidadBala;
    //Velocidad movimiento jugador
    public Rigidbody m_Rigidbody;
    // Use this for initialization
    void Start () {
        m_Rigidbody = GetComponent<Rigidbody>();
    }
    // Update is called once per frame
    [ClientCallback]
    void Update () {
        if (!isLocalPlayer)
        {
            return;
        }
        SacarGuardar = Input.GetButton("Fire2") ? true : SacarGuardar;
        if (SacarGuardar == true)
            if (Input.GetButtonDown("Fire1"))
            {
                CmdSpawnBalaNew(m_Rigidbody.velocity, VelocidadBala, SpawnBala.forward, SpawnBala.position, SpawnBala.rotation);
            }
    }
    [Command]
    void CmdSpawnBalaNew(Vector3 rigidbodyVelocity,float velocidadb, Vector3 forward, Vector3 position, Quaternion rotation)
    {
        Rigidbody bullet = Instantiate(Bala, position, rotation) as Rigidbody;
        Vector3 velocity = rigidbodyVelocity + velocidadb * forward;
        bullet.velocity = velocity;
        NetworkServer.Spawn(bullet.gameObject);
        //(bullet.gameObject, 4.0f);
    }
   
}

 
 

No entendí una cosa. El cliente cuando dispara crea la bala en un lugar incorrecto, o cuando los demás clientes y/o el host dispara los clientes la reciben en un lugar incorrecto?.

No conozco mucho del nuevo sistema multiplayer de unity pero por lo que veo estás creando y pasando muchos datos a través de la red, lo recomendable sería enviar lo mínimo indispensable, en el caso de un FPS podría ser tanto el disparar y realizar todo el calculo en el cliente para luego avisarle al host que le diste al objetivo (no recomendable) o avisarle al host que queres disparar y este se encargue de hacerlo y comunicarle a todos los clientes si la bala dio en el objetivo o no.

La proyección de la bala y el efecto no tienen que estar directamente relacionados a la funcionalidad del disparo en sí.

Exacto, no creo que instanciar una bala sea buena idea, puedes hacer un raycast en el servidor y un linerender en el cliente.
 
Por otra parte si tienes perfectamente sincronizado la posición y rotación en servidor y cliente puedes crear objetos en algunos clientes sin necesidad de crearlos en todos los clientes, de manera offline vamos, ten en cuenta que a no ser que todos los jugadores estén en pantalla no tienen por qué ver la bala, y si creas una bala en clientes que estén lejos será un gasto innecesario de ancho de banda.

Toma muy en cuenta lo que dice stick.
 
La programacion en red no es algo simple, y el sistema UNET hace las cosas aún más difíciles de lo que parecen, cuando se requiere un juego en red se hablan de muchas cosas el uso de múltiples Socket TCP y UDP la abstracción de eventos a sincronizar y otro tanto más. Te recomiendo lo siguiente.
 
¿Que debo sincronizar? ¿Es aceptable la pérdida de datos? ¿Cómo sincronizar un evento?
 
Se entiende que en un juego de red los clientes no están perfectamente sincronizados, y este es el error de los que recién comienzan, juegos como Battlefield utilizan metodos de interpolacion muy transparentes para hacernos creer que estamos sincronizados, pero en realidad las instancias de los clientes se encuentran en muchas ocasiones muy disparejas. La interpolación tiene pro y contras por ejemplo la tan odiada "Compensación de Lag" que experimentan los jugadores.
 
Si tu idea es armar un juego con UNET te recomiendo que leas acerca de QoS, que entiendas los canales de UNET y como trabaja Network Transport.
 
Ahora, vamos con las preguntas - ¿Que debo sincronizar?
 
Bien, para la sincronización de la posicion, rotación y la cámara del jugador se pueden utilizar 5 valores decimales. posX, posY, posZ, rotY, rotCamX. Con un paquete que envíe estos datos ya puedes simular un interpolacion en el cliente. 
 
¿Es aceptable la pérdida de datos?
 
Cuando se trata de la sincronización de la posicion estamos enviando 30 Paquetes x segundo, basándonos en VALVE los llamados Network Tick, si 1, 2 o 3 paquetes se pierden no es de mucha importancia debido a que se estará recibiendo constantemente nuevos datos, en caso de la posicion se utiliza un TimeSpan para saber cuando se envió el paquete, debido a que una conexión UDP no nos garantiza la posicion correcta de los paquetes.
 
¿Cómo sincronizar un evento?
 
Existen 2 tipos de eventos, los "Requeridos" y "No requeridos", muy similar a la respuesta anterior, los requeridos son aquellos eventos de los cuales nos tenemos que asegurar que lleguen, por ejemplo el daño recibido o producido, el score, drop o drag de un arma, un disparo, los no requeridos pueden ser eventos de la escena, que pueden fallar por que no afectarían directamente al cliente.
 
 
 
Tu caso en particular. Instanciar un objeto en la posicion correcta.
 
Bien, partimos de que estas enviando desde un cliente la posicion de Instancia, esto es un error grave debido a "La interpolación", ¿Como solucionarlo?, creamos un paquete, que indique qué objeto va a spawnear y desde donde, en este caso, se spawnea una "bala" desde X cliente.
 
Cliente -> Enviar evento de instanciar bala al servidor -> Servidor -> Envía a todos los clientes el evento de instanciar bala -> lo clientes instancian la bala en el lugar donde actualmente tienen sincronizado dicho jugador. 
 
¿Se entiende?
 
Veamos un Código de Ejemplo.
 
El cliente que efectúa el disparo.
 

private void Shot()
{
  //Ejecutar instancia local..
 
  //Enviar evento al Servidor...
  var pkg = new NetworkSyncEvent{EventID = 1, Data="ID_OBJETO_BALA"};
  NetworkServer.Send(pkg);
}
public class NetworkSyncEvent
{
  public int EventID;
  public string Data; // De ser necesario para enviar alguna informacion, en este caso el id del objeto a instanciar.
}

 
El cliente que recibe el evento.
 

private void NetworkEjecuteSyncEvent(NetworkClient sender, int eventID, string data)
{
switch(eventID)
{
   case 1:
   {
      int id_object = int.Parse(data);
      Instantiate(Network.SyncObject[id_object], sender.position, sender.camera.rotation);
   }
   break;
}
}

 
Los clientes locales no son iguales a un cliente remoto, es decir, UNET es simple, pero a la misma vez poco escalable.
 
 
 
Recomendación:
 
Intenta programar tu propio servidor UDP/TCP, experimenta con Sockets, a la larga te vas a dar cuenta de que UNET te limita a muchas cosas, hablo de "HLAPI".
 
LLAPI es otro mundo, un gran trabajo de Unity Technology el problema es que no liberan Network Transport y los desarrolladores no están muy dispuestos a distribuir el código del servidor junto con el del cliente. 
 
 
 
Espero te haya servido! 

Febrero 18, 2017, 06:01:25 PM #4 Ultima modificación: Febrero 18, 2017, 06:24:29 PM por Portief
Gracias a todos por responder. Stick tomo nota, aunque mi idea era hacer algo mas tipo battelfield que call of duty, en el hecho de que pudieras ver la bala y llegar a esquivarla. Respecto a la respuesta de francoe1 gracias por dar tanto detalle. Tengo dudas en tu estructura de script, no veo la forma en que el servidor recibe los datos, pero investigare un poco mas en esa dirección, por cierto si quiero sincronizar la posicion del jugador de esa forma como controlo la frecuencia en el envio de datos? Respecto a montarme mi propio código me gustaría mucho pero de momento no tengo el conocimiento y ojala el tiempo para ponerme a investigar como un loco el tema.

Febrero 19, 2017, 04:27:25 PM #5 Ultima modificación: Febrero 19, 2017, 04:28:53 PM por stick
@francoe1 Wow! creo que pocas veces he visto una respuesta tan detallada y bien formada.
 
@Portief No necesitas instanciarla en la red para lograr eso. Justamente si el battelfield intentara algo como eso no existiría ese juego ya que la cantidad de datos necesarios sería enorme, imagínate en los escenarios de gran envergadura si se transmitiera cada posición y rotación y velocidad de cada bala en cada instante del juego solo sería jugable en la NASA. Para lograr eso tenes que pasar los parámetros necesarios para ser lo más preciso posible y que el cliente lo represente, una vez que empieces a estudiar como están hecho la mayoría de los juegos multiplayers de las grandes empresas verás como pierden un poco de su magia :P.

Te recomiendo leer este libro que por desgracia es de Unity 4 por lo que si usas el sistema integrado actual puede que no te sirva mucho, pero si te va a dar mucho conocimiento general de como se deben hacer las cosas.
https://www.packtpub.com/game-development/unity-multiplayer-games


Cualquier cosa me podes escribir por skype: maurosticklp (pero sin llamar).

Etiquetas: