Cómo creamos Purrfect Code: un juego de habilidad mental para desarrolladores

AGO 01, 2024

Link to Youtube Video (visible only when JS is disabled)

Purrfect Code” es un nuevo juego de habilidad mental para programación en el que se deben empujar cajas. Cuenta con tecnología de Flutter, Dart y el motor de juego Flame, y está diseñado para desafiar a los desarrolladores a resolver rompecabezas escribiendo código.


Descripción general del juego

En “Purrfect Code”, quienes juegan tienen la tarea de actualizar la programación de un robot conserje en la nueva sede de la estación espacial de Google, donde llegó por error un cargamento de gatos. El objetivo del juego es ayudar al robot a reunir de forma segura las cajas que contienen los gatos y empujarlas hasta las plataformas de teletransportación para que puedan volver a su destino. Para jugar, debes escribir en JavaScript para controlar los movimientos de los robots mientras intentas descubrir soluciones eficientes para sortear juegos de habilidad mental basados en cuadrículas.

El juego es una forma divertida de explorar la codificación utilizando tu inteligencia y creatividad. Cada nivel se centra en un concepto de programación y la complejidad aumenta paulatinamente a lo largo de los cinco niveles del juego.

El bucle de juego es el siguiente:

  1. Los jugadores escriben código JavaScript para mover el robot (hacia arriba, abajo, izquierda o derecha)

2. Luego, ejecutan el código y observan cómo el robot intenta pasar el nivel

3. El nivel se completa cuando todas las cajas que contienen gatos se empujan hasta las plataformas de teletransportación

4. Los jugadores obtienen una calificación según la eficiencia de sus soluciones, incluido el número de espacios que se movieron y la brevedad de su código

Creación del juego con Flutter, Dart y Flame

Elegimos crear “Purrfect Code” con Flutter y Dart, ya que proporcionan un marco de trabajo potente y flexible para crear apps y juegos multiplataforma. El sistema de widgets y el modelo de programación reactiva de Flutter nos permitieron crear una interfaz de usuario receptiva para una variedad de tamaños de pantalla en el navegador. Las características de programación con mucha escritura y orientadas a objetos de Dart facilitaron la estructura de nuestro código y mantuvieron nuestra base de código limpia. El motor de juego Flame, creado sobre la base de Flutter, ofreció una buena base de funciones básicas necesarias para el desarrollo del juego, lo que nos permitió centrarnos en nuestra lógica de juego y en los aspectos únicos de este. Flutter y Flame fueron una opción clara para este proyecto debido a la naturaleza de Purrfect Code como un híbrido único entre juego y app, con una IU inspirada en IDE y una vista de juego con sprites y sonido animados.


Chrome y JavaScript: aprovechamiento de la compatibilidad con el navegador incorporado

Una vez que nos decidimos por el concepto de programación para el juego, tuvimos que elegir qué lenguaje de programación usarían los jugadores. Buscábamos algo que fuera familiar y popular entre los desarrolladores. En un principio, consideramos usar Python como lenguaje de programación del juego. Sin embargo, después de un poco de deliberación, decidimos optar por JavaScript para aprovechar el soporte de JavaScript incorporado que ofrece Chrome. Con JavaScript, pudimos aprovechar las capacidades nativas del navegador sin la necesidad de cargar un intérprete de idioma adicional. Esta decisión no solo simplificó nuestro proceso de desarrollo, sino que también garantizó una experiencia perfecta para los jugadores, ya que el juego podía cargarse rápidamente con dependencias mínimas.

Con el lanzamiento de la versión 3.22 de Flutter, este año en Google I/O, se presentó un soporte estable para WebAssembly (WASM) que nos permitió optimizar las partes críticas para el rendimiento del juego. Al compilar cierta lógica de juego para WASM, nos aseguramos de que "Purrfect Code" se ejecutara de manera eficiente en el navegador, con lo que ofrecimos una experiencia de juego fluida y receptiva sin comprometer el rendimiento.

var dir = [moveEast,moveNorth,moveWest,moveSouth];
for(i=0;i<4;i++){
    for(j=0;j<5;j++)dir[i]();
}

Motor de juego Flame: desarrollo de juegos modular y eficiente

Para dar vida a "Purrfect Code", utilizamos Flame, un motor de juego modular de código abierto basado en Flutter, que ofrece muchas de las funciones que se necesitan habitualmente para el desarrollo de juegos. Flame aprovecha la sólida infraestructura de Flutter, al tiempo que simplifica el código necesario para crear nuestro proyecto. Ofrece una implementación de bucle de juego simple pero efectiva y una amplia gama de funcionalidades esenciales, como reproducción de audio, administración de sprites, funciones de animación, colisión y el sistema de componentes Flame (FCS). La arquitectura basada en componentes, la representación de sprites y el soporte de animación del motor nos permitieron crear gráficos visualmente atractivos, animaciones fluidas y elementos de juego interactivos sin tener que reinventar la rueda. Gracias a que aprovechamos las ventajas de Flame, logramos que el proceso de desarrollo fuera más eficiente, con funciones esenciales listas para usar, lo que nos permitió centrarnos en crear una experiencia de juego atractiva para nuestros jugadores.


Implementación de funciones del juego con Flame

Flame ofrece una implementación de bucle de juego simple pero efectiva y una amplia gama de funcionalidades esenciales, como reproducción de audio, administración de sprites, funciones de animación, colisión y el sistema de componentes de Flame (FCS).

El sistema de renderización y animación de sprites de Flame nos permitió dar vida a los personajes y entornos del juego sin tener que escribir código gráfico. Pudimos crear hojas de sprites, definir secuencias de animación y animar sin problemas los movimientos y efectos especiales del personaje. Utilizamos el sistema de prioridades de Flame para escribir un sistema de clasificación visual que se adaptara a nuestra perspectiva de juego “de arriba hacia abajo”. Nuestro diseñador creó niveles con muchas características superpuestas para mejorar la sensación de profundidad con los sprites y darle a la vista del juego una apariencia menos “de cuadrícula”. Necesitábamos asegurarnos de que cuando el robot se moviera detrás de ellos, se ocluyera correctamente. El sistema de prioridades de Flame nos permitió asignar prioridades a diferentes elementos visuales, y asegurarnos de que estuvieran dibujados en el orden apropiado y se superpusieran correctamente. Este sistema tuvo la flexibilidad suficiente como para incluir un sistema de sombras donde los componentes individuales podían presentar sombras animadas que reflejaban las acciones de los objetos y le daban al diseño una sensación de profundidad que hacía que las escenas fueran más vibrantes y fáciles de comprender visualmente.

La clase BoxShadow de nuestra base de código es un buen ejemplo de esto y muestra cómo creamos sombras dinámicas e interactivas para las cajas en movimiento del juego. Al extender el SpriteAnimationComponent e implementar los mixins GridElement y HasVisibility, pudimos cargar hojas de sprites para las animaciones de sombras y teletransportación de cajas, definir múltiples animaciones para los estados abierto, cerrado, inactivo y teletransportación, e integrarlas en el diseño basado en cuadrícula. El método onLoad carga las animaciones y establece la posición inicial y la prioridad del componente en función de su posición en la cuadrícula, mientras que el método Update garantiza que la prioridad del componente se actualice dinámicamente si la caja se mueve delante o detrás de un objeto de oclusión. Este uso de los sistemas de prioridad y animación de Flame nos permitió crear efectos de sombra que siempre ayudan a los usuarios a comprender visualmente los espacios virtuales y hacerlos más creíbles, y generan una presentación visual más integrada.

Extracto de box_shadow.dart. Consulta la clase completa en GitHub para obtener más información.

@override
  Future<void> onLoad() async {
    await _loadAnimations().then((_) => {animation = _boxClosed});
 
 
    position.add(Vector2(
        ((gridPosition.x * gridPixelDimensions.x) + gridPixelOffset.x),
        ((gridPosition.y * gridPixelDimensions.y) + gridPixelOffset.y)));
    priority = getLayeredGridValue();
  }
  @override
  void update(double dt) async {
    super.update(dt);
 
 
    if (getLayeredGridValue() != priority) {
      priority = getLayeredGridValue();
    }
  }

Programa e insignias para desarrolladores de Google

Debido a que queríamos mantener un alcance pequeño para el juego y queríamos poder implementarlo como una simple página web, nuestra intención era evitar configurar un backend para el juego. Pero buscábamos recompensar de alguna manera a los desarrolladores por su progreso, como los sistemas de logros de las plataformas de juegos populares. En lugar de crear un sistema de backend separado para hacer un seguimiento del progreso y los logros de los jugadores, nos integramos con el Programa de Desarrolladores de Google y permitimos que los jugadores hicieran clic en un vínculo y recolectaran una insignia para su perfil una vez que hayan completado un nivel, lo que brindó una sensación de logro obtenido y reconocimiento dentro de la comunidad de desarrolladores.

Purrfect Code Google Developer Program profile badges

Proyecto IDX: un entorno de desarrollo optimizado

Durante el desarrollo de "Purrfect Code", aprovechamos el Proyecto IDX de Google, un espacio de trabajo asistido por IA para el desarrollo completo de aplicaciones multiplataforma en la nube. Como ya nos sentíamos cómodos trabajando en VS Code, Project IDX proporcionó un entorno familiar para codificar, depurar y probar nuestro juego y nos permitió ponernos en marcha rápidamente. Con Flutter y Dart ya configurados y listos para funcionar en el navegador, podríamos sumergirnos directamente en el desarrollo sin la molestia de la configuración del entorno local. La finalización inteligente de código, la verificación de errores en tiempo real y las herramientas de depuración integradas que ofrece Project IDX nos ayudaron a mantener nuestra productividad alta. Si tiene curiosidad, Project IDX es una excelente manera de probar rápidamente Purrfect Code y explorar su código directamente desde su navegador. Haga clic en este enlace para abrir el proyecto directamente en IDX y ejecutarlo usted mismo. Asegúrate de marcar la casilla que pregunta si se trata de una aplicación Flutter.


Firebase para un hosting rápido y seguro

Elegimos Firebase Hosting para garantizar la entrega global segura y eficiente de Purrfect Code. El SSL de configuración cero de la plataforma garantiza que el contenido se entregue a través de HTTPS, lo que mejora la seguridad. Además, la compatibilidad con marcos de trabajo web modernos y compilaciones automatizadas desde nuestro repositorio de GitHub permitió una rápida implementación de actualizaciones. La CLI de Firebase, la emulación local y las URL de vista previa optimizaron nuestros procesos de prueba y colaboración. Estas características, junto con el potencial de futuras evoluciones de juegos, en las que se pueden aprovechar las plantillas de muestra de Gemini para la integración de la IA, hicieron de Firebase Hosting la opción ideal para lanzar nuestro juego.


Configuración de Firebase con WASM

Purrfect Code utiliza Web Assembly, que requiere algunos pasos adicionales durante la implementación. En nuestra configuración de firebase.json, agregamos un comando de preimplentación que permite que nuestra compilación web se compile con WASM. El argumento "--no-strip-wasm" impide que el código se ejecute a través de un último paso de minimización, lo que hace que los errores sean más difíciles de leer y depurar. WASM también requiere una política de apertura de origen cruzado y una política de incorporación de origen cruzado para subprocesos múltiples y uso compartido de memoria.

"hosting": {
      "predeploy": "flutter build web --wasm",
      "public": "build/web",
      "ignore": [
        "firebase.json",
        "**/.*"
      ],
      "headers": [
        {
          "source": "**/*",
          "headers": [
            {
              "key": "cross-origin-opener-policy",
              "value": "same-origin"
            },
            {
              "key": "cross-origin-embedder-policy",
              "value": "require-corp"
            }
          ]
        }
      ]
    }

Aprendizaje y recursos

Durante el desarrollo de “Purrfect Code”, tomamos como referencia varios recursos y nos inspiramos en proyectos existentes, un cuerpo de trabajo que esperamos que este proyecto pueda aumentar. Estos son algunos recursos de aprendizaje clave y referencias que consideramos valiosos:


Proyectos de referencia

Utilizamos los juegos Super Dash y I/O Flip como referencias en relación con las prácticas recomendadas y las ideas de implementación. Ambos proyectos proporcionaron información valiosa sobre la estructuración de un juego de Flutter, el manejo de los estados del juego y la implementación de la mecánica del juego. Super Dash fue muy relevante porque era simple y no requería servicios de backend, como nuestro juego. I/O Flip era más grande y es compatible con un backend, así como con funciones de IA generativas, por lo que puede ser de interés para los desarrolladores que crean juegos que requieren esas funciones. Ambos son recursos valiosos si te interesa crear juegos en Flutter.


Conclusión

Esperamos que "Purrfect Code" no solo proporcione una experiencia de juego agradable, sino que también sirva como recurso de aprendizaje para los desarrolladores interesados ​​en el desarrollo de juegos con Flutter y Flame. La combinación de Flutter, Flame y Chrome resultó ser una excelente opción. para nuestro híbrido juego/aplicación, brindándonos una base sólida para el desarrollo de la interfaz de usuario, la representación de gráficos, la gestión del sonido y más. Te animamos a explorar el código fuente del juego y experimentar extendiéndolo aún más. Existen numerosas posibilidades para agregar nuevas funciones, niveles y mecánicas de juego. Ingresa al código base en Project IDX, marca la casilla que pregunta si se trata de una aplicación de Flutter y ¡deja volar tu creatividad!