Crear un juego cómo Flappy Bird usando Flame y Flutter

Tiempo de lectura: 5 minutos

Flutter es un marco de desarrollo móvil de código abierto que permite a los desarrolladores crear aplicaciones móviles de alta calidad para Android y iOS. Una de las herramientas más populares en Flutter para la creación de juegos es la biblioteca de animaciones y gráficos conocida como Flame.

Flappy Bird es un juego de plataformas simple y adictivo en el que un pájaro debe volar a través de tubos sin chocar con ellos.

En este tutorial, crearemos un juego simple utilizando la biblioteca Flame en Flutter.

Nuestro juego se llamará «Flappy Bird Clone» y se centrará en la mecánica de juego de «Flappy Bird». El objetivo del juego es guiar a un pájaro a través de una serie de tuberías sin chocar con ellas.

  1. Configuración de Flutter y Flame

Primero, necesitas instalar Flutter y configurar un proyecto de Flutter. Puedes seguir la documentación oficial de Flutter para hacerlo. Una vez que hayas creado un proyecto de Flutter, puedes agregar la dependencia de Flame a tu proyecto en el archivo pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  flame: ^1.0.0-rc11

Luego, ejecuta el comando flutter pub get en tu terminal para instalar las dependencias.

  1. Configuración del juego

En tu proyecto de Flutter, crea un nuevo archivo llamado «game.dart». Este archivo contendrá todo el código del juego. Primero, importa las dependencias necesarias:

import 'dart:math';
import 'package:flame/flame.dart';
import 'package:flame/game.dart';
import 'package:flame/sprite.dart';
import 'package:flame/components/component.dart';
import 'package:flame/position.dart';
import 'package:flutter/gestures.dart';

Luego, crea una clase llamada «FlappyBirdCloneGame» que extienda la clase «BaseGame» de Flame:

class FlappyBirdCloneGame extends BaseGame {
  @override
  void update(double dt) {
    // TODO: Implementar la lógica del juego aquí
  }
  
  @override
  void render(Canvas canvas) {
    // TODO: Implementar la representación del juego aquí
  }
}

Esta clase será el núcleo del juego y contendrá toda la lógica y la representación del juego.

  1. Creación del fondo

Para crear el fondo del juego, necesitamos cargar una imagen de fondo y renderizarla en el juego. Primero, carga la imagen en el constructor del juego:

class FlappyBirdCloneGame extends BaseGame {
  Sprite background;
  
  FlappyBirdCloneGame() {
    background = Sprite('background.png');
  }
  
  // ...
}

Luego, en la función «render», dibuja la imagen de fondo en el lienzo:

@override
void render(Canvas canvas) {
  background.renderPosition(canvas, Position(0, 0));
  // ...
}
  1. Creación del pájaro

Para crear el pájaro, necesitamos cargar una imagen del pájaro y renderizarla en el juego. Además, necesitamos detectar las entradas táctiles del usuario para hacer que el pájaro vuele.

Primero, carga la imagen del pájaro en el constructor del juego:

class FlappyBirdCloneGame extends BaseGame {
  Sprite birdSprite;
  Position birdPosition;
  double birdVelocity = 0.0;
  
  FlappyBirdCloneGame() {
    birdSprite = Sprite('bird.png');
    birdPosition = Position(100, 100);
  }
  
  // ...
}

En este código, utilizamos una variable de posición para controlar la posición del pájaro en el juego y una variable de velocidad para controlar la velocidad del pájaro.

Luego, en la función «render», dibuja la imagen del pájaro en el lienzo:

@override
void render(Canvas canvas) {
  background.renderPosition(canvas, Position(0, 0));
  birdSprite.renderPosition(canvas, birdPosition);
  // ...
}

Para hacer que el pájaro vuele, necesitamos detectar las entradas táctiles del usuario. Agrega un controlador de gestos al constructor del juego:

FlappyBirdCloneGame() {
  // ...
  Flame.util.addGestureRecognizer(new TapGestureRecognizer()
    ..onTapDown = (TapDownDetails evt) => onTap());
}

En este código, creamos un nuevo controlador de gestos que detecta cuando el usuario toca la pantalla. Cuando se detecta un toque, se llama a la función «onTap».

Agrega la función «onTap» al juego:

void onTap() {
  birdVelocity = -300.0;
}

En esta función, establecemos la velocidad del pájaro en un valor negativo para hacer que el pájaro vuele hacia arriba cuando el usuario toca la pantalla.

  1. Creación de los tubos

Para crear los tubos, necesitamos cargar una imagen del tubo y renderizarla en el juego. Además, necesitamos detectar las colisiones del pájaro con los tubos.

Primero, carga la imagen del tubo en el constructor del juego:

class FlappyBirdCloneGame extends BaseGame {
  Sprite tubeSprite;
  
  FlappyBirdCloneGame() {
    // ...
    tubeSprite = Sprite('tube.png');
  }
  
  // ...
}

Luego, crea una clase llamada «Tube» que extienda la clase «Component» de Flame:

class Tube extends Component {
  Sprite tubeSprite;
  double topY, bottomY, x;
  
  Tube(this.tubeSprite, this.x, this.topY, this.bottomY);
  
  @override
  void render(Canvas canvas) {
    tubeSprite.renderPosition(canvas, Position(x, topY));
    tubeSprite.renderPosition(canvas, Position(x, bottomY));
  }
  
  @override
  void update(double dt) {
    x -= 200.0 * dt;
  }
}

En esta clase, utilizamos las variables de posición «topY» y «bottomY» para controlar la posición de los tubos en el juego. Además, utilizamos la variable «x» para controlar la posición horizontal de los tubos y la función «update» para mover los tubos hacia la izquierda.

En la función «update» del juego, crea nuevos tubos y añádelos al juego:

@override
void update(double dt) {
  super.update(dt);
  
  if (timer >= 1.0) {
    timer = 0.0;
    double y = rnd.nextDouble() * (size.height - 300.0) + 150.0;
    add(Tube(tubeSprite, size.width, y - 150.0, y + 150.0));
  }
  
  timer += dt;
}

En este código, utilizamos una variable de temporizador para crear nuevos tubos cada segundo. En la función «add» del juego, añadimos el nuevo tubo al juego.

Para detectar las colisiones del pájaro con los tubos, agrega una función de detección de colisiones al juego:

bool collidesWithTube(Tube tube) {
  double birdRadius = birdSprite.size.width / 2;
  double birdX = birdPosition.x + birdRadius;
double birdY = birdPosition.y + birdRadius;
  double topTubeY = tube.topY + tube.tubeSprite.size.height;
  double bottomTubeY = tube.bottomY;
  
  if (birdX > tube.x - birdRadius && birdX < tube.x + tube.tubeSprite.size.width + birdRadius) {
    if (birdY > topTubeY || birdY < bottomTubeY) {
      return true;
    }
  }
  
  return false;
}

En esta función, comprobamos si la posición del pájaro se superpone con la posición de los tubos. Si se detecta una colisión, la función devuelve «true».

  1. Creación de la pantalla de juego

Ahora que hemos creado la lógica del juego, necesitamos crear una pantalla de juego para que el usuario pueda jugar.

Crea una nueva pantalla llamada «GameScreen» y añade un objeto «FlappyBirdCloneGame» a la pantalla:

class GameScreen extends StatefulWidget {
  @override
  _GameScreenState createState() => _GameScreenState();
}

class _GameScreenState extends State<GameScreen> {
  final game = FlappyBirdCloneGame();
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: game.widget,
    );
  }
}

En esta pantalla, utilizamos un objeto «Scaffold» para envolver el widget del juego. El widget del juego se crea utilizando la función «widget» del objeto «FlappyBirdCloneGame».

  1. Creación del menú principal

Para permitir que el usuario inicie el juego, necesitamos crear un menú principal con un botón de inicio.

Crea una nueva pantalla llamada «MainMenuScreen» y añade un botón de inicio:

class MainMenuScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(context, MaterialPageRoute(builder: (context) => GameScreen()));
          },
          child: Text('Start'),
        ),
      ),
    );
  }
}

En este código, utilizamos un widget «ElevatedButton» para crear el botón de inicio. Cuando el usuario pulsa el botón, se navega a la pantalla de juego utilizando la función «push» del objeto «Navigator».

  1. Creación del juego completo

Ahora que hemos creado todas las partes del juego, podemos crear el juego completo utilizando un objeto «MaterialApp» en la clase principal:

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flappy Bird Clone',
      home: MainMenuScreen(),
    );
  }
}

En este código, utilizamos un objeto «MaterialApp» para envolver todas las pantallas del juego. La pantalla principal es la pantalla de menú.

Conclusión

En este tutorial, hemos creado un juego Flappy Bird Clone utilizando el motor de juegos Flame con Flutter. Hemos creado un objeto de juego, un fondo, un pájaro, tubos y una pantalla de juego. También hemos creado una pantalla de menú para que el usuario pueda iniciar el juego.

Este tutorial es solo una introducción a la creación de juegos con Flutter y Flame. Con las habilidades aprendidas en este tutorial, puedes crear juegos más complejos y emocionantes en el futuro. ¡Diviértete creando juegos!

Deja un comentario