Can I code a simple game in Golang instead of JavaScript?

Crea tu Primer Juego 2D con Golang y Fyne

14/03/2017

Valoración: 3.94 (2763 votos)

Muchos desarrolladores que se inician en el mundo de la creación de videojuegos recurren a JavaScript por su accesibilidad y la infinidad de tutoriales disponibles. Sin embargo, existen alternativas potentes y eficientes que pueden ofrecer una experiencia de desarrollo diferente y muy gratificante. ¿Es posible programar un juego simple en Golang en lugar de JavaScript? La respuesta es un rotundo sí. Inspirados en los excelentes tutoriales de Frank’s Laboratory, en este artículo exploraremos cómo dar vida a un personaje que camina sobre un fondo estático, utilizando el poder de Golang, su paquete estándar de manejo de imágenes y el versátil toolkit de GUI Fyne.

Can I code a simple game in Golang instead of JavaScript?
I have decided to code one of his simple games in Golang using the Fyne GUI toolkit for this article instead of JavaScript. The structure of the code I present to you here will also be somewhat different from the original by Frank’s Laboratory, given that Golang works differently from JavaScript.

El objetivo no es construir un juego completo desde cero, sino sentar las bases. Al finalizar, tendrás un personaje que podrás mover por la pantalla con las flechas del teclado. Aunque la estructura del código será distinta a la de un proyecto en JavaScript, debido a las diferencias inherentes entre ambos lenguajes, este ejercicio te proporcionará una base sólida para futuros proyectos de videojuegos en Go.

Índice de Contenido

¿Por qué Golang para Desarrollar Juegos?

Aunque JavaScript domina el desarrollo web, Golang se presenta como una alternativa formidable para aplicaciones que requieren alto rendimiento, como los videojuegos. Al ser un lenguaje compilado, Go ofrece una velocidad de ejecución significativamente superior a la de los lenguajes interpretados. Su sintaxis simple y limpia facilita la lectura y el mantenimiento del código, mientras que su robusto sistema de tipos estáticos ayuda a detectar errores en tiempo de compilación, no en ejecución. Además, sus famosas goroutines abren un mundo de posibilidades para manejar la concurrencia de manera eficiente, algo crucial para la lógica de juegos complejos que necesitan gestionar múltiples procesos simultáneamente (físicas, IA, renderizado, etc.).

Preparando Nuestro Entorno de Juego

Antes de escribir una sola línea de código, necesitamos preparar los elementos básicos de nuestro proyecto: los activos gráficos y las librerías de Go que nos facilitarán el trabajo.

Los Activos Gráficos (Assets)

Todo juego necesita elementos visuales. Para nuestro proyecto, utilizaremos dos imágenes en formato PNG:

  • Imagen de fondo: Un escenario inspirado en Star Wars que servirá como nuestro nivel.
  • Hoja de Sprites del Personaje: Una imagen que contiene múltiples fotogramas de nuestro personaje, Chewbacca, en diferentes poses para simular el movimiento. Esto se conoce como una hoja de sprites (sprite sheet).

Es fundamental que, una vez descargados, estos dos archivos se coloquen en una carpeta llamada ./assets, la cual debe estar en el mismo directorio donde se ejecutará nuestro programa de Go.

Las Librerías de Golang a Utilizar

Nos apoyaremos en un conjunto de paquetes que nos proporcionarán las herramientas necesarias:

  • Fyne: Es un toolkit de GUI (Interfaz Gráfica de Usuario) para Go, moderno y multiplataforma. Lo usaremos para crear la ventana del juego, dibujar nuestros activos en ella y, lo más importante, capturar los eventos del teclado para mover al personaje.
  • Image: Este es un paquete de la librería estándar de Go. Nos proporciona todas las funcionalidades necesarias para cargar, decodificar y manipular nuestras imágenes PNG (el fondo y la hoja de sprites).
  • Time: Otro paquete de la librería estándar. Será útil para controlar el ritmo del juego, como la velocidad de la animación, creando un bucle de juego más estable.

La Arquitectura del Código: Modelando al Jugador y el Juego

En Go, es una práctica común utilizar 'structs' para modelar las entidades de nuestro programa. Para nuestro juego, definiremos una estructura principal para el jugador (Player), que contendrá toda la información relevante sobre su estado y apariencia.

La Estructura Player

Esta estructura es el cerebro de nuestro personaje. Cada campo tiene un propósito específico para controlar su posición, animación y movimiento.

  • x, y: Coordenadas de tipo flotante que determinan la posición del personaje en la ventana del juego.
  • width, height: El ancho y alto de un único fotograma (sprite) dentro de la hoja de sprites. Es crucial que todos los fotogramas tengan el mismo tamaño.
  • frameX, frameY: Estos campos indican qué fotograma específico de la hoja de sprites se debe dibujar. frameX es la columna y frameY es la fila.
  • cyclesX: Un contador que nos dice cuántos fotogramas hay en una fila de animación (por ejemplo, para la animación de caminar).
  • upY, downY, leftY, rightY: Estas variables son muy importantes. Almacenan el índice de la fila (frameY) en la hoja de sprites que corresponde a cada dirección. Por ejemplo, la fila 0 puede contener los sprites para caminar hacia arriba, la fila 1 para caminar hacia abajo, etc.
  • speed: Un valor numérico que define cuántos píxeles se moverá el personaje cada vez que se actualice su posición.

Al encapsular toda esta lógica en una estructura, mantenemos nuestro código organizado y fácil de entender. Es un enfoque mucho más estructurado que manejar múltiples variables globales, como a veces se ve en ejemplos simples de JavaScript.

El Corazón del Juego: El Bucle Principal y la Animación

El concepto central en la mayoría de los juegos es el game loop o bucle de juego. Es un ciclo infinito que se encarga de tres tareas fundamentales repetidamente: procesar la entrada del usuario, actualizar el estado del juego y renderizar (dibujar) los gráficos en la pantalla.

1. Procesamiento de Entrada

Fyne nos permite registrar funciones que se ejecutan cuando ocurre un evento, como presionar una tecla. Configuraremos un manejador para las teclas de flecha. Cuando un usuario presiona, por ejemplo, la flecha derecha:

  1. Actualizamos la dirección del jugador, cambiando el valor de player.frameY para que coincida con el valor de player.rightY. Esto asegura que se use la fila correcta de sprites.
  2. Aplicamos el movimiento actualizando la coordenada player.x sumándole el valor de player.speed.

2. Actualización del Estado (Animación)

Para que el personaje no se deslice sin mover las piernas, necesitamos una animación por sprites. Cuando el personaje está en movimiento, dentro del bucle de juego, incrementamos continuamente el campo player.frameX. Cuando llega al final de los fotogramas disponibles (cyclesX), lo reiniciamos a 0. Esto crea la ilusión de que el personaje está caminando. Este contador solo debe avanzar cuando una tecla de movimiento está presionada.

3. Renderizado

En cada iteración del bucle, la pantalla debe ser redibujada desde cero. El proceso es el siguiente:

  1. Se dibuja la imagen de fondo completa, cubriendo toda la ventana.
  2. Se dibuja una porción específica de la hoja de sprites de Chewbacca. La porción a dibujar se calcula usando las propiedades de nuestro `Player`: se recorta un rectángulo de la hoja de sprites de tamaño `width` x `height`, comenzando en la coordenada `(frameX * width, frameY * height)`.
  3. Este recorte se dibuja en la ventana en la posición `(player.x, player.y)`.

Este ciclo se repite decenas de veces por segundo, creando un movimiento fluido y una animación convincente.

Tabla Comparativa: JavaScript vs. Golang para este Proyecto

CaracterísticaJavaScript (con HTML Canvas)Golang (con Fyne)
TipadoDinámico. Los errores de tipo se descubren en tiempo de ejecución.Estático. El compilador detecta errores de tipo antes de ejecutar.
RendimientoInterpretado (aunque con optimizaciones JIT). Generalmente más lento.Compilado a código máquina nativo. Muy alto rendimiento.
Manejo de EstadoA menudo se usan objetos o variables globales para el estado del jugador.Uso de structs para encapsular el estado de forma limpia y organizada.
RenderizadoMediante la API de Canvas 2D y su método drawImage().Fyne abstrae el renderizado. Se trabaja con objetos de tipo Canvas y Raster.
DistribuciónSe ejecuta en cualquier navegador web sin instalación.Se compila en un único archivo ejecutable nativo para cada sistema operativo.

Preguntas Frecuentes (FAQ)

¿Necesito experiencia previa en Golang para seguir este tutorial?

Tener conocimientos básicos de Go (variables, structs, funciones) es muy recomendable. Sin embargo, la lógica del juego es lo suficientemente simple como para que un principiante pueda seguirla y aprender mucho en el proceso.

¿Es Fyne la única opción para hacer juegos en Go?

No, en absoluto. El ecosistema de Go para desarrollo de juegos está en crecimiento. Otras librerías muy populares y potentes son Ebiten (enfocada en juegos 2D) y Raylib-go (un wrapper de la famosa librería Raylib). Fyne es una excelente opción si buscas crear aplicaciones con interfaces gráficas que, además, puedan contener un componente de juego.

¿Cómo podría añadir colisiones con los bordes de la pantalla?

Este sería un excelente siguiente paso. Dentro del bucle de juego, después de calcular la nueva posición del jugador pero antes de dibujarlo, deberías añadir una comprobación. Por ejemplo, para el borde izquierdo: if player.x < 0 { player.x = 0 }. Harías lo mismo para los otros tres bordes, asegurándote de que las coordenadas del jugador nunca excedan los límites de la ventana.

¿Por qué usar una hoja de sprites en lugar de imágenes separadas para cada fotograma?

Usar una hoja de sprites es una técnica de optimización clásica en el desarrollo de videojuegos. Cargar una sola imagen en memoria es mucho más eficiente que cargar decenas de archivos pequeños. Además, reduce las llamadas a la API de renderizado, lo que puede mejorar significativamente el rendimiento, especialmente en juegos más complejos.

Conclusión

Hemos visto que Golang, combinado con una librería como Fyne, es perfectamente capaz de manejar las tareas fundamentales del desarrollo de un juego 2D. Aunque hemos partido de una idea comúnmente implementada en JavaScript, la adaptación a Go nos obliga a pensar de una manera más estructurada y nos recompensa con un rendimiento excepcional y un código robusto. Este pequeño proyecto de un personaje caminante es solo la punta del iceberg. A partir de aquí, puedes explorar cómo añadir enemigos, físicas simples, recolectar objetos o crear niveles más complejos. ¡El viaje en el desarrollo de juegos con Go no ha hecho más que empezar!

Si quieres conocer otros artículos parecidos a Crea tu Primer Juego 2D con Golang y Fyne puedes visitar la categoría Juegos.

Subir