Historia de participación en el Game Jam. Caja de nieve

imagen A finales de 2017, tuve la oportunidad de probar mi fortaleza y entusiasmo como participante en uno de los muchos Game Jams del mundo.

Como esta fue mi primera experiencia en un proyecto así, aprendí algunas lecciones útiles y un par de sorpresas agradables. Bueno, también conseguí un juguete que podía jugar con mis colegas los viernes festivos.

Debajo del gato, una descripción de cuán intensivos fueron 30 días de desarrollo y 20 días lentos de espera para obtener resultados.

Nota: el artículo es de naturaleza narrativa, con pocos detalles técnicos.

Prologo


Durante mucho tiempo he querido probarme a mí mismo como desarrollador de juegos, y en el último año este deseo se volvió cada vez más intrusivo e intrusivo. Al final del año, decidí comenzar a hacer algo: asistir a reuniones sobre desarrollo de juegos, leer libros e incluso hacer un pequeño prototipo del juego.

Y luego, un día, Selim, mi colega, se ofreció a participar en uno de los muchos Game Jam y pensé que esta es la mejor oportunidad para estudiar la cocina del juego sin ninguna obligación a largo plazo. Este no es un prototipo, pero aún no es su proyecto de larga duración. Además, los plazos ajustados son a menudo algunos de los mejores motivadores. Y la principal ventaja es que, en cualquier caso, el resultado es algo holístico y completo, que siempre inspira nuevos logros.

Entonces decidimos participar. Dos desarrolladores de Java, sin absolutamente ninguna experiencia en la creación de juegos. ¿Pero cuándo hay que empezar?

Preparación


El Game Jam elegido fue temático, y los organizadores tuvieron que anunciar el tema estrictamente antes del inicio del desarrollo. Los concursantes recibieron 1 mes para crear el juego.

Nos registramos una semana antes del comienzo y, debido a la falta de un tema, pasamos un tiempo lánguido de anticipación e inacción. Quizás fue mejor pasar este tiempo eligiendo un género y la mecánica del juego. Y luego envuélvelo en un estilo adecuado.

En la hora X, los organizadores anunciaron el tema del concurso: Retroceso (regreso) y comenzó la cuenta regresiva.

Selim tenía el único deseo para el juego: la presencia de un servidor de juegos y necesariamente en tiempo real. Estaba interesado en conocer las delicias y las dificultades de desarrollar el lado del servidor. Además, el juego debería haber sido simple, de lo contrario podríamos no tener tiempo para completarlo.

Con respecto al tema, los pensamientos al principio eran sobre lo retro o sobre los tiempos prehistóricos. El tema retro no me interesa, así que traté de encontrar algo sobre dinosaurios o personas antiguas. Sin embargo, no pude encontrar algo simple y multiusuario sobre este tema.

Y luego se me ocurrió la idea de hacer un juego de sandbox, en sentido literal, una referencia al entretenimiento infantil en la playa. Construimos castillos, corremos y arrojamos arena a nuestros rivales (espero que no todos hayan tenido una infancia tan difícil), pero los padres insatisfechos son castigados por esto. Todo esto en 2D con una vista superior.

Selim consideró que la arena es demasiado cruel para los ojos y sugirió reemplazar la arena con nieve y los castillos con fortalezas de hielo. En esta opción, nos detuvimos. A lo mejor de nuestra capacidad y tiempo, podríamos agregar nueva funcionalidad y variedad.

Desarrollo. Semana 1


Nuestros roles en el proyecto se dividieron por sí mismos. Como dije, Selim solo estaba interesado en el desarrollo del servidor. Y estaba interesado en probar el desarrollo de la interfaz de usuario, porque, en mi opinión, es muy diferente del desarrollo de aplicaciones empresariales. La interfaz del juego fue planeada web.

Breve descripción de las tecnologías utilizadas
La interacción cliente-servidor se creó por completo en los sockets web, ya que necesitábamos enviar mensajes desde el servidor al cliente de forma asincrónica y regular. Los mensajes del cliente también se transmitieron a través del socket web, pero simplemente por la razón "por qué no". Todos los mensajes estaban en formato Json.

Para trabajar con sockets web en el servidor, se utilizó el micro-marco Spark. Y como motor físico se utilizó box2d, como uno de los más populares. Fue responsable de calcular la física en el servidor.

El cliente fue escrito en JS puro, sin usar ningún framework, ya que no soy fuerte en ellos, y además, esto tiene poco sentido para un proyecto pequeño. Como motor de juego, se utilizó Phaser 2, un motor JS joven y prometedor. A diferencia del motor físico en el servidor, su propósito principal era gráficos y cálculos físicos simples. KnockoutJS también se usó para el enlace de datos .

Los productos JetBrains se utilizaron como IDE: Intellij IDEA y una versión de prueba de WebStorm (solo una versión de 30 días, en el momento del Game Jam).

El desarrollo comenzó con una reunión conjunta en el día libre. En las primeras 2 horas, recogí sprites "temporales" y me di cuenta de un niño corriendo que sabía cómo lanzar bolas de nieve. Podría haber una imagen de "fue / se ha convertido", pero carece de significado: visualmente lo que fue, luego permanece.

Al tener una IU que funcionaba, solo quedaba por estrechar la interacción del servidor. Por lo tanto, el resto del día se dedicó a la integración: discusión de la API, configuración de la conexión, implementación de la interacción. Al final del día, nuestro pequeño hombre podría moverse 1 píxel hacia un lado, presionando una tecla. Fue modesto, pero aún exitoso.

El desarrollo adicional continuó solo en el tiempo libre del trabajo, con 6 días a la semana por separado para hogares y un día libre juntos. Esto fue suficiente gracias a una API simple y una distribución rígida de roles.

Para repetir el éxito del primer día (correr y lanzar bolas de nieve), pero ya a través del servidor, nos llevó una semana. Esto se vio igualmente afectado por muchos factores técnicos, incluidos problemas con herramientas mal diseñadas para nuestro modelo de interacción cliente-servidor. Me gustaría detenerme en este modelo con más detalle.

Modelo de interacción cliente-servidor


Inicialmente, se decidió que el servidor sería responsable de cualquier movimiento, y el cliente solo enviaría comandos, como presionar una tecla, y dibujaría lo que provenía del servidor. Más tarde, esta filosofía se modificó ligeramente, pero el servidor hasta el final siguió siendo el enlace central.

En la primera implementación, el servidor envió actualizaciones al cliente cada vez. Es decir por ejemplo, si se mantiene presionada una tecla, la posición del jugador cambia cada pocos milisegundos y se envía una nueva posición al cliente.

Usando un algoritmo tan simple, implementamos el movimiento del jugador. Pero luchar por lo mejor nos impulsó a cambiar el algoritmo a un evento uno: solo se envían eventos importantes suficientes para la sincronización al servidor, así como al cliente.

Por ejemplo, para mover a un jugador hacia arriba en el mapa necesitas 4 eventos:

  1. Cliente : tecla arriba presionada;
  2. Servidor : el jugador comenzó a moverse desde el punto [x, y1 ], en un ángulo α, con velocidad ν;
  3. Cliente : tecla arriba presionada;
  4. Servidor : el jugador ha terminado de moverse en el punto [x, y2 ].

Este enfoque tiene ventajas y desventajas.

Contras

  • mucha más lógica en el cliente: el propio cliente debe calcular el movimiento, las colisiones, etc.
  • la lógica en el cliente debe repetir con mucha precisión la lógica en el servidor, de lo contrario se obtienen saltos en el movimiento de los objetos. Además en el artículo habrá varios ejemplos sobre este tema;
  • en caso de problemas de red, recibir eventos más tarde puede conducir a estados incorrectos (salir de límites, pasar a través de objetos, etc.);
  • en general, es mucho más fácil desincronizar el estado en el servidor y el cliente.

Pros

  • mucha menos carga en E / S;
  • Se puede colgar lógica adicional en los eventos. Por ejemplo, al comienzo / final de un movimiento, puede iniciar / detener la animación;
  • Los problemas de red afectan menos la suavidad del movimiento. Mientras la bola de nieve está volando, un retraso de incluso un segundo no afectará nada, ya que el servidor no debe enviar nada;
  • La API y la interacción en sí se vuelven más transparentes.

De estas listas está claro que si trabajas duro en la implementación del cliente, entonces hay casi una ventaja. Para nuestro proyecto, logramos esto y la interacción cliente-servidor funcionó a la perfección. Pero debemos rendir homenaje, tomó mucha fuerza y ​​nervios.

Desarrollo. Semanas 2 y 3


Una semana después, cuando logramos dominar la integración como mínimo y brindar soporte para varios tipos de mensajes entre el servidor y el cliente, llegó el momento de agregar más al juego que solo un chico corriendo.

Por lo tanto, decidimos agregar una niña corriendo! En el camino, para la niña tuve que hacer una ventana de selección de personaje. Y dado que la ventana todavía tenía que hacerse, es un pecado no agregar un nombre también. Además, tuve que estirar las imágenes, de manera similar al enfoque de 9 parches .

imagen
Parecía la primera versión de la ventana de inicio de sesión

Debido a todo esto, una tarea tan simple e importante de agregar una niña tomó un par de noches, pero claramente valió la pena. Y luego vino la vida cotidiana gris con las mismas tareas: nuevas funciones simples, correcciones de errores, mejoras visuales menores.

A medida que mi progreso progresaba en el lado del cliente, surgió el problema de la velocidad desigual de desarrollo del servidor y la interfaz de usuario. Y dado que el servidor es una parte central de cualquier interacción, sin la funcionalidad final y operativa en el servidor, el desarrollo del cliente se ha detenido.

Por lo tanto, implementé un servidor simulado simple directamente en el cliente. Muchas cosas en él se implementaron muy torpemente, incluyendo a través del uso del estado global del mundo del juego. Esto fue suficiente para desarrollar una interfaz de usuario completamente independiente del servidor y me ahorró mucho tiempo.

El servidor falso tenía otra ventaja definitiva. Había bots que no sabían cómo disparar, por lo que al menos había una posibilidad de que alguien ganara.

Al mismo tiempo, Selim luchó con el servidor. En el servidor, conectó el motor de física box2d para simular la física en el juego. Este motor tiene muchos de sus matices, y su estudio llevó mucho tiempo. La mayor dificultad en el desarrollo fue la falta de visualización del mundo del juego. Nuestro cliente solo prestó lo que se necesitaba. Y algunos elementos del "mundo del servidor" estaban ocultos del lado del cliente. Además, no había garantías completas de que el cliente representara todo correctamente.

Una de las subtareas importantes que Selim tuvo que resolver fue verificar la colisión de objetos. En el cliente, las colisiones se verificaron solo para elementos fijos. En el servidor, era necesario hacer todo honestamente para no infringir los derechos de los objetos en movimiento.

Durante el desarrollo de las colisiones, recordé un error divertido que podría pretender una funcionalidad especial: cuando un jugador arrojó una bola de nieve, se la arrojó con un "retroceso". Esto sucedió porque en box2d, por defecto, todos pueden colisionar con todos, y siempre ocurre repulsión.

Este problema se resolvió introduciendo máscaras, es decir, especificando clases de objetos que no pueden colisionar entre sí. Por ejemplo, para un jugador y sus bolas de nieve, la máscara se hizo igual.

Selim decidió no perder el tiempo en el manejo especial de colisiones de bolas de nieve entre sí y comentó sobre tal colisión:

// for the unlikely event that we collide with a sibling snowball

Como ha demostrado la práctica, la frecuencia de este evento "improbable" tiende a la frecuencia de lanzar bolas de nieve, porque cuando se encuentran uno frente al otro, las trayectorias de las bolas de nieve coinciden. Debido a esto, las bolas de nieve alienígenas constantemente superan a las suyas. Nuestras opiniones sobre este comportamiento divergieron, así que lo dejamos como está.

Mientras Selim se estaba divirtiendo con las colisiones, depuré el movimiento sincrónico de objetos en el servidor y el cliente. Hubo errores menores en nuestra propia implementación, pero Phaser presentó la mayor sorpresa. En su motor de física, la velocidad real de los objetos se ajusta a FPS y detiene el mundo. Esto se hace para aumentar la suavidad. Desafortunadamente, este comportamiento inteligente no era consistente con la operación síncrona con respecto al servidor, y tuve que hacer mi propia implementación de objetos en movimiento.

Descripción de los detalles de la velocidad en Phaser o "carrera con sombra"
Para verificar y depurar la velocidad, agregué el objeto de otro jugador al mapa, que se movía con la misma velocidad, pero cerca. Este jugador sombra y el jugador normal usaron diferentes algoritmos de movimiento. Y así organicé competiciones y comparé la estabilidad de la velocidad.

Al principio traté de resolver el problema de la velocidad desigual usando la configuración del motor y la física que usamos. Pero, según tengo entendido, este comportamiento no se puede cambiar de ninguna manera. Era posible cambiar a una implementación más compleja de la física, pero no quería hacerlo solo por la velocidad. Además, esta implementación tampoco dio una velocidad absolutamente estable.

El siguiente paso traté de implementar el movimiento de los objetos yo mismo, pero desde el motor tomé el control y el delta de tiempo entre cada ciclo del mundo. Hay varios modelos de tiempo en Phaser y la implementación de velocidad estándar se basa en uno de ellos. Pero, por alguna razón desconocida, ninguno de estos tiempos tiene estabilidad y no pueden proporcionar una velocidad constante. Este es un problema conocido que no se planea solucionar en la versión 2: github.com/photonstorm/phaser/issues/798 .

Pasé 2 días en la "competencia" del jugador y la sombra y no encontré una opción de trabajo. Entonces, al final, hice todo mi procesamiento de velocidad basado en el tiempo estándar en JS. Es muy sorprendente que una función tan importante recibiera un apoyo tan extraño en el motor y tuviera que implementar su propia bicicleta.

Con tales bromas y chistes, pasamos tranquilamente la segunda y tercera semana. Además, cada semana comenzó con la frase "bueno, para la próxima semana estamos obligados a preparar una versión jugable", cada vez que nos parecía que estábamos a punto de estar listos. La falta total de experiencia y la presencia de un tremendo optimismo es la peor manera de evaluar el marco temporal.

Una semana antes de la entrega, en la reunión de desarrolladores, ni siquiera pudimos mostrar la versión normal y tuvimos que mostrar el servidor falso.

Por supuesto, con tal velocidad de desarrollo, no había nada que soñar con una lista de funcionalidades inicialmente concebida, y aún más con tener cosas agradables. Por lo tanto, tuvimos que olvidarnos del "sandbox" y detenernos en el shooter 2D más simple.

Desarrollo. Semana 4, última


En la última semana de desarrollo, nos centramos en corregir errores. Es mejor tener algo modesto y funcional que multifuncional, pero desmoronado. Hubo muchos problemas, y la mayoría de ellos se referían a la desafortunada integración. Aquí y allá, aparecieron fallas menores, empeorando en gran medida la impresión del juego.

Además de corregir errores, también traje el brillo final a la interfaz de usuario: agregando música y sonidos, jugando con fuentes y mejorando pequeños detalles.

De toda la funcionalidad, esta semana solo se agregó el sistema de puntos, así como la limitación del stock de bolas de nieve y su restauración.

El último día de Game Jam pudimos jugar un poco con colegas. A pesar de las críticas positivas generales, esta sesión de juego no tuvo éxito debido a un error crítico: las bolas de nieve volaron en el servidor y en el cliente a diferentes velocidades. Debido a esto, entrar en otros jugadores era más probable que fuera un accidente.

Descripción de la causa y corrección del error.
Hicimos este error en el último momento, reduciendo el número de FPS en el servidor de 1000 a 100 experimentales.

Cabe señalar que hasta este punto, no pudimos lograr un movimiento totalmente sincrónico en el cliente y el servidor, ocasionalmente hubo saltos en el movimiento. Al cambiar FPS, intentamos aumentar la capacidad de respuesta del servidor.

Cuando comencé a investigar este error, descubrí 2 patrones:

  • el movimiento del jugador funcionó de manera correcta y estable;
  • El movimiento de bola de nieve en el cliente siempre fue más rápido que en el servidor.

El valor de la velocidad de los objetos llega al cliente desde el servidor, es decir, no podría ser una discrepancia banal de valores. Además, un aumento inverso de FPS a 1000 mejoró la situación.

Pasé mucho tiempo tratando de corregir este error. Pero nada ayudó. Y, al final, se descubrió que el uso de google: box2d limita indirectamente la velocidad máxima al mover el objeto en no más de 2 píxeles en un solo paso del mundo. Es decir a 100 FPS, la velocidad máxima es de 200 píxeles / s (p / s), y a 1000 FPS - 2000 p / s. Este valor se especifica en una constante y no se puede cambiar dinámicamente. Esto explicaba completamente la razón de la desaceleración de nuestras bolas de nieve, porque su velocidad debería haber sido 700 p / s, lo que requería un FPS estable por encima de 350.

Para solucionar este problema, aumenté el FPS a 500, pero por una razón. En box2d, debe pasar a la función de paso del mundo cuánto tiempo ha pasado desde el paso anterior. Antes de mi cambio, siempre calculamos esta diferencia antes de llamar a la función. Pero ahora, conociendo la frecuencia deseada, siempre fue posible indicar un delta constante de 2 ms. Con el mundo rezagado respecto al real, los pasos tuvieron que repetirse uno tras otro, hasta que el mundo se recupere del retraso. Luego un poco de sueño y todo es nuevo.

Esta solución, como se esperaba, resolvió el problema de la velocidad de la bola de nieve. Además, el problema con el movimiento no sincronizado en el servidor y el cliente finalmente ha desaparecido. En ese momento, esta curación milagrosa fue una completa sorpresa para nosotros, pero ahora entendí la razón: a pesar del máximo de 1000 FPS, nadie canceló la operación lenta del servidor, y especialmente la recolección de basura. Es decir en algunos momentos, FPS podría caer libremente por debajo de los 350 FPS requeridos, lo que condujo a saltos arbitrarios en la velocidad.

Entonces, felices por un error cerrado y un juguete que funcionaba, 2 horas antes de la fecha límite, estábamos listos para rendirnos. Solo quedaba enviar el juego al sitio web del proyecto.

Esperaba que sacar el juego fuera sin problemas y en vano. Era necesario crear una página de proyecto, hacer una descripción, cargar capturas de pantalla y mucho más. Nos conocimos, como era de esperar, de principio a fin. Aunque más tarde los organizadores todavía aceptaron individualmente proyectos tardíos.

imagen
Captura de pantalla de la versión final del juego.

Votar


Según los términos de la competencia, inmediatamente después de la finalización del desarrollo, se abrió una votación de 20 días. Durante este período, cualquiera podía ver los proyectos terminados, de los cuales se acumularon más de 200. Solo los participantes podían votar por los proyectos. Cada juego podría clasificarse en las siguientes categorías: general, gráficos, sonido, jugabilidad, innovación y relevancia para el tema.

La etapa de votación nos ha preparado una mala sorpresa relacionada con la naturaleza multijugador de nuestro juego. Había relativamente pocas personas viendo los juegos y la posibilidad de encontrarse con el enemigo tendía a cero. Es decir la gente fue a una tarjeta vacía, lanzó un par de bolas de nieve, corrió unos metros y se fue decepcionado.

Intentamos organizar sesiones de juego a través del foro del proyecto. Además, Selim y yo entramos periódicamente en el juego, con la esperanza de entretener a un vagabundo solitario y aburrido. Todo esto no produjo casi ningún resultado.

Me interesaba ver a algunas personas probar el juego. Recuerdo especialmente el caso cuando un jugador ingresó varios personajes al mismo tiempo y construyó un pentagrama de niños. No sé qué quería decir el autor, pero aún tengo una captura de pantalla del proceso de su creación.

imagen

Otro jugador "pirateó" nuestro juego. Tenemos una verificación de longitud de nombre, pero solo en el lado del cliente. En consecuencia, eludió esta defensa y comenzó a comunicarse conmigo de esta manera, cada vez que ingresaba un nuevo personaje y su frase en nombre de un hombre.

Junto con la votación, mis colegas y yo volvimos a jugar, vengándonos por el juego fallido el día en que se completó el proyecto. Esta vez todo salió muy bien y obtuvimos una serie de ideas para nuevas funciones que, sin embargo, era demasiado tarde para agregar.

En mi opinión, la calidad del juego, especialmente con respecto a la interacción con el servidor, resultó en un muy buen nivel. Cuantas veces lo jugamos, no notamos ningún problema, ni los escuchamos de otros jugadores. Para mí personalmente, esto fue una sorpresa, dada la calidad y la cantidad de errores unos días antes de la entrega.

Los 20 días dados para la evaluación de los juegos duraron mucho tiempo, pero al final terminaron y llegó el tan esperado tiempo de resultados.

imagen

Por lo tanto, obtuvimos el puesto 36 de un poco más de 200 participantes. Lo cual, por un lado, no es malo para el primer proyecto, pero por otro lado, es algo desagradable para el orgullo. Sobre todo teniendo en cuenta que los 10 mejores obtuvieron buenos juegos, pero no todos merecían una atención especial.

Lecciones aprendidas


En aras de obtener lecciones en condiciones de semi-invernadero, todo esto comenzó. Tratamos de inventar muchas cosas nosotros mismos y nos limitamos a un conjunto mínimo de herramientas para sentir los problemas y experimentar malos enfoques en nuestra propia piel. Pero ahora teniendo conocimiento sobre cómo hacerlo y por qué, estudiar la teoría será más fácil.

La necesidad de un artista . Como ha demostrado la práctica (no solo la nuestra), puedes hacer un buen juego sin buenos gráficos. Sin embargo, seleccionar las imágenes, las fuentes y los elementos de interfaz de usuario correctos ocupa la mayor parte del tiempo. Y lo peor es que al final no coinciden. A partir de esto, se pierde la atmósfera del tubo cálido y el juego no parece holístico.

Las pruebas de juego son muy importantes . Debido a problemas con la velocidad de desarrollo, no pudimos probar la jugabilidad de nuestra implementación la mayor parte del tiempo. Cuando pudimos jugar normalmente, había muy poco tiempo para arreglar las áreas problemáticas. Y tuvimos mucha suerte de que no hubiera tantos problemas.
En mi opinión, para los juegos, tales pruebas en usuarios reales son mucho más importantes que para las aplicaciones comerciales, porque además de la conveniencia y la resolución de los problemas del usuario, todavía es necesario mantener un cierto nivel de emociones y participación.

No todas las bibliotecas son igualmente útiles . Casi nadie desarrolla juegos en su propio motor y hay motores en el mercado para todas las ocasiones. En el patio fue 2017, y uno esperaría su alta calidad. Elegí Phaser como uno de los motores JS más recomendados y más jóvenes. Después de todos los problemas que tuvimos con él, me temo imaginar cómo se ven peor los motores. No, en general, las impresiones de trabajar con Phaser son bastante positivas, especialmente teniendo en cuenta la buena documentación y ejemplos. Pero trabajar con él sin conocer una gran cantidad de matices es muy difícil. En la primavera sale una nueva versión y espero una mejora significativa. Y también en mis planes hay un mini proyecto en otro motor para comparar.
Y recordaré ese problema con la velocidad máxima en box2d durante mucho tiempo.

En el proceso de Game Jam es bastante posible aprender . Al comenzar este proyecto, no sabíamos casi nada sobre el desarrollo de juegos, ni sobre las bibliotecas que utilizamos. Y la mayor parte del tiempo la pasé estudiando estas cosas. A pesar de esto, aún logramos llevar el juego a un estado completo.

No se requiere mucha funcionalidad . Estoy un poco sorprendido de que a muchas personas les haya gustado nuestro juego. Sí, no se sientan allí todas las noches, sino que disfrutan de una o dos sesiones pequeñas. Pero en nuestro juego no hay ni una idea original, ni una gran cantidad de funcionalidad, ni ninguna historia. Lo mismo puede decirse de la mayoría de los juegos en Game Jam que han recibido calificaciones positivas.

(Juego) Jam es una gran razón para intentarlo . No importa qué: una idea, una nueva biblioteca, tus propias fortalezas. Cuando hay una meta clara y otros deben ver su resultado, es muy motivador no debilitarse y dar lo mejor. E incluso si el resultado es peor de lo esperado, ¡no será una pena tirarlo, aprender lecciones por ti mismo y seguir adelante!

Enlaces a recursos:



¡Gracias a todos por su tiempo y buen humor!

Source: https://habr.com/ru/post/es409679/


All Articles