Actualmente tenemos un fondo más grande que el que se puede mostrar en la ventana, dando como resultado de que existe una gran parte del mismo que no puede ser visible; para poder aprovechar este fondo no visible y que se muestre a medida que el player se desplaza, tenemos que colocar un seguimiento de la cámara al player.
El concepto de cámara se ha trabajado en cualquier motor de videojuegos como Unity o Unreal o software de creación de contenido en 3D/2D como Blender debe de ser conocido por ti; pero en esencia, la cámara no es más que un mecanismo por el cual podemos observar una parte del mundo, en donde el mundo no es más que todos los componentes que están dentro del juego; y mediante coordenadas, podemos desplazar este observador.
En este tipo de juegos, usualmente la cámara sigue al player y con esto, podemos visualizar partes del fondo que no son visibles a medida que el player se desplaza por el mundo.
Puedes obtener más información al respecto en:
https://docs.flame-engine.org/latest/flame/camera_component.html
En Flame tenemos un componente llamado CameraComponent con la cual, podemos indicar el área de visualización.
El componente World, tal cual indica su nombre, es un componente que puede ser usado para albergar todos los demás componentes que conforman el mundo de juego. En el caso del juego que estamos implementando sería:
Todos estos componentes, deben ser agregados en una instancia del componente World para que se encuentren en la misma capa y con esto, poder interactuar con el player.
Usar un componente World es tan sencillo como, crear la instancia:
final world = World();
Y agregar en Flame:
add(world);
Y en este punto, todos los componentes que formen parte del juego, deben ser agregados a la instancia de world:
world.add(<COMPONENT>);
El componente de World y CameraComponent están diseñados para trabajar en conjunto; una instancia de la clase CameraComponent "mira" el mundo/world y esta es la razón por la cual introducimos el componente world junto con el componente de cámara.
Usualmente no es obligatorio usar un componente World para nuestros juegos en Flame, ya que, podemos agregar los componentes de nuestro juego directamente en la instancia de Flame:
add(player);
O en los mismos componentes, pero, en este tipo de juegos que tenemos un mundo más grande del que puede entrar en la pantalla, es necesario usar una cámara que siga o vigile a nuestro player.
CameraComponent como comentamos antes, es el componente empleado para observar al mundo, tenemos varias propiedades para establecer en este componente y que "mire" exactamente a donde queramos y que siga a un componente:
Como cualquier otro juego, la cámara debe de observar al jugador que es el que se va desplazando por el mundo, aunque, por suerte esta actualización se realiza de manera automática usando la función de follow(), que recibe como parámetro, un componente a observar y actualizar la posición de la cámara mientras este se desplaza por la pantalla:
cameraComponent.follow(player);
El Viewfinder, permite personalizar aspectos en la visualización como el anchor, con el cual, podemos especificar el centro de la cámara; es decir, nuestro mundo luce como el siguiente:
La cámara debe de estar centrada en la esquina inferior izquierda, es decir, en el bottomLeft:
Y para esto:
cameraComponent.viewfinder.anchor = Anchor.bottomLeft
Aclarado el funcionamiento del componente de mundo y cámara, vamos a implementar la lógica en nuestra aplicación:
class MyGame extends FlameGame *** {
***
late PlayerComponent player;
final world = World();
late final CameraComponent cameraComponent;
@override
void onLoad() {
var background = Background();
add(world);
world.add(background);
background.loaded.then(
(value) {
player = PlayerComponent();
cameraComponent = CameraComponent(world: world);
cameraComponent.follow(player);
cameraComponent.setBounds(Rectangle.fromLTRB(0, 0, background.size.x, background.size.y)));
// cameraComponent.viewfinder.anchor = Anchor.bottomLeft;
cameraComponent.viewfinder.anchor = const Anchor(0.1, 0.9);
add(cameraComponent);
cameraComponent.world.add(player);
},
);
add(ScreenHitbox());
}
}
Puntos claves
Definimos y agregamos a Flame el objeto world:
final world = World();
***
add(world);
Al usar el componente de cámara, es necesario emplear un objeto world:
cameraComponent = CameraComponent(world: world);
Con una instancia del componente anterior, podemos personalizar varios aspectos sobre la visualización como lo es, que la cámara siga a un componente mediante la función de follow() la cual recibe como parámetros al componente a seguir:
cameraComponent.follow(player);
El centro de la cámara, se define en la esquina inferior izquierda, pero, si la dejamos de esta manera:
cameraComponent.viewfinder.anchor = Anchor.bottomLeft; // Anchor(0, 1);
Al posicionar la cámara que está siguiendo al player, veremos que el player es visible en parte:
Esto se debe a que la cámara "sigue" al anchor del player, el cual está alienado en el centro:
PlayerComponent({required this.mapSize}) : super() {
anchor = Anchor.center;
debugMode = true;
}
Para evitar este comportamiento, podemos especificar valores numéricos para el anchor de la cámara que no sean tan restringidos como el valor anterior.
cameraComponent.viewfinder.anchor = const Anchor(0.1, 0.9);
Y con esto tenemos una visualización completa del player:
Sumado a lo anterior, también podemos colocar restricciones sobre la visualización de la cámara (el área visible por la cámara, que en este caso, es justamente es el tamaño de la imagen o mapa); es importante mencionar, que el área de visualización corresponde a un rectángulo con el tamaño del fondo; por lo tanto:
cameraComponent.setBounds(Rectangle.fromLTRB(0, 0, background.size.x, background.size.y)));
Y desde el main, pasamos la posición de la cámara:
lib\main.dart
@override
void update(double dt) {
if (elapsedTime > 1.0) {
Vector2 cp = cameraComponent.viewfinder.position;
cp.y = cameraComponent.viewfinder.position.y -
cameraComponent.viewport.size.y;
world.add(MeteorComponent(cameraPosition: cp));
elapsedTime = 0.0;
}
elapsedTime += dt;
super.update(dt);
}
Como puedes apreciar, no tenemos de manera directa la posición de la cámara, por lo tanto, debemos de hacer un cálculo; como comentamos anteriormente, mediante el Viewfinder:
cameraComponent.viewfinder
Tenemos la posición de la cámara, la cual se va actualizando mediante el player se va moviendo por pantalla, y podemos obtener con:
cameraComponent.viewfinder.position
Pero, esto nos da la posición de la cámara en la esquina inferior, es decir, la parte de abajo, y necesitamos generar los meteoritos en la parte de arriba, por tal motivo, le restamos el alto de la cámara que podemos obtener con:
cameraComponent.viewport.size.y
Este material forma parte de mi curso y libro completo sobre el desarrollo de juegos en 2D con Flutter y Flame.
- Andrés Cruz
In englishDesarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter
Acepto recibir anuncios de interes sobre este Blog.
!Cursos desde!
4$
En Academia
Ver los cursos!Libros desde!
1$
Ver los libros