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

Utilizando el NetworkManager (Administrador de red)

El Network Manager es un componente para manejar el estado de red de un juego multijugador. En realidad es implementado completamente utilizando el API de Alto Nivel (HLAPI), entonces cualquier cosa que haga también está disponible para desarrolladores a través de scripting. Sin embargo, el componente Network Manager envuelve muchas funcionalidades útiles en un solo lugar y hace que la creación, ejecución y depuración de un juego multijugador sea tan simple como posible.

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

Las características del Network Manager incluyen:

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

Comenzando con el Network Manager

El Network Manager se puede utilizar como el componente nuclear de control de un juego multijugador. Para comenzar, cree un GameObject vacío en su escena de inicio (o escoja un GameObject conveniente que pueda alojar el componente Network Manager). Luego agregue el componente Network Manager del menú de items Network/NetworkManager. El nuevo componente NetworkManager agregado debería verse algo así:

El Inspector para el Network Manager en el editor le permite a usted configurar y controlar muchas cosas relacionadas al networking.

El Network Manager HUD es otro componente que funciona con el Network Manager. Éste le da a usted una interfaz de usuario simple cuando el juego está ejecutando 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 terminados todavía van a requerir una interfaz de usuario adecuada para controlar el estado de juego y para permitirle a los jugadores escoger qué tipo de juego jugar - el Network Manager Hud está intencionado para el uso de desarrollo solamente.

Administración 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.

NetworkManager has functions for entering each of these modes:

  • NetworkManager.StartClient()
  • NetworkManager.StartServer()
  • NetworkManager.StartHost()

Todos estos están disponibles para programar código, por lo que pueden ser invocados desde manejadores de input de teclado o de interfaces de usuarios personalizadas. Los controles predeterminados en runtime que pueden ser mostrados opcionalmente también invocan estas mismas funciones. También hay botones en el Inspector del Network Manager HUD, disponible en el editor en Modo de Reproducción, que llama las mismas funciones:

Cualquier función que sea utilizada para cambiar el estado de juego, las propiedades networkAddress y networkPort son utilizadas. Cuando el servidor o host ha empezado, networkPort se vuelve el puerto que escuchar. Cuando un cliente empieza , networkAddresses la dirección a la cual se conecta, y networkPort es el puerto a conectarse.

Administración de generaciones (Spawning)

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

Adicionalmente al prefab Jugador, los Prefabs de otros GameObjects que serán generados dinámicamente deben ser registrados con la client Scene (Escena del cliente). Esto se puede realizar con las funciones ClientScene.RegisterPrefab() , o se puede hacer por el Network Manager automáticamente. Agregar los Prefabs a la lista de generación (spawn) hará que sean auto-registrados. 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 GameObject jugador generado. Parar el juego debería hacer que el GameObject jugador sea destruido. Correr otra copia del juego y conectándose como cliente a localhost debería hacer que otro objeto jugador aparezca, y parar ese cliente debería hacer que el GameObject jugador del cliente sea destruido.

El GameObject jugador es generado por la implementación por defecto del NetworkManager.OnServerAddPlayer. Si usted quiere personalizar la manera de que los GameObjects jugador son creados, usted puede anular la función virtual. Este código muestra un ejemplo de la implementación por defecto:

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 Network Start Position. El Network Manager mira por GameObjects en una escena que tengan el componente Network Start Position adjunto, y si encuentra alguno, entonces genera el jugador en la posición y orientación de uno de ellos. Utilice código personalizado para acceder a los componentes Network Start Position disponibles por la listaNetworkManager.startPositions, y una función de ayuda GetStartPosition() en el Network Manager que puede ser utilizado en la implementación del OnServerAddPlayer para encontrar una posición de inicio.

Para utilizar las posiciones iniciales, adjunte un componente Network Start Position a un GameObject en la escena. Pueden haber varias posiciones iniciales dentro de una escena. El Network Manager luego registra la posición y orientación del GameObject como una posición inicial (start position). Cuando un cliente se une al juego y un jugador se agrega, el GameObject jugador es 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 Network Manager está configurado para manejar de manera automática el estado de escena y las transiciones de la escena de una manera que funcione para un juego multi-jugador. Hay dos ranuras en el inspector del Network Manager: la offlineScene (escena no en linea) y la onlineScene (escena en linea). Al arrastrar GameObjects de escena a estas ranuras activa el manejo de escena en red.

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

Cuando la red para, al parar el servidor o el 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 desconecta de un juego multi-jugador.

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

Mientras que el manejo de escena en red esté activo, cualquier llamado a las funciones del manejo de estados del juego tal como NetworkManager.StartHost() o NetworkManager.StopClient() puede causar cambios en la escena. Esto aplica al control UI en tiempo de ejecución. Al configurar 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 GameObjects en la escena previa sean destruidos. El Network Manager por lo general tiene que persistir entre escenas (de lo contrario la conexión en red se rompe en un cambio de escena), entonces asegúrese que la caja Don’t Destroy On Load esté marcada en el Inspector. También es posible tener un Network Manager en cada escena con diferentes configuraciones, lo cual puede ser útil si usted quiere controlar la carga de Prefab de manera incremental, o diferentes transiciones de escena.

Depurando información

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

  • Conexiones de red
  • Activa los objetos del servidor NetworkIdentity
  • Activa los objetos cliente NetworkIdentity
  • Compañeros clientes

Los manejadores de mensajes de clientes registrados son mostrados en la ventana de pre-visualización.

Emparejamiento de partidas (Matchmaking)

El UI en tiempo de ejecución del Network Manager y el UI del Network Manager Inspector permite interacciones con el servicio de emparejamiento. La función NetworkManager.StartMatchmaker() permite el emparejamiento de partidas, y puebla la propiedad NetworkManager.matchmaker con un objeto NetworkMatch. Una vez esto esté activo, los UIs por defecto lo utilizan y le hacen callback a funciones en el NetworkManager para permitirle a usted realizar un emparejamiento de partida simple.

Hay funciones virtuales en el NetworkManager que las clases derivadas pueden utilizar para personalizar el comportamiento de respuesta a callbacks de Matchmaker.

Personalización

Hay funciones virtuales en el NetworkManager que las clases derivadas pueden utilizar para personalizar comportamiento. Cuando implemente estas funciones, asegúrese de cuidar la funcionalidad que la implementación por defecto proporciona. Por ejemplo, en OnServerAddPlayer(), la función NetworkServer.AddPlayer se debe llamar para activar el GameObject jugador para la 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