Version: 2017.2
Utilizando el NetworkManager (Administrador de red)
Funciones Personalizadas de Generación (Spawn)

Generación (spawning) de objetos

En Unity, crear nuevos objetos con Instantiate() es a veces llamado “spawning” (generación). En el HLAPI de la red, la palabra “spawn” es utilizado para significar algo más especifico. En el modelo autoritario del servidor del HLAPI de la red, para “spawn” un objeto en el servidor significa que el objeto debería ser creado en clientes conectados al servidor, y el objeto será manejado por el sistema de generación (“spawning”). Una vez en el sistema de generación (“spwaning”)las actualizaciones de estado son enviadas a los clientes cuando el objeto cambia en el servidor, y el objeto será destruido en clientes cuando sea destruido en el servidor. Los objetos generados (spawned) también son agregados para configurar los objetos en red que el servidor está manejando, por lo que si otro cliente se une al juego después, los objetos también serán generados en ese cliente. Estos objetos tienen una instancia ID de red única llamada “netId” que es la misma en el servidor y los clientes para cada objeto. Esto es utilizado para el enrutamiento de mensajes a objetos y para identificar objetos.

Cuando los objetos NetworkIdentity son generados en clientes, estos son creados con el estado actual del objeto en el servidor. Esto aplica al estado de movimiento del transform del objeto y variables sincronizadas. Por lo que los objetos cliente siempre están actualizados cuando son creados. Esto evita problemas como objetos siendo generados en una ubicación inicial equivocada, luego apareciendo en su posición correcta cuando un paquete de actualización de estado llega.

Esto suena genial, pero hay algunas preguntas inmediatas que surgen a partir de esto. Cómo es el objeto creado en los clienteS? Y, qué si los objetos cambian entre el tiempo cuando son generados y otro cliente los conecta? Qué versión del objeto es generada para el nuevo cliente entonces?

La generación de objetos en clientes instancia el objeto Cliente del prefab del objeto que fue pasado a NetworkServer.Spawn en el servidor. El panel de pre-visualización del inspector NetworkIdentity muestra el ID del Asset del NetworkIdentity, es este valor que identifica el prefab para que el cliente pueda crear los objetos. Para que esto funcione eficientemente, hay un paso de registración que los clientes deben realizar; estos deben llamar ClientScene.RegisterPrefab para decirle al sistema acerca del asset el cual el objeto cliente será creado.

El registro de prefabs de generación se hace más conveniente por el NetworkManager en el editor. La sección “Spawn Info” del NetworkManager le permite a usted registrar prefabs sin escribir código. Estoy también se puede hacer a través de código cuando un NetworkClient es creado. Para hacerlo en código:

using UnityEngine;
using UnityEngine.Networking;

public class MyNetworkManager : MonoBehaviour 
{
    public GameObject alienPrefab;
    
    NetworkClient myClient;

    // Create a client and connect to the server port
    public void SetupClient()
    {
        ClientScene.RegisterPrefab(alienPrefab);

        myClient = new NetworkClient();

        myClient.RegisterHandler(MsgType.Connect, OnConnected);
        myClient.Connect("127.0.0.1", 4444);
    }
}

En este ejemplo, el usuario arrastraría el asset prefab a la ranura alienPrefab en el script MyNetworkManager. Para que entonces cuando un objeto alien sea generado en el servidor, el mismo tipo de objeto será creado en el del cliente. Este registro de assets asegura que el asset sea cargado a la escena, para que no hay una espera para cargar el asset cuando sea creado. Para casos de uso más avanzado tal como object pools (agrupamiento de objetos) o la creación dinámica de asset, está ClientScene.RegisterSpawnHandler que le permite a funciones callback en ser registradas para la generación por el lado del cliente.

Abajo hay un ejemplo simple de un generado que crea un árbol con un número aleatorio de hojas.

class Tree : NetworkBehaviour
{
    [SyncVar]
    public int numLeaves;
}

class MySpawner : NetworkBehaviour
{
    public GameObject treePrefab;

    public void Spawn()
    {
        GameObject tree = (GameObject)Instantiate(treePrefab, transform.position, transform.rotation);
        tree.GetComponent<Tree>().numLeaves = Random.Range(10,200);
        NetworkServer.Spawn(tree);
    }
}

Para completar este ejemplo, el proyecto tendría un asset prefab para el árbol que tiene un script Tree y un componente NetworkIdentity. Luego en la instancia MySpawner en la escena, la ranura treePrefab sería llenada por el asset prefab árbol. También, el árbol prefab debe estar registrado como un objeto que se puede generar - ya sea utilizando el UI del NetworkManager, o utilizando ClientScene.RegisterPrefab() en el código.

Cuando este código corre, los objetos árbol creados en clientes tendrán el valor correcto de numLeaves (número de hojas) del servidor.

Restricciones

  • Una NetworkIdentity debe estar en la raíz del game object del prefab generado
  • Los script del NetworkBehaviour deben estar en el mismo game object que la NetworkIdentity, no en game objects hijos.
  • Los Prefabs no pueden estar registrados con el NetworkManager al menos de que tengan un NetworkIdentity en su objeto raíz

Flujo de creación de objeto

Las operaciones actuales de flujo que toman lugar para la generación son:

  • Prefab con el componente NetworkIdentity es registrados como que se puede generar (spawnable)
  • GameObject es instanciado del prefab en el servidor
  • El código del juego configura valores iniciales en la instancia (tenga en cuenta que las fuerzas de física 3D aplicadas aquí no toman efecto inmediatamente)
  • NetworkServer.Spawn() es llamado con la instancia
  • El estado del SyncVars en la instancia del servidor son coleccionadas al llamar OnSerialize() en los componentes NetworkBehaviour
  • Un mensaje en red de tipo MsgType.ObjectSpawn es enviado para conectar clientes que incluyen los datos SyncVar
  • OnStartServer() es llamado en la instancia del servidor, y isServer es configurada a true
  • El cliente recibe el mensaje ObjectSpawn y crea una nueva instancia del prefab registrado
  • Los datos SyncVar son aplicados a la nueva instancia en el cliente all llamar OnDeserialize() en los componentes NetworkBehaviour
  • OnStartClient() es llamado en la instancia de cada cliente, y isClient es configurado a true
  • A medida que el gameplay progresa, los cambios a los valores SyncVar son automáticamente sincronizados en clientes. Esto continua hasta que el juego finaliza.
  • NetworkServer.Destroy() es llamado en la instancia en el servidor
  • Un mensaje de tipo MsgType ObjectDestroy es enviado a los clientes
  • OnNetworkDestroy() es llamado en la instancia en los clientes, entonces la instancia es destruida.

Objetos Jugador

Los Objetos jugadores en el HLAPI de la red son especiales en algunas maneras. El flujo para la generación de objetos jugador con el NetworkManager es:

  • Un prefab con NetworkIdentity es registrado como el PlayerPrefab
  • El cliente se conecta al servidor
  • El cliente llama AddPlayer(), un mensaje red de tipo MsgType.AddPlayer es enviado al servidor
  • El servidor recibe mensajes y llama el NetworkManager.OnServerAddPlayer()
  • El GameObject es instanciado del PlayerPrefab en el servidor
  • NetworkManager.AddPlayerForConnection() es llamado con la nueva instancia del jugador en el servidor
  • La instancia del jugador es generada - usted no tiene que llamar NetworkServer.Spawn() para la instancia del jugador
  • Un mensaje red de tipo MsgType.Owner es enviado al cliente que agrego el jugador (solamente ese cliente!)
  • El cliente original recibe el mensaje en red
  • OnStartLocalPlayer() es llamado en la instancia del jugador en el cliente original, y isLocalPlayer es configurado a true

Tenga en cuenta que OnStartLocalPlayer() es llamado después de OnStartClient(), ya que solo sucede cuando llega el mensaje de propiedad del servidor después de que el objeto jugador es generado. Por lo que isLocalPlayer no estará configurado en OnStartClient().

Debido a que OnStartLocalPlayer es solamente llamado para SU jugador, es un buen lugar realizar la inicialización que debería ser solo hecha para el jugador local. Esto puede incluir activar el procesamiento de input, y activar el seguimiento de cámara para el objeto jugador. Típicamente solamente el jugador local tiene una cámara activa.

Generar un objeto con Autoridad de Cliente

Es posible generar objetos y asignar autoridad a los objetos de un cliente en particular. Esto es hecho con NetworkServer.SpawnWithClientAuthority, que toma la NetworkConnection del cliente que será la autoridad como un argumento.

Para estos objetos, la propiedad hasAuthority será true en el cliente con autoridad y OnStartAuthority() será llamado en el cliente con autoridad. Este cliente será capaz de establecer comandos para ese objeto. En otros clientes (y en el anfitrión), hasAuthority será falso.

Los objetos generados con una autoridad de cliente deben tener LocalPlayerAuthority configurados en su NetworkIdentity.

Por ejemplo, para permitirle a un jugador generarse y controlar un objeto:

[Command]
void CmdSpawn()
{
    var go = (GameObject)Instantiate(
       otherPrefab, 
       transform.position + new Vector3(0,1,0), 
       Quaternion.identity);
       
    NetworkServer.SpawnWithClientAuthority(go, connectionToClient);
}
Utilizando el NetworkManager (Administrador de red)
Funciones Personalizadas de Generación (Spawn)