Implementar un Joystick en Flutter y Flame

Hasta este momento, hemos usado el teclado como fuente principal para interactuar con el juego, pero, esto trae como inconveniente de que solo podríamos emplear dispositivos que cuenten con un teclado, como sería un PC o Mac, dejando a la aplicación fuera de otros dispositivos como teléfonos con Android e iOS; para que este tipo de aplicación, en la cual es necesario desplazar al player en todos los ejes (o solamente de izquierda a derecha y viceversa, como en el caso del juego del dinosaurio) podemos implementar un joystick virtual; Flame dispone de este tipo de componentes ya listos para usar en nuestra aplicación.

Un joystick virtual luce como el siguiente:

En definitiva, puedes ver que constan de dos componentes:

  1. El círculo translúcido que indica el área de movimiento de la palanca/mando.
  2. El circulo de color solido que se encuentra contenido en el anterior que indica el control para que el usuario pueda mover el joystick virtual, este corresponde a la palanca o mando.

Comencemos implementando una clase joystick como la siguiente:

lib\components\hud\joystick.dart

import 'package:flame/components.dart';
import 'package:flutter/material.dart';

class Joystick extends JoystickComponent {
  Joystick(
      {required PositionComponent knob,
      required PositionComponent background,
      required EdgeInsets margin})
      : super(knob: knob, background: background, margin: margin);
}

La clase JoystickComponent existe en la API de Flame y para implementar la misma, tenemos que definir dos propiedades fundamentales:

  1. knob, que hace referencia al mando.
  2. background, que es el área en la cual se puede desplazar el mando.

Ambos son PositionComponent por lo tanto, pueden ser un Sprite o una figura geométrica como un círculo.

Existen otras propiedades para personalizar el joystick como puedes consultar en la documentación oficial, pero, estas son las principales o claves.

Finalmente, creamos otro archivo y clase, con el cual, implementaremos la clase Joystick personalizada definida anteriormente y que llamaremos como Hud de heads-up display que es un término común en los videojuegos para referirse al apartado de iconos, mapas, vida, etc sobre el personaje. En esta clase implementamos la propiedad de knob y background del joystick como un par de círculos con colores, además, de posicionar el mismo en la pantalla mediante el margen:

lib\components\hud\hud.dart

import 'dart:async';

import 'package:flame/components.dart';
import 'package:flame/palette.dart';
import 'package:flutter/widgets.dart';
import 'package:parallax06/hud/joystick.dart';

class HudComponent extends PositionComponent {
  late Joystick joystick;

  @override
  void onLoad() {
    final joystickKnobPaint = BasicPalette.red.withAlpha(200).paint();
    final joystickBackgroundPaint = BasicPalette.black.withAlpha(100).paint();

    joystick = Joystick(
        knob: CircleComponent(radius: 30.0, paint: joystickKnobPaint),
        background:
            CircleComponent(radius: 100, paint: joystickBackgroundPaint),
        margin: const EdgeInsets.only(left: 40, top: 100));

    add(joystick);
  }
}

Finalmente, desde el main, agregamos una instancia de la clase anterior:

lib\main.dart

import 'package:parallax06/hud/hud_component.dart';
***
class MyGame extends FlameGame
    with HasKeyboardHandlerComponents, HasCollisionDetection {
  ***
  late HudComponent hudComponent;

  @override
  void onLoad() async {
    ***
    hudComponent = HudComponent();
    add(hudComponent);
  }
  ***
}

El componente de Joystick de Flame, tiene todo lo necesario para implementar la movilidad en nuestros componentes; mediante el tipo enumerado:

joystick.direction

Conocemos el estado del knob o mando:

enum JoystickDirection {
  up,
  upLeft,
  upRight,
  right,
  down,
  downRight,
  downLeft,
  left,
  idle,
}

También tenemos otras opciones que podemos usar para determinar el desplazamiento del knob; es decir, mientras más lejos el usuario lleve el mando:

Más alto será el vector:

[-1.0,-67.0] (joystick.delta)

Y mientras más cerca esté el knob del centro (mientras menos se desplace):

Más bajo será el vector:

[-1.0,-21.0] (joystick.delta)

Con este vector también tenemos el ángulo, es decir que si movemos el knob hacia arriba, tendremos un valor negativo en el Y:

[X,-72.0] (joystick.delta)

Y positivo si va hacia abajo:

[X,72.0] (joystick.delta)

Y la misma lógica se aplica para el eje de las X. Con esto, podemos usar de manera directa esta propiedad para desplazar al player sin necesidad de hacer comparaciones adicionales; finalmente, el desplazamiento del player mediante el joystick queda como:

lib\components\player_component.dart

class PlayerComponent extends Character with HasGameRef<MyGame> {
  ***
  final double _maxVelocity = 5.0;

  @override
  void update(double dt) {
    ***
    _movePlayerJoystick(dt);
  }

  void _movePlayerJoystick(double delta) {
    if (gameRef.hudComponent.joystick.direction != JoystickDirection.idle) {
      position.add(gameRef.hudComponent.joystick.delta * _maxVelocity * delta);
    }
  }
} 

Es importante mencionar que podemos tener habilitados (como es el caso para nuestra aplicación) dos o más funciones para el desplazamiento, en nuestro proyecto sería el desplazamiento mediante teclado y también mediante el joystick.

Finalmente, al ejecutar la aplicación, tendremos:

Claro está, que el joystick lo puedes implementar en los juegos anteriores para utilizar el juego en otros dispositivos que no cuenten con teclados.

Este material forma parte de mi curso y libro completo sobre el desarrollo de juegos en 2D con Flutter y Flame.

Transcripción del vídeo

Vamos de una ya implementar nuestro joystick o al menos una parte ya la parte del movimiento que bueno sencilla pero para no hacerlo tan largo lo vemos en el siguiente vídeo así que por aquí vamos a hacer la firma también crear el joystick como tal Y por supuesto poder verlo aquí a nivel de la pantalla entonces para esto para tener esto un poco más organizado voy a crear un componente una carpeta llamada Hood por aquí vamos a crear nuestros redundancia comenzando con el de joystick aquí lo puedes colocar la palabra componente también pero bueno yo para no marear este mucho Voy a dejarlo así y ya me gusta siempre Mostrar bueno hacer varias variantes aquí en mi código en fin por aquí tenemos la clase con el mismo nombre llamada joystick que extiende de joystick componentes joystick aquí lo tenemos componente y aquí tenemos bueno donde se encuentra en componentes como la mayoría o como todos la cual vamos a colocar aquí constructor e indicamos los parámetros necesarios para nosotros el posición componente para background que es el control o el mando aquí lo tenemos otra posición componente para el background este también es requerido coloque polígono Dios mío posición [Música] también requerido y el margen finalmente Ya por aquí llamamos a la superclase es decir este es nuestro constructor el de nosotros que también es requerido y este es el de la clase joystick como tal de yo estoy componente este de la súper clase Recuerda que este Llamar nuevamente al método padre que en este caso sería este entonces aquí le pasamos el cono Fíjate que ya lo tenemos o el no Mejor dicho este es el nuestro Recuerda que este es el de esta clase y este es el nuestro ahí lo puedes ver más claramente Bueno yo sé que tomaré un poquito a veces con lo cual background Aquí también coloco el background y finalmente margin con esto con estas tres sencillas propiedades ya tenemos aquí nuestro joystick en fin regresamos acá y creamos otro llamado y por aquí colocamos Boot va a extender de un posición componente y por aquí creamos una instancia de nuestro joystick aquí también es mejor colocarle joystick para que no choque con el yo este componen de flame Este es el de nosotros nuevamente este no lo confundas con este en fin por aquí en el hacemos la parte de la inicialización y todo esto así que bueno Esto creo que ya no va que es Y esto no haría falta eso siempre lo va cambiando flame en fin por aquí creamos nuestra instancia de joystick muy importante nuevamente colocar joystick y por aquí le tenemos que pasar todo esto así que por acá arriba vamos a crear sus valores el primero va a ser el del noc que es un basic punto red Bueno aquí tenemos varios colores yo estoy empleando los que ya te mostré antes punto Alfa bueno para el Alfa y finalmente Pain para crear el color para el siguiente Sería para el background colocón que va a ser de tipo Black como te mostraba y aquí si vamos a bajarle bastante al Alfa Ya lo tenemos ahora con estos colores perfectamente podemos crear nuestro componente Recuerda que esto recibe en posición componente entonces bueno usualmente se coloca un círculo Porque queremos que sea de forma circular hay mucha vuelta en este asunto por aquí colocamos bueno para un círculo necesitamos definir el radios para definir el tamaño del mismo Ya esto es matemática básica de bachillerato como de primaria Bueno aquí colocamos el radios que va a tener un radio de 30 ella aquí le podemos indicar un color para que sea visible en este caso como es el noc entonces colocamos el de joystick Paint y un proceso similar para Macro colocamos un radios solo que en este caso es mucho más grande y estos son valores que yo estudié y son los que yo estoy empleando

    joystick = Joystick(
        knob: CircleComponent(radius: 30.0, paint: joystickKnobPaint),
        background:
            CircleComponent(radius: 100, paint: joystickBackgroundPaint),
        margin: const EdgeInsets.only(left: 40, top: 100));

Así que bueno Tú puedes probar otros Pero estos son los que yo quiero y para el Paint, le colocamos joystick Power Paint finalmente aquí el voy a bajar esto colocamos el set punto only colocamos LED en unos 40 esto lógicamente es una constante Así que se lo colocamos y bueno Y aquí tenemos nuestro joystick así de simple y aquí tenemos la parte visual entonces falta es agregarlo colocamos joystick y esto sería todo por acá vamos a regresar aquí al Main y lo único que tenemos que hacer por acá es crear una instancia de nuestro justo Así que colocamos late por aquí lo llame solamente Hood debería de colocarle componente culpa a esta componente así queda más claro es pública En este caso porque la vamos a acceder desde el Player Así que ahora a nivel sea donde sea que esté aquí está creamos una instancia colocamos componente va a ser igual a hot componente y aquí lo agregamos y esto sería prácticamente todo este lo define como ley no A ver déjame chequear sí late entonces ejecutamos aquí la aplicación y se debería de Ver Nuestro círculo como te lo mostraba antes en alguna parte de la pantalla ya listo para poder emplear sin aún implementar ninguna lógica para la parte del desplazamiento Pero bueno vamos a esperar aquí un segundo y por aquí lo tenemos por aquí lo tenemos a la pantalla aquí puedes colocarle [Música] otro valor al margen si no te gusta siéntete libre personalizarlo yo dejarlo ahí Y fíjate que ya lo podemos mover es decir ya tenemos este comportamiento de gratis gracias al emplear nuestro yo estoy componite es decir del deflame por acá tal cual puedes ver entonces falta es implementar la parte de movimiento Pero eso lo vamos a hacer en el siguiente vídeo.

- Andrés Cruz

In english

Andrés Cruz

Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz En Udemy

Acepto recibir anuncios de interes sobre este Blog.