Aplicacions amb Flutter, Dart i Flame

Tutorial Flutter Flame Projectes   Recursos CITCEA
Exemples Dart Dades pràctiques     Inici

Canviem la bola segons el sentit de moviment

En molts jocs hi ha un personatge que es desplaça per la pantalla. Normalment ens interessa que quan el personatge es desplaça cap a un costat o un altre estigui mirant cap a aquell costat. Per fer-ho, cal tenir les dues versions al mateix full i triar una o l'altra segons el sentit del moviment. Fins i tot, en alguns casos, pot interessar tenir quatre orientacions del personatge o l'element que es desplaça.

En aquest cas canviarem el color de la bola quan es desplaci a la dreta o a l'esquerra. És un canvi que no és gaire realista però ens serveix per mostrar com es faria.

El nostre fitxer d'imatges serà la que es mostra a continuació, on trobem la pala, els cinc totxos i les dues boles.

Full d'sprites

Per baixar-la, pots picar sobre la imatge amb el botó dret i guardar-la a l'ordinador.

A la taula següent tenim les mides i la posició de cada imatge. La posició correspon a la coordenada superior esquerra.

Element Mides Posició
Amplada Alçada X Y
Pala 512 64 0 0
Totxo blau 256 128 0 64
Totxo verd 256 128 256 64
Totxo groc 256 128 0 192
Totxo taronja 256 128 256 192
Totxo roig 256 128 0 320
Bola1 64 64 256 320
Bola2 64 64 320 320

En el fitxer de configuració hem d'afegir les coordenades de la nova bola.

config.dart
import 'package:vector_math/vector_math.dart';
const gameWidth = 820.0;
const gameHeight = 1600.0;
const ballRadius = gameWidth * 0.02;
const batWidth = gameWidth * 0.2;
const batHeight = ballRadius * 2;
const batStep = gameWidth * 0.05;
const numTotxosFilera = 10;
const juntaTotxos = gameWidth * 0.015;
final brickWidth =
    (gameWidth - (juntaTotxos * (numTotxosFilera + 1))) / numTotxosFilera;
const brickHeight = gameHeight * 0.03;
const factorDif = 1.03;
// Configuració per al full d'sprites
Vector2 posPalaImg = Vector2(0, 0);
Vector2 midaPalaImg = Vector2(512, 64);
// Definim les coordenades de cada totxo dins de la imatge
final List<Vector2> llistaCoordenades = [
  Vector2(0.0, 64.0),  // Totxo blau
  Vector2(256.0, 64.0),  // Totxo verd
  Vector2(0.0, 192.0),  // Totxo groc
  Vector2(256.0, 192.0),  // Totxo taronja
  Vector2(0.0, 320.0),  // Totxo roig
];
Vector2 midaTotxoImg = Vector2(256, 128);
Vector2 posBolaImg1 = Vector2(256, 320);
Vector2 posBolaImg2 = Vector2(320, 320);
Vector2 midaBolaImg = Vector2(64, 64);

En la definició de la bola creem dues variables per guardar les definicions dels sprites, així només cal passar de l'una a l'altra sense haver-ho de tornar a carregar. Després carreguem els dos sprites amb la imatge qie correspon a cada un.

Atès que la velocitat inicial és aleatòria, definim l'sprite inicial segons el signe de la seva coordenada X. També actualitzem l'sprite dins del mètode update, per assegurar que es fa a cada refresc.

bola.dart
import 'package:flame/collisions.dart';
import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:joc_breakout/breakout.dart';
import 'package:joc_breakout/components/pala.dart';
import 'package:joc_breakout/components/totxo.dart';
import 'package:joc_breakout/components/play_area.dart';
import 'package:joc_breakout/config.dart';
class Bola extends SpriteComponent  //
    with CollisionCallbacks, HasGameReference<Breakout> {
  Bola({
    required this.velocity,
    required super.position,
    required double radius,
    required this.factorDificultat,
  }) : super(
        // Posem el diàmetre (doble del radi) en les dues direccions
        size: Vector2.all(radius * 2), //
        anchor: Anchor.center,
      );

  final Vector2 velocity;
  final double factorDificultat;

  // Declarem els dos sprites com a variables de la classe per no recrear-los constantment
  late final Sprite spriteDreta;
  late final Sprite spriteEsquerra;

  // En carregar l'sprite, definim el fitxer i el detector de col·lisions
  @override
  Future<void> onLoad() async {
    super.onLoad();

    // Carreguem la imatge des del fitxer
    spriteDreta = Sprite(
      game.images.fromCache('sheet.png'),
      srcPosition: posBolaImg1,   // Coordenades inici bola
      srcSize: midaBolaImg,     // Mida de la bola
    );
    spriteEsquerra = Sprite(
      game.images.fromCache('sheet.png'),
      srcPosition: posBolaImg2,   // Coordenades inici bola
      srcSize: midaBolaImg,     // Mida de la bola
    );

    // Definim l'sprite inicial segons la velocitat de partida
    sprite = velocity.x >= 0 ? spriteDreta : spriteEsquerra;

    // Detector de col·lisions circular ajustat a la mida de la imatge
    add(CircleHitbox());
  }

  @override
  void update(double dt) {
    super.update(dt);
    position += velocity * dt;

    // Actualitzem l'sprite dinàmicament a cada frame segons la direcció actual
    if (velocity.x >= 0) {
      sprite = spriteDreta;
    } else {
      sprite = spriteEsquerra;
    }
  }

  @override
  void onCollisionStart(
    Set<Vector2> intersectionPoints,
    PositionComponent other,
  ) {
    super.onCollisionStart(intersectionPoints, other);
    if (other is PlayArea) {
      if (intersectionPoints.first.y <= 0) {
        velocity.y = -velocity.y;
      } else if (intersectionPoints.first.x <= 0) {
        velocity.x = -velocity.x;
      } else if (intersectionPoints.first.x >= game.width) {
        velocity.x = -velocity.x;
      } else if (intersectionPoints.first.y >= game.height) {
        add(
          RemoveEffect(
            delay: 0.35,
            onComplete: () {
              game.playState = PlayState.fi;
            },
          ),
        );
      }
    } else if (other is Pala) {
      velocity.y = -velocity.y;
      velocity.x =
          velocity.x +
          (position.x - other.position.x) / other.size.x * game.width * 0.3;
    } else if (other is Totxo) {
      if (position.y < other.position.y - other.size.y / 2) {
        velocity.y = -velocity.y;
      } else if (position.y > other.position.y + other.size.y / 2) {
        velocity.y = -velocity.y;
      } else if (position.x < other.position.x) {
        velocity.x = -velocity.x;
      } else if (position.x > other.position.x) {
        velocity.x = -velocity.x;
      }
      velocity.setFrom(velocity * factorDificultat);
    }
  }
}

 

 

 

 

 

 

 

 

 

 

Llicència de Creative Commons
Aquesta obra d'Oriol Boix està llicenciada sota una llicència no importada Reconeixement-NoComercial-SenseObraDerivada 3.0.