Colisão Phaser – Tutorial Phaser Parte 4

Colisão Phaser – Este é o quarto artigo da série “Phaser para iniciantes”. No terceiro artigo (https://programandojuntos.com.br/tutorial-phaser-movimento-personagem/) criamos os controles do nosso personagem para ele poder se movimentar pelo mapa e também realizar o movimento de ataque. Agora nesse quarto artigo iremos incluir a colisão com o cenário e também permitir que o personagem realize apenas um ataque por vez.

Conheça nosso canal e assista o vídeo dessa aula:

Colisão Phaser – Setando propriedade de colisão

Como primeiro passo vamos iniciar uma propriedade chamada ‘water’ no nosso arquivo ‘src/game.ts’. O início dele ficará assim:

import * as Phaser from "phaser";
import { createPlayer, loadSprites } from "./player";
import { createControls, configControls } from "./controls";

export default class Demo extends Phaser.Scene {
  player;
  controls;
  water; // Aqui é a propriedade que iremos iniciar

Agora dentro da função ‘create()’ onde estava:

const water = map.createLayer("water", tilesetWater, 0, 0);

Substitua para:

this.water = map.createLayer("water", tilesetWater, 0, 0);

Em vez de guardar a layer referente a água numa constante, iremos armazenar numa propriedade da classe para que ela seja acessível em toda a Scene.

Logo abaixo dessa linha insira o seguinte trecho de código:

this.water.setCollisionByProperty({ collider: true });

Esse código é responsável por setar a propriedade “collision” da layer ‘water’ de acordo com o que configuramos durante a criação do mapa.

Recomendo muito que você assista esse vídeo para entender os detalhes da criação de mapas:

Colisão Phaser – Colidindo personagem com a água

Finalmente para a colisão começar funcionar, após o trecho:

this.player = createPlayer(this);

Insira o código:

this.physics.add.collider(this.player, this.water);

Este código espera como parâmetros as duas layers que estamos setando a configuração de colisão. Nesse caso queremos que o personagem colida com a layer da água.

Nossa função ‘create()’ deve estar assim agora:

create() {
  const map = this.make.tilemap({ key: "map" });
  const tilesetGrass = map.addTilesetImage("grass", "tiles");
  const tilesetWater = map.addTilesetImage("water", "border");

  const ground = map.createLayer("grass", tilesetGrass, 0, 0);
  this.water = map.createLayer("water", tilesetWater, 0, 0);

  this.water.setCollisionByProperty({ collider: true });
  this.player = createPlayer(this);
  this.physics.add.collider(this.player, this.water);
  this.controls = createControls(this);
}

Nesse momento a colisão já está funcionando e não é mais possível passar por cima da água com o personagem, pois agora quando os dois entram em contato, há uma colisão e o personagem não passa.

Permitindo apenas um ataque por vez

No fim da última aula, percebemos que se apertarmos a barra de espaço várias vezes rapidamente, o personagem irá executar a animação de ataque várias vezes rapidamente também.

Nesse caso o comportamento esperado é que a animação de ataque termine de ser executada até que o jogador possa pressionar o botão de espaço novamente.

Antes de tudo, abra o arquivo ‘src/player.ts’ e cole o seguinte trecho de código:

export interface Player extends Phaser.Physics.Arcade.Sprite {
  isAttacking?: boolean;
}

Esse código cria o tipo “Player” extendendo o “Sprite” do Phaser. E adiciona a propriedade “isAttacking” para sinalizar se o jogador está realizando algum ataque.

Em seguida, abra o arquivo ‘src/controls.ts’ e dentro da função ‘attack’ adicione a seguinte linha de código:

player.isAttacking = true;

Aqui estamos setando a propriedade “isAttacking” do player, pra gente saber se ele já está realizando um ataque antes de disparar novamente a animação de ataque.

Importe o tipo”Player” que criamos e também configure o tipo do parâmetro “player” como “Player”, a função ficará assim:

const attack = (player: Player): void => {
  player.isAttacking = true;
  player.anims.play("player_attack", true);
};

Ainda dentro do arquivo ‘src/controls.ts’ na função ‘configControls’ vamos até a parte do código que captura o momento que o usuário pressiona a barra de espaço.

Adicione uma condicional. Caso a propriedade “isAttacking” do player seja falsa, execute a função de ataque:

if (controls.space.isDown) {
  if (!player.isAttacking) {
    attack(player, scene);
  }
  return;
}

Logo abaixo, dentro da mesma função é possível ver a execução da animação do player parado caso não execute nenhuma outra animação. Vamos precisar também verificar se o player está atacando antes deixar ele ficar parado. Caso esteja atacando, deverá primeiro finalizar o ataque atual:

if (!player.isAttacking) {
    player.anims.play("player_idle", true);
}

E também adicione o tipo “Player” para o parâmetro “player” da função.

A função completa deverá ficar assim:

export const configControls = (
  player: Player,
  controls: Phaser.Types.Input.Keyboard.CursorKeys,
  scene: Phaser.Scene
): void => {
  player.setVelocity(0);
  if (player.isAttacking) {
    return;
  }

  if (controls.down.isDown) {
    moveDown(player);
    return;
  }
  if (controls.up.isDown) {
    moveUp(player);
    return;
  }

  if (controls.right.isDown) {
    moveRight(player);
    return;
  }
  if (controls.left.isDown) {
    moveLeft(player);
    return;
  }
  if (controls.space.isDown) {
    if (!player.isAttacking) {
      attack(player);
    }
    return;
  }
  if (!player.isAttacking) {
    player.anims.play("player_idle", true);
  }
};

Capturando fim da animação

Agora volte ao arquivo ‘src/player.ts’, e no final da função “createAnimations” adicione o seguinte código:

player.on(
  "animationcomplete",
  function (animation, frame) {
    if (animation.key === "player_attack") {
      player.isAttacking = false;
    }
  },
  scene
);

Esse trecho de código é executado toda vez que uma animação do personagem finaliza. Nesse caso estamos conferindo se a animação em questão é a animação “player_attack”, caso seja, setamos a propriedade “isAttacking” como false.

E também configure o tipo do parâmetro “player” da função “createAnimations” parar “Player.
A função deverá estar assim:

const createAnimations = (scene: Phaser.Scene, player: Player): void => {
  scene.anims.create({
    key: "player_idle",
    frames: scene.anims.generateFrameNames("player_idle", {
      start: 0,
      end: 7,
    }),
    frameRate: 8,
    repeat: -1,
    yoyo: true,
  });

  scene.anims.create({
    key: "player_walk",
    frames: scene.anims.generateFrameNames("player_walk", {
      start: 0,
      end: 6,
    }),
    frameRate: 8,
    repeat: -1,
  });

  scene.anims.create({
    key: "player_attack",
    frames: scene.anims.generateFrameNames("player_attack", {
      start: 0,
      end: 3,
    }),
    frameRate: 12,
    repeat: 0,
  });

  player.on(
    "animationcomplete",
    function (animation, frame) {
      if (animation.key === "player_attack") {
        player.isAttacking = false;
      }
    },
    scene
  );
};

E dentro da função “createPlayer” adicione o parâmetro “player” onde é chamado a função “createAnimations”:

export const createPlayer = (scene: Phaser.Scene) => {
  const player = scene.physics.add.sprite(200, 200, "player_idle");
  createAnimations(scene, player);
  return player;
};
Resultado final Colisão Phaser

Conclusão – Colisão Phaser

Nessa aula aprendemos como fazer colisão no Phaser e também concertamos o comportamento de múltiplos ataques do personagem.

Se inscreva em nossa canal do Youtube

Você pode gostar...