Configurando un proyecto Multijugador desde el principio
Generación (spawning) de objetos

Utilizando el NetworkManager (Administrador de red)

El NetworkManager es un componente para manejar el estado de red de un juego multi-jugador. En realidad es implementado enteramente utilizando el HLAPI, entonces cualquier cosa que haga está disponible para desarrolladores en otras formas. Sin embargo, el NetworkManager envuelve muchas funcionalidades útiles en un solo lugar y hace que crear, correr, y depurar juegos multijugador sean lo más simple posible.

El NetworkManager puede ser utilizado completamente sin scripting. Tiene controles del inspector en el editor que permite que la configuración de todas sus características. El NetworkManagerHUD proporciona una simple, predeterminada interfaz de usuario en el tiempo de ejecución que permite que los juegos de red sean controlados por el usuarios. Para usos avanzados, los desarrolladores puede derivar una clase del NetworkManager y personalizar su comportamiento al anular cualquiera de los hooks (ganchos) funcionales virtuales que proporciona.

Las características del NetworkManager incluye:

  • Manejo del estado de juego
  • Manejo de generaciones (Spawning)
  • Manejo de escenas
  • Información de depuración
  • Emparejamiento de partidas
  • Personalización

Comenzando con el NetworkManager

El NetworkManager puede ser utilizado como el componente nuclear de control de un juego multijugador. Para comenzar, cree un game object vacío en la escena de inicio, o escoja un objeto de manejo conveniente. Luego agregue el componente NetworkManager del menú de items Network/NetworkManager. El nuevo componente NetworkManager agregado debería verse algo así:

El inspector para el NetworkManager en el editor le permite a usted configurar y controlar muchas cosas relacionadas a la red.

El NetworkManagerHUD es otro componente que funciona con el NetworkManager. Éste le da a usted una interfaz de usuario simple cuando el juego está corriendo para controlar el estado de red. Este es bueno para comenzar con un proyecto de red, pero no está intencionado para ser utilizado como el UI final del juego. El Network ManagerHUD se ve así:

Los juegos verdaderos tendrá una interfaz de usuario apropiada para controlar el estado del juego y para permitirle a los jugadores escoger qué tipo de juego jugar. Pero, para comenzar, nosotros podemos utilizar esto para controlar el juego.

Manejo del Estado del Juego

Un juego de red multijugador puede correr en tres modos - como un cliente, como un servidor dedicado, o como un “host” (anfitrión) que es ambos un cliente y un servidor a la misma vez. El networking (red) es diseñado para hacer que el mismo código del juego y los assets funcionen en todos estos casos. Desarrollar para la versión de un solo jugador del juego y la versión multijugador debería ser la misma cosa.

El NetworkManager tiene métodos para ingresar en cada uno de estos modos. NetworkManager.StartClient(), NetworkManager.StartServer() y NetworkManager.StartHost() están todos disponibles para código script, por lo que pueden ser invocados de los manejadores de entrada del teclado o de interfaces de usuario personalizadas. Los controles por defecto en tiempo de ejecución que pueden ser mostradas opcionalmente también invocan estas mismas funciones. También hay botones en el inspector NetworkManagerHUD disponibles cuando se está en modo de reproducción que llama las mismas funciones:

Cualquiera que sea el método utilizado para cambiar el estado del juego, las propiedades networkAddress y networkPort son utilizadas. Cuando un servidor o host (anfitrión) ha iniciado, el networkPort se vuelve el puerto de escucha. Cuando un cliente comienza, la networkAddress es la dirección para conectarse, y el networkPort es el puerto para conectarse.

Manejo de generaciones (Spawning)

El NetworkManager puede ser utilizado para manejar la generación de objetos en red prefabs. La mayoría de juegos tienen un prefab utilizado como el objeto principal del jugador, entonces el NetworkManager tiene una ranura para arrastrar el prefab del jugador. Cuando un prefab del jugador haya sido configurado, un objeto jugador automáticamente será generado de ese prefab para cada usuario en el juego. Esto aplica al jugador local en el servidor anfitrión, y los jugadores remotos en clientes remotos. Tenga en cuenta que el prefab del jugador debe tener un componente NetworkIdentity.

Adicionalmente al prefab del jugador, los prefabs de otros objetos que serán generados dinámicamente deben ser registrados con la ClientScene (Escena del cliente). Esto se puede realizar con las funciones ClientScene.RegisterPrefab(), o puede ser hecho por el NetworkManager automáticamente. Agregar prefabs a la lista de generaciones hará que sean auto-registradas. La sección de configuración de generación (spawn) del inspector NetworkManager se ve así:

Una vez un prefab de un jugador esté configurado, usted debería poder comenzar el juego como anfitrión y ver el objeto del jugador generado. Parar el juego debería hacer que el objeto jugador sea destruido. Correr otra copia del juego y conectándose como un cliente a localhost debería hacer que otro objeto jugador aparezca, y parar ese cliente debería hacer que el objeto jugador del cliente sea destruido.

El objeto jugador es generado por la implementación por defecto del NetworkManager.OnServerAddPlayer. Si usted quiere personalizar la manera de que los objetos jugadores son creados, usted puede anular la función virtual. La implementación por defecto es algo así:

public virtual void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
{
    var player = (GameObject)GameObject.Instantiate(playerPrefab, playerSpawnPos, Quaternion.identity);
    NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
}

Tenga en cuenta que la función NetworkServer.AddPlayerForConnection() debe ser llamada para el nuevo objeto jugador creado para que sea generado y asociado con la conexión del cliente. Esto va a generar el objeto, entonces NetworkServer.Spawn no necesita ser llamado para el objeto jugador.

Start Positions (Posiciones de Inicio)

Para controlar dónde los jugadores son generados (spawned), usted puede utilizar el componente NetworkStartPosition. El NetworkStartPosition busca objetos NetworkStartPosition en la escena, y si encuentra alguno, entonces va a generar el jugador en la posición y orientación de uno de ellos. Un código personalizado puede acceder el NetworkStartPositions disponible por la lista NetworkManager.startPositions, y también hay una función de ayuda GetStartPosition() en el NetworkManager (Administrador de red) que puede ser utilizado en la implementación de OnServerAddPlayer para encontrar una posición inicial.

Para utilizar posiciones iniciales, adjunte un componente NetworkStartPosition a un objeto en la escena de juego. Pueden haber varias posiciones dentro de una escena. El NetworkManager luego registrará la posición y orientación del objeto como una posición inicial. Cuando un cliente une el juego y un jugador es agregado, el objeto jugador será creado en una de las posiciones iniciales, con la misma posición y orientación.

El NetworkManager tiene una propiedad PlayerSpawnMethod que permite una configuración acerca de cómo las startPositions son escogidas.

  • Escoja Random para generar jugadores en opciones startPosition aleatorias.
  • Escoja Round Robin para recorrer las opciones startPosition en una lista.

El código para el área de generación se ve así:

if (m_PlayerSpawnMethod == PlayerSpawnMethod.Random && s_StartPositions.Count > 0)
{
    // try to spawn at a random start location
    int index = Random.Range(0, s_StartPositions.Count);
    return s_StartPositions[index];
}
if (m_PlayerSpawnMethod == PlayerSpawnMethod.RoundRobin && s_StartPositions.Count > 0)
{
    if (s_StartPositionIndex >= s_StartPositions.Count)
    {
        s_StartPositionIndex = 0;
    }
​
    Transform startPos = s_StartPositions[s_StartPositionIndex];
    s_StartPositionIndex += 1;
    return startPos;
}

Manejo de escena

La mayoría de los juegos tienen más de una escena. Al menos siempre hay una pantalla de titulo o una escena de menú al inicio adicionalmente a la escena dónde el juego en realidad es jugado. El NetworkManager está configurado para automáticamente manejar el estado de la escena y las transiciones de la escena en una manera que funcione para un juego multi-jugador. Hay dos ranuras en el inspector NetworkManager, la offlineScene (escena no en linea) y la onlineScene (escena en linea). Arrastrar los objetos de la escena a estas ranuras activan el manejo de la escena en red.

Cuando un servidor o anfitrión es iniciado, la escena en linea será cargada. Esto luego se volverá la escena de red actual. Cualquier cliente que se conecte al servidor será instruido para también cargar la escena. El nombre de está escena es almacenado en la propiedad networkSceneName.

Cuando la red para, al parar el servidor o anfitrión, o por un cliente desconectándose, la escena fuera de linea será cargada. Esto permite que el juego vuelva automáticamente a la escena del menú cuando se desconectada de un juego multi-jugador.

Usted también puede cambiar escenas mientras el juego está activado al llamar NetworkManager.ServerChangeScene(). Esto hará que todos los clientes conectados actuales cambie de escena también, y va actualizar el networkSceneName (nombre de la escena en red) para que nuevos clientes también carguen la escena nueva.

Mientras el manejo de escena en red es activa, cualquier llamado a las funciones del manejo de estados tal como NetworkManager.StartHost() o NetworkManager.StopClient() puede causar cambios en la escena. Esto aplica al control UI en tiempo de ejecución. Por lo que al ajustar las escenas y llamar estas funciones, es fácil controlar el flujo de un juego multi-jugador.

Tenga en cuenta que los cambios de escena causan que los objetos en la escena sean destruidos. Esto puede incluir el NetworkManager! Si usted quiere que NetworkManager persista entre escena, entonces asegure que la casilla de verificación “Dont Destroy On Load” sea configurada a true. En los casos más simples, esta es la mejor configuración. Pero, es posible tener un NetworkManager en cada escena con diferentes ajustes para controlar la carga incremental de prefab, o diferentes transiciones de escena.

Información De Depuración

El panel del inspector NetworkManagerHUD muestra información adicional acerca el estado de la red en tiempo de ejecución. Esto incluye:

  • conexiones en red
  • objetos del servidor NetworkIdentity activos
  • objetos cliente NetworkIdentity activos
  • compañeros clientes

También, los manejadores de mensajes de clientes registrados son mostrados en la ventana de pre-visualización.

Emparejamiento de partidas

El UI en tiempo de ejecución del NetworkManager y el inspector UI del NetworkManager permite por interacciones con el servicio de emparejamiento de partidas, y emerge la propiedad NetworkManager.matchmaker con un objeto NetworkMatch. Una vez esto esté activo, los UIs predeterminados lo utilizan y le hace callback a las funciones en el NetworkManager para permitirle realizar un emparejamiento de partidas simple.

Hay funciones virtuales en el NetworkManager que las clases derivadas pueden utilizar para personalizar comportamiento de callbacks de emparejamiento de partidas.

Personalización

Hay funciones virtuales en el NetworkManager que las clases derivadas puede utilizar para personalizar comportamiento. Cuando implemente estas funciones, asegúrese de cuidad la funcionalidad que las implementaciones por defecto proporcionan. Por ejemplo, en OnServerAddPlayer(), la función NetworkServer.AddPlayer debe ser llamada para activar el objeto jugador para esa conexión.

Funciones invocadas en el Servidor/Anfitrión:

// called when a client connects 
public virtual void OnServerConnect(NetworkConnection conn);

// called when a client disconnects
public virtual void OnServerDisconnect(NetworkConnection conn)
{
    NetworkServer.DestroyPlayersForConnection(conn);
}

// called when a client is ready
public virtual void OnServerReady(NetworkConnection conn)
{
    NetworkServer.SetClientReady(conn);
}

// called when a new player is added for a client
public virtual void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
{
    var player = (GameObject)GameObject.Instantiate(playerPrefab, playerSpawnPos, Quaternion.identity);
    NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
}

// called when a player is removed for a client
public virtual void OnServerRemovePlayer(NetworkConnection conn, short playerControllerId)
{
    PlayerController player;
    if (conn.GetPlayer(playerControllerId, out player))
    {
        if (player.NetworkIdentity != null && player.NetworkIdentity.gameObject != null)
            NetworkServer.Destroy(player.NetworkIdentity.gameObject);
    }
}

// called when a network error occurs
public virtual void OnServerError(NetworkConnection conn, int errorCode);

Funciones invocadas en el Cliente:

// called when connected to a server
public virtual void OnClientConnect(NetworkConnection conn)
{
    ClientScene.Ready(conn);
    ClientScene.AddPlayer(0);
}

// called when disconnected from a server
public virtual void OnClientDisconnect(NetworkConnection conn)
{
    StopClient();
}

// called when a network error occurs
public virtual void OnClientError(NetworkConnection conn, int errorCode);

// called when told to be not-ready by a server
public virtual void OnClientNotReady(NetworkConnection conn);

Funciones invocadas para el Emparejamiento de partidas:

// called when a match is created
public virtual void OnMatchCreate(CreateMatchResponse matchInfo)

// called when a list of matches is received
public virtual void OnMatchList(ListMatchResponse matchList)

// called when a match is joined
public void OnMatchJoined(JoinMatchResponse matchInfo)
Configurando un proyecto Multijugador desde el principio
Generación (spawning) de objetos