Nivel 7: Creemos un mini juego - desplazamiento de personaje

Juego con golang y ebitengine


🎮 Creemos un mini juego - desplazamiento de personaje

En este tutorial, crearemos un juego simple pero divertido llamado "Gopher Walk" utilizando la biblioteca Ebitengine. ¡Prepárate para aprender mientras te diviertes! 🚀

📺 Video Tutorial


🧠 Conceptos que aprenderemos

  1. Configuración básica de un juego con Ebitengine
  2. Manejo de entrada del usuario
  3. Animación de sprites
  4. Escalado y transformaciones de imágenes
  5. Ciclo de juego (actualización y dibujado)

🛠️ Preparación del entorno

Antes de comenzar, asegúrate de tener Go instalado en tu sistema. Puedes descargarlo e instalarlo desde la página oficial de Go.

Instalación de Ebitengine

  1. Visita la documentación oficial de instalación de Ebitengine.
  2. Sigue las instrucciones específicas para tu sistema operativo (Windows, macOS, o Linux).

Inicialización del proyecto

  1. Crea un nuevo directorio para tu proyecto:
    mkdir gopher-walk cd gopher-walk
  2. Inicializa un nuevo módulo Go:
    go mod init gopher-walk
  3. Instala las dependencias necesarias:
    go get "github.com/hajimehoshi/ebiten/v2" go get "github.com/hajimehoshi/ebiten/v2/ebitenutil"

💻 ¡Manos a la obra!

Vamos a escribir cada parte del código 

Importaciones y Constantes

package main import ( "fmt" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/ebitenutil" "image" "log" "math" ) const ( baseScreenWidth = 640 baseScreenHeight = 480 frameWidth = 64 frameHeight = 64 )
  • Importamos los paquetes necesarios, incluyendo Ebitengine (ebiten/v2) y utilidades adicionales (ebitenutil).
  • Definimos constantes para el tamaño de la pantalla y las dimensiones de cada frame de animación.

Estructura del Juego

type Game struct { playerImage *ebiten.Image playerX float64 playerY float64 frameIndex int direction int // 1 mira hacia la derecha, -1 mira hacia la izquierda frameCount int isMoving bool scale float64 speed float64 }

Esta estructura Game es el corazón de nuestro juego:

  • playerImage: Almacena la imagen del personaje.
  • playerX y playerY: Posición del jugador en la pantalla.
  • frameIndex: Índice del frame actual de la animación.
  • direction: Dirección del personaje (1 derecha, -1 izquierda).
  • frameCount: Contador para controlar la velocidad de la animación.
  • isMoving: Indica si el personaje está en movimiento.
  • scale y speed: Escala y velocidad del personaje.

Inicialización del Juego

func New() *Game { g := &Game{ playerX: baseScreenWidth / 2, playerY: baseScreenHeight / 2, direction: 1, frameCount: 0, isMoving: false, scale: 2.0, speed: 1.0, } //Carga la imagen del jugador img, _, err := ebitenutil.NewImageFromFile("gognition_gopher.png") if err != nil { log.Fatal(err) } g.playerImage = img return g }

Esta función crea una nueva instancia del juego:

  • Inicializamos al jugador en el centro de la pantalla.
  • Establecemos valores iniciales para dirección, escala y velocidad.
  • Cargamos la imagen del personaje desde un archivo.

Actualización del Juego

func (g *Game) Update() error { g.isMoving = false moveSpeed := g.speed * g.scale // Movimiento de nuestro personaje if ebiten.IsKeyPressed(ebiten.KeyLeft) { g.playerX -= moveSpeed g.direction = -1 g.isMoving = true } if ebiten.IsKeyPressed(ebiten.KeyRight) { g.playerX += moveSpeed g.direction = 1 g.isMoving = true } if ebiten.IsKeyPressed(ebiten.KeyUp) { g.playerY -= moveSpeed g.isMoving = true } if ebiten.IsKeyPressed(ebiten.KeyDown) { g.playerY += moveSpeed g.isMoving = true } //Escalamiento de nuestro personaje if ebiten.IsKeyPressed(ebiten.KeyQ) { g.scale = math.Max(0.1, g.scale*0.99) } if ebiten.IsKeyPressed(ebiten.KeyE) { g.scale = math.Min(5.0, g.scale*1.01) } //Ajuste de la velocidad if ebiten.IsKeyPressed(ebiten.KeyA) { g.speed = math.Max(0.1, g.speed*0.99) } if ebiten.IsKeyPressed(ebiten.KeyD) { g.speed = math.Min(5.0, g.speed*1.01) } // Animación del personaje if g.isMoving { g.frameCount++ if g.frameCount >= 5 { g.frameIndex = (g.frameIndex + 1) % 6 //Avanzamos al siguiente frame, volviendo al 0 después del 5 g.frameCount = 0 } } else { g.frameIndex = 0 } return nil }

Esta función se llama cada frame y maneja la lógica del juego:

  • Detectamos la entrada del teclado para mover al personaje.
  • Ajustamos la escala y velocidad del personaje.
  • Manejamos la animación del personaje cambiando los frames.

Renderizado del Juego

func (g *Game) Draw(screen *ebiten.Image) { baseScreen := ebiten.NewImage(baseScreenWidth, baseScreenHeight) op := &ebiten.DrawImageOptions{} op.GeoM.Scale(float64(g.direction)*g.scale, g.scale) if g.direction == -1 { op.GeoM.Translate(frameWidth*g.scale, 0) } //Posicionar el personaje op.GeoM.Translate(g.playerX, g.playerY) sx := g.frameIndex * frameWidth frame := g.playerImage.SubImage(image.Rect(sx, 0, sx+frameWidth, frameHeight)).(*ebiten.Image) //Dibujar el personaje en la imagen base baseScreen.DrawImage(frame, op) //Añadir información de depuración ebitenutil.DebugPrint(baseScreen, fmt.Sprintf("Escala: %.2f\nVelocidad: %.2f", g.scale, g.speed)) // Calcular la escala necesaria para ajustar el juego a la ventana actual screenScale := math.Min( float64(screen.Bounds().Dx())/float64(baseScreenWidth), float64(screen.Bounds().Dy())/float64(baseScreenHeight), ) screenOp := &ebiten.DrawImageOptions{} screenOp.GeoM.Scale(screenScale, screenScale) screenOp.GeoM.Translate( float64(screen.Bounds().Dx())/2-float64(baseScreenWidth)*screenScale/2, float64(screen.Bounds().Dy())/2-float64(baseScreenHeight)*screenScale/2, ) screen.DrawImage(baseScreen, screenOp) }

Esta función se encarga de renderizar el juego:

  • Creamos una imagen base y aplicamos transformaciones al personaje.
  • Seleccionamos el frame correcto de la animación.
  • Dibujamos el personaje y añadimos información de depuración.
  • Escalamos y centramos el juego en la pantalla.

Diseño de la Ventana

func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { return outsideWidth, outsideHeight }

Esta función maneja el diseño de la ventana, permitiendo que el juego se ajuste a diferentes tamaños de pantalla.

Función Principal

func main() { ebiten.SetWindowSize(baseScreenWidth, baseScreenHeight) ebiten.SetWindowTitle("¡Aprende haciendo con Gognition") if err := ebiten.RunGame(New()); err != nil { log.Fatal(err) } }

La función main es el punto de entrada de nuestro programa:

  • Configuramos el tamaño y título de la ventana.
  • Iniciamos el juego llamando a ebiten.RunGame(New()).

🕹️ Cómo Jugar

  • Usa las flechas del teclado para mover al personaje.
  • Presiona Q para reducir el tamaño del personaje y E para aumentarlo.
  • Presiona A para reducir la velocidad y D para aumentarla.

🌟 Source code

🚀 Próximos Pasos

¡Este es solo el comienzo! Puedes expandir "Gopher Walk" de muchas maneras:

  1. Añadir obstáculos o enemigos
  2. Implementar un sistema de puntuación
  3. Crear diferentes niveles
  4. Mejorar los gráficos y añadir efectos de sonido
¡Déjame en los comentarios si quieres más aprendizajes con videojuegos!

Crear juegos es una forma divertida y práctica de aprender Go. A través de este proyecto, hemos explorado conceptos fundamentales de Go y cómo se pueden aplicar en el desarrollo de juegos. 

¿Tienes preguntas o sugerencias? ¡Déjalas en los comentarios abajo! Y no olvides suscribirte a nuestro canal de YouTube para más tutoriales de Go.

Happy coding! 🐹✨

    Comentarios

    Formulario de contacto

    Enviar