Archivo de la etiqueta: cliet-server

Introducción al desarrollo de juegos multiplayer en Unreal Engine 4

English version: Introduction to the development of multiplayer games using Unreal Engine 4

Varias personas me han escrito con muchas dudas sobre el desarrollo de juegos multiplayer en Unreal Engine y me ha parecido muy buena idea comenzar una serie de tutoriales que ayuden ha entender bien a fondo como desarrollar juegos multiplayer con este magnifico motor. Como no hay mejor forma para aprender que practicando, pues eso es lo que vamos a hacer. Vamos a comenzar a desarrollar un simple “Third Person Multiplayer Shooter” en Unreal Engine. Esto nos va a permitir conocer muchas cosas nuevas que aún no hemos visto en tutoriales anteriores, y el plato fuerte, todo lo relacionado con el desarrollo multiplayer.

Como este asunto no es nada simple, ni tan siquiera en Unreal que tan fácil nos hace las cosas :), vamos a ir poco a poco, por lo que vamos a dejar este primer tutorial solo para una introducción en su mayoría teórica de todo este asunto del multiplayer.

NOTA: Este tutorial ha sido desarrollado con Unreal Engine 4.8, si estás trabajando con otra versión puede que encuentres algunas diferencias, ya que el Engine está en constante actualización. De ser así, déjame tus comentarios al final del post y buscamos juntos la solución.

Introducción

Un juego multiplayer o multijugador es un juego que es jugado por varios jugadores a la vez. En este punto tenemos dos variantes principales. La primera, donde los jugadores comparten una misma estación, cada uno con un control, y la pantalla se divide en partes. El hablar de este modo de juego multiplayer siempre me recuerda cuando jugaba de chico el GoldenEye 007 en un Nintendo 64 con todos los chicos del vecindario :)

Captura de pantalla del GoldenEye 007 donde se ve la pantalla dividida en dos partes, cada parte es el espacio de juego de cada jugador. En este caso hay dos jugadores jugando la partida con dos controles distintos. Imagen tomada de: http://www.meristation.com/nintendo-64/goldeneye-007/darwin-image/1507537/1155968

Captura de pantalla del GoldenEye 007 donde se ve la pantalla dividida en dos partes, cada parte es el espacio de juego de cada jugador. En este caso hay dos jugadores jugando la partida con dos controles distintos. Imagen tomada de:
http://www.meristation.com/nintendo-64/goldeneye-007/darwin-image/1507537/1155968

Otro ejemplo de juegos multiplayers donde los jugadores pueden jugar en la misma estación son los juegos de pelea, solo que en estos casos la pantalla no es dividida ya que ambos jugadores siempre están en la misma zona del nivel. Al igual que la modalidad anterior, cada jugador cuenta con un control para controlar a su personaje. Sin duda, como mejor ejemplo de este estilo de juego tenemos que citar al recién Mortal Kombat X, que dicho sea de paso, es uno de los grandes títulos desarrollados con Unreal Engine.

Captura de pantalla del Mortal Kombat X (Versión para móvil)

Captura de pantalla del Mortal Kombat X (Versión para móvil)

Las dos variantes mencionadas anteriormente se puede decir que son el principio de los juegos multijugador. Con el desarrollo de las redes de computadoras y el aumento en la velocidad de transmisión de datos por esta vía, surgió otro modo de juego multijugador, donde varios jugadores pueden jugar un mismo nivel, pero en este caso, en distintas estaciones, permitiendo a dos jugadores, incluso estando a kilómetros de distancia, compartir una partida.

… y bueno, ejemplos y las ventajas de esto no creo que sean necesarias mencionarlas, no !!?? :)

Bueno, en estos tutoriales nos centraremos en este último tipo de juego multijugador y vamos a comenzar comentando un poco la arquitectura de red más usada para soportarlos, hablamos de la arquitectura clientes/servidor.

Arquitectura Cliente/Servidor

En la arquitectura cliente/servidor cada uno de los jugadores son los clientes, y están conectados a una máquina centrar que será el servidor. El servidor es el responsable de todas las decisiones importantes del juego, manteniendo el estado del juego y transmitiendo esta información a cada uno de los clientes para que todos los jugadores estén sincronizados.

Diagrama de la arquitectura Cliente/Servidor. Imagen tomada del excelente video-tutorial “Blueprint Networking Tutorials (https://www.unrealengine.com/blog/blueprint-networking-tutorials)” de Billy Bramer (Lead GamePlay Programmer en Epic Game)

Diagrama de la arquitectura Cliente/Servidor. Imagen tomada del excelente video-tutorial “Blueprint Networking Tutorials (https://www.unrealengine.com/blog/blueprint-networking-tutorials)” de Billy Bramer (Lead GamePlay Programmer en Epic Game)

En esta arquitectura el servidor puede correr en dos modos distintos:

Listen server: En este modo el servidor también funciona como cliente. O sea, en el mismo ordenador que está haciendo función de servidor puede haber un jugador.

Dedicated Server: En este modo el servidor hace exclusivamente función de servidor, es en este modo en el que generalmente corre los servidores en producción.

Es importante destacar que en el modo Listen Server ese cliente no tiene ninguna exclusividad por estar jugando en el servidor, todas las reglas de la arquitectura se aplican de la misma forma ya que el cliente y el servidor son totalmente independientes aunque estén compartiendo el mismo hardware.

Representación gráfica del Listen Server y el Dedicated Server. Imagen tomada del excelente video-tutorial “Blueprint Networking Tutorials (https://www.unrealengine.com/blog/blueprint-networking-tutorials)” de Billy Bramer (Lead GamePlay Programmer en Epic Game)

Representación gráfica del Listen Server y el Dedicated Server. Imagen tomada del excelente video-tutorial “Blueprint Networking Tutorials (https://www.unrealengine.com/blog/blueprint-networking-tutorials)” de Billy Bramer (Lead GamePlay Programmer en Epic Game)

Unreal Engine 4 usa esta arquitectura Cliente/Servidor para soportar juegos multiplayers. Esto es un punto súper importante a tener siempre claro, el control total del juego lo llevará el servidor, y este será el encargado de actualizar el estado en todos los clientes . . . no te preocupes que entenderás mejor a que nos referimos con esto más adelante cuando comencemos con la práctica, pero de momento, ve grabándote esto “a fuego” en la cabeza, porque es la regla de oro.

Vamos a poner un ejemplo simple que ayude a aclarar el asunto.

Supongamos que estamos desarrollando un juego donde participan cuatro jugadores. Cada jugador tendrá su propia estación, o sea cada jugador será un cliente. Los jugadores puedes equipar armas que están regadas por el escenario para liquidar a los otros jugadores. El jugador “sobreviviente“ es el ganador.

Al comenzar a implementar este juego tenemos un primer problema. Cuando comienza el juego los cuatro jugadores salen en una posición determinada en el nivel, pero supongamos que el jugador 1 comienza a moverse. Lo que tendría que pasar es que en las otras 3 estaciones, el personaje del jugador 1 también se mueva, verdad?? Pues bien, para esto es que tenemos al servidor.

El jugador 1 en realidad lo que hará es “decirle“ al servidor que se está moviendo en x dirección. Esto lo hará básicamente enviando al servidor su vector de posición y rotación. El servidor se encargará de actualizar este valor en todos los clientes, incluyendo la validación del movimiento del jugador 1, que fue el que realizó la acción de caminar, y de esta forma en el momento en el que el Jugador 1 se mueve, en todas las estaciones, el personaje del jugador 1 también se mueve y así todos los clientes podrán ver lo mismo.

Esto pasa en cualquier acción en donde intervenga el estado de algún actor del juego, por ejemplo, si un jugador dispara, también se tiene que notificar al servidor de esto para que el servidor pueda actualizar en todos los clientes el momento de la acción, la posición del proyectil, etc. En fin, que la tarea de los clientes es notificar al servidor, y es el servidor el que se encarga de mantener actualizados a todos los clientes. Este concepto es muy importante que lo tengas siempre en cuenta y ya te digo, no te preocupes que lo veremos en la práctica.

Por supuesto, en todo este proceso intervienen varios temas bien conflictivos. Uno de ellos, por ejemplo, hablando del desplazamiento de los personajes, es evitar los saltos. Si en algún momento en el que el jugador 1 se está moviendo comienza a congestionarse la red, habrán algunos paquetes que no lleguen al servidor, y esto implicará que en los clientes, en vez de verse ese personaje desplazándose fluidamente de un punto a otro, se verá a saltos. Si has jugado juegos multiplayers en otras ocasiones, sobre todo en redes sobrecargadas y lentas, lo habrás notado de seguro.

En esos casos, hay que complicar un poco más la solución para evitar estos problemas. Generalmente se usan soluciones de interpolación de movimiento, para “predecir“ la posición del personaje con respecto al tiempo, y evitar estos saltos. Esto es fácil decirlo, pero la implementación no es nada trivial. . . por suerte estamos desarrollando con Unreal Engine :).

Lo mejor que tenemos al usar UE4 en el desarrollo de nuestros juegos, es que muchas veces no nos tenemos que preocupar por estos problemas “a bajo nivel“, verás que fácil es lograr esto con este Engine. La verdad esta ha sido una de las cosas que a mi en lo personal más me sorprendió cuando comencé a trabajar con Unreal, sobre todo porque tenía la experiencia de un proyecto multiplayer que desarrollé en Unity3D y SmartFoxServer . . . y en el que casi me vuelvo loco por tener que encargarme de todos estos detalles :(

Probando el modo multijugador desde el Editor

Bueno, después de haber hecho este pequeño recorrido por la teoría detrás de los juegos multiplayer con Unreal, vamos a pasar a desarrollar un simple ejemplo que nos permita poner en practica todos estos conceptos.

Para nuestro proyecto de ejemplo vamos a partir de la plantilla Tercera Persona desde C++. Crea un proyecto a partir de esta plantilla y ábrelo en el Editor.

El Unreal Editor nos facilita muchísimo a la hora de desarrollar un juego multiplayer, sobre todo para probar con distintos clientes sin la necesidad de tener distintas estaciones. Hasta ahora, para correr nuestro juego, dábamos clic en el botón de Play en el Toolbar y el juego era lanzado dentro del viewport en el editor, verdad ?. En el caso de un juego single player esto es suficiente, pero en el caso de un juego multiplayer, donde quieres probar distintas instancias (clientes) esta variante no nos es factible.

Si te fijas en el botón Play del Toolbar, este botón tiene al lado una flechita para desplegar más opciones. Al dar clic aquí se nos desplegará un menú con 3 secciones y la opción de Configuración Avanzada.

Captura del editor donde se ve el menú de opciones para jugar en el Editor.

Captura del editor donde se ve el menú de opciones para jugar en el Editor.

En la primera sección tenemos los distintos Modos de Play:

Selected ViewPort: Este es el modo que hemos usado hasta ahora para probar nuestro juego desde el Editor. Al seleccionar este modo el juego es lanzado dentro del viewport en el editor.

Mobile Preview: Nos lanza en una ventana nueva un simulador de móviles con dos Joystick virtuales sobre la pantalla. Muy útil para probar desde el ordenador los controles para móviles.

New Editor Windows: Al darle Play al juego en este modo, el juego es abierto en una ventana nueva independiente a la ventana del Editor. Esta es la forma más cómoda de probar nuestro juego en varios clientes, ya que cada cliente se abre en una ventana independiente que podemos acomodar en la pantalla para visualizar fácilmente el comportamiento del juego en esos clientes.

Standalone Game: El juego será lanzado en una nueva ventana pero que corre en su propio proceso. Por este motivo si corres el juego en este modo no podrás pausarlo o pararlo desde el Editor como hacemos normalmente.

Simulate: Esta opción es sumamente útil, yo la uso mucho testeando la IA de nuestro juego. Esta opción nos permite correr el juego en una especia de espectador, no tendremos el control de nuestro personaje, básicamente no tendremos un PlayerController que nos sirva de puente entre nosotros y nuestro Pawn, pero el resto del juego correrá normalmente y podremos “volar” por el escenario inspeccionándolo todo como queramos. Cuando estas corriendo el juego en este modo tienes total acceso a las herramientas del editor, o sea que puedes modificar la escena y su contenido a medida que el juego está corriendo. Juega un poco con este modo de Play, verás lo útil que te resultará.

PD: La opción VR Preview la verdad que no tengo ni idea ahora mismo de lo que es jejeje . . . esta opción la agregaron recientemente al Editor y tendrá que ver con todas las nuevas funcionalidades de Realidad Virtual de la versión 4.8 y posteriores. No he tenido tiempo de revisarlas así que si sabes de que va y te animas a dejármelo en el comentario me vas a sacar de una duda :)

A continuación de la sección Modos de Play tenemos la sección Spawn Player At. Aquí tenemos dos opciones: Current Camera Location y Default Player Start. Estas son bastante evidentes por su nombre. La primera nos permite que al lanzar el juego el Player salga en la posición y dirección en la que tenemos la cámara en el viewport y la segunda, es la que generalmente usamos hasta ahora, hace que el Player siempre salga en el Player Start que tenemos en el nivel.

Por último tenemos la sección Multiplayer Options, que es la sección que más nos interesa en este tutorial. En esta sección tenemos dos opciones:

Number of Players, es un campo para entrar la cantidad de jugadores con la que queremos probar nuestro juego.

Pon aquí 2, por ejemplo, y dale Play para que veas lo que pasa. Si en la sección Modes, mantienes la opción Selected Viewport verás que se lanzan dos instancias del juego una dentro del mismo editor y otra en una nueva ventana. Esto hace que las pruebas en los dos clientes sean un poco complejas, por lo que lo mejor para este caso es seleccionar New Editor Windows. De esta forma, al dar Play se abrirá cada una de las estaciones en ventanas independientes, y las puedes acomodar fácilmente dentro del espacio de la pantalla.

Por último, si estás trabajando en un monitor relativamente pequeño tendrás otro problema, el tamaño de cada una de las ventanas es muy grande. Esto lo podemos solucionar dando clic en la opción Advaced Settings del menú de Play. En la ventana que se abre (Editor Preferences/Level Editor/Play) verás la sección Play in New Window y la propiedad New Window Size. Ahí podrás modificar la resolución de la ventana para que puedas acomodar dos clientes (o los que quieras) en una sola pantalla. En mi caso, por ejemplo, generalmente redacto los tutoriales en un MacBook Air de 13 pulgadas, y para probar tengo que configurar la ventana para 640 x 480 :(.

Level Editor – Play donde se muestran las opciones para definir las dimensiones de la nueva ventana donde correrá el juego.

Level Editor – Play donde se muestran las opciones para definir las dimensiones de la nueva ventana donde correrá el juego.

Bien, después de configurar 2 Players, marcar como modo de Play, New Editor Windows, vuelve a lanzar el juego. Verás que se abren dos ventanas del mismo juego. Fíjate en el título de cada ventana:

Captura de pantalla donde se ven dos instancias o dos clientes del juego en ejecución.

Captura de pantalla donde se ven dos instancias o dos clientes del juego en ejecución.

Como puedes ver, por defecto el Unreal lanza una primera instancia que es el servidor en modo Listen. O sea, que también funciona como cliente. Es la ventana que en el título dice NombreDelProyecto Preview Server, el otro es un cliente normal. Probar el juego con el servidor en modo Listen siempre es de mucha ayuda porque muchas veces cuando estamos desarrollando nos pasa que notificamos en el servidor, en el servidor se realiza la acción correctamente pero no se replica al resto de los clientes. Al correr el juego en este modo podemos ver que la instancia del cliente que está corriendo en el server si verá la acción que sucedió correctamente, pero los otros no. Dado que nos está faltando implementar que el server notifique a todos los clientes, con esto podemos saber por donde comenzar a buscar :)

Además de esto Unreal nos permite también testear nuestro juego mutiplayer en un servidor dedicado. Fíjate al desplegar las opciones de Play desde el Toolbar que debajo de la opción para definir el número de clientes tienes un check que dice Run Dedicated Server. Si lo seleccionas y das Play verás que al igual que el caso anterior, se ejecutarán dos instancias de nuestro juego, pero si te fijas en el nombre de cada ventana ambas dicen clientes, porque el servidor está corriendo también pero en modo dedicado (sin salida gráfica).

Muy bien, como verás en cada una de las instancias puedes controlar al personaje y si te mueves con una en el otro cliente se ve perfectamente ese personaje moviéndose. Al menos yo, cuando vi esto, mi expresión fue de: 😮 !! ¿Pero cómo, si no he hecho absolutamente nada ¡!!? . . . no he escrito ni una línea de código para lograr todo esto, y recuerdo que en mi proyecto Unity hace un tiempo, para lograr solamente esto, que al moverme con un personaje se actualicen correctamente todos los clientes, me tomó una semana y tuve que programar “como loco“, incluidos métodos para interpolación.

Pues sí, así mismo, y vamos a aprovechar este asombro que de seguro tienes como mismo tuve yo, para comentar un atributo prácticamente mágico que tiene Unreal en los Actores y es el atributo Replicate

Replicated Actors en Unreal Engine 4

Si vas a la sección Default de cualquier Actor, ya sea el PlayerCharacter o cualquier otro actor del juego, verás que cuenta con una sección de nombre Replication. Esta sección contiene la mayoría de las propiedades que definen el comportamiento de este actor en un juego multiplayer. Ahí verás la propiedad de tipo bool, Replicates. Si abres, por ejemplo, el blueprint del Character, verás que esta propiedad está en true (marcado el checkbox) así como la propiedad Replicate movement.

networking_tuto_01_image_08

Pues tan simple como esto, solamente con marcar en un actor estas dos propiedades en true el Engine se encarga de manejar todo lo necesario para cuando este actor se mueva por el escenario el servidor actualice en todos los clientes la posición y rotación de ese Actor 😮 !!!

Vamos a hacer una pruebilla muy simple, desmarca la propiedad Replicate Movement y vuelve a correr el juego con dos clientes. Verás que en este caso siguen saliendo los dos Players pero cuando te mueves con uno, su personaje en la otra instancia no se mueve. Por tanto, en todo Actor donde queramos que se replique la posición del mismo a travez de la red (aplicando mecanismos de interpolación etc) tenemos que dejarle en true esta propiedad.

La propiedad Replicates básicamente debe estar en true para todos los actores que tengan relevancia a través de la red. Lo veremos más adelante con las atributos que queramos que se repliquen.

Muy bien, pues sin escribir una línea de código (ni crear un nodo en el Blueprint) tenemos todo el mecanismo de replicación del Player Character entre todos los clientes del juego. Perfecto !! es un genial punto de partida para nuestro juego multijugador.

Conclusión

Vamos a dejar esta primera parte introductoria aquí para comenzar en el siguiente tutorial con la práctica. Como les comentaba al inicio, la idea es desarrollar un juego simple tercera persona y multiplayer, donde podrán jugar varios jugadores y el ganador será el que sobreviva. Además de los conceptos y las técnicas relacionadas con el multiplayer que será el plato fuerte de esta serie, también el ejemplo nos permitirá ver como implementar muchísimas cosas nuevas que aún no hemos visto en los tutoriales anteriores, así que no te vayas muy lejos que pronto estamos por aquí con la segunda parte . . . hasta entonces, bye 😉

English version: Introduction to the development of multiplayer games using Unreal Engine 4