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;
};
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.