Criando estalactites que caem com Phaser JS - Game Devlog #20
Escrito em 28 de junho de 2021 - 🕒 4 min. de leituraFala galera! Estou finalmente de volta com outro devlog para o jogo Super Ollie, e hoje implementarei um elemento clássico de jogos de plataforma 2D, uma daquelas paradas que caem em cima do personagem quando ele passa por baixo, tipo uma estalactite.
Primeiro vou criar a classe stalactite
, bem simples:
class Spike extends GameObjects.Sprite {
constructor({
scene,
x = 0,
y = 0,
asset = 'spike',
enablePhysics = true,
addToScene = true,
frame,
name,
}) {
super(scene, x, y, asset, frame);
this.setOrigin(0, 1);
this.setName(name || asset);
if (addToScene) {
scene.add.existing(this);
}
if (enablePhysics) {
scene.physics.add.existing(this);
this.body.setAllowGravity(false);
this.body.setImmovable(true);
}
}
}
Agora no meu loop do dataLayer
(que expliquei no devlog 8) vou adicionar um caso para as estalactites (spike) a serem adicionadas.
// in the scene create function
const dataLayer = map.getObjectLayer('data');
dataLayer.objects.forEach((data) => {
const { x, y, name, height, width } = data;
if (name === 'spike') {
// TODO add spike logic here
}
});
Antes de entrar na lógica, meu objetivo é ser capaz de adicionar uma estalactite onde eu quiser pelo Tiled
, e o código irá automaticamente fazer a estalactite funcionar.
Como funciona uma estalactite dessas que caem em jogos de plataforma? Bem, quando o jogador passa por debaixo dela, ela cai nele, imediatamente ou alguns milissegundos depois, e para isso precisamos de um custom collider no chão, que quando o jogador interagir com ele, fará com que a estalactite caia.
Essa é a mecânica que eu quero automatizar, e para isso vou criar uma lógica para que a estalactite decida automaticamente onde o custom collider estará no mapa a partir da sua posição inicial.
if (name === 'spike') {
const spike = new Spike({
scene: this,
x,
y,
});
const customCollider = createColliderFromGameObjectToGround(
this,
spike,
mapSize,
this.mapData.dynamicLayers.ground
);
const { y: spikeY } = spike;
const collider = this.physics.add.overlap(
customCollider,
this.player,
() => {
// TODO handle spike falling
}
);
this.physics.add.overlap(
spike,
this.player,
() => {
// TODO handle spike hitting the player
}
);
}
A função createColliderFromGameObjectToGround
terá toda a lógica para obter a posição da estalactite e encontrar o tile do tipo ground
mais próximo de sua posição e colocar um custom collider lá.
Dentro desta nova função, vou precisar de 2 outras funções, createInteractiveGameObject
, que mostrei no devlog 15, e uma nova função chamada isBelowTileCollidable
, que receberá um Game Object e verificará se o tile abaixo desse objeto é colidível ou não.
const isBelowTileCollidable = (
gameObject,
dynamicLayer // single layer or GameObject Group
) => {
const { x, y, height } = gameObject;
const layers = dynamicLayer?.getChildren?.() || [dynamicLayer];
let tile = null;
let result = false;
layers.forEach((layer) => {
if (result) {
return;
}
tile = layer.getTileAtWorldXY(
x - 0.5,
y + height + 0.5
);
if (tile) {
result = tile?.properties?.collideUp;
}
});
return result;
};
Para a função createColliderFromGameObjectToGround
, eu irei criar um loop do tamanho da altura do mapa e, a partir da posição da estalactite, começar a buscar pelo tile do tipo ground
mais próximo, e também usarei a função some
para poder sair do loop quando um tile for encontrado.
const createColliderFromGameObjectToGround = (
scene,
gameobject,
mapSize,
mapLayer
) => {
let customCollider;
let result = false;
// TODO improve the size of this array
new Array(mapSize.height / gameobject.height).fill(null).some(
(val, index) => {
const posY = gameobject.y + (TILE_HEIGHT * (index + 1));
result = isBelowTileCollidable({
...gameobject,
y: posY,
},
mapLayer
);
if (result) {
const spikeHeight = gameobject.height + (posY - gameobject.y);
customCollider = createInteractiveGameObject(
scene,
gameobject.x,
// posY,
posY - spikeHeight + gameobject.height,
gameobject.width,
// spike.height,
spikeHeight,
gameobject.name
);
}
return result;
}
);
return customCollider;
};
Com essas 2 funções criadas, eu posso voltar ao meu loop do dataLayer
e trabalhar na lógica da estalactite, por enquanto quero que a estalactite caia quando o jogador interagir com o custom collider, e em seguida, fazer a estalactite reaparecer no teto após 2 segundos.
if (name === 'spike') {
const spike = new Spike({
scene: this,
x,
y,
});
const customCollider = createColliderFromGameObjectToGround(
this,
spike,
mapSize,
this.mapData.dynamicLayers.ground
);
// if for some reason the collider was not created, do nothing.
if (customCollider) {
const { y: spikeY } = spike;
const collider = this.physics.add.overlap(
customCollider,
this.player,
() => {
if (!spike.body.allowGravity) {
spike.body.setAllowGravity(true);
this.time.delayedCall(2000, () => {
spike.body.setAllowGravity(false);
spike.body.setAcceleration(0, 0);
spike.body.setVelocity(0, 0);
spike.setY(spikeY);
spike.setActive(true);
spike.setVisible(true);
});
}
}
);
this.physics.add.overlap(
spike,
this.player,
() => {
if (spike?.visible) {
this.gameHud.decreasePlayerLife(5);
spike.setActive(false);
spike.setVisible(false);
}
}
);
} else {
spike.destroy();
}
}
Com o modo de debug ativado, é assim que o custom collider aparece no jogo:
Isso é tudo por hoje, até o próximo devlog!
Tags:
- programação
- jogos
- javascript
- phaser
- phaser 3
- game devlog
- gamedev
- skate platformer
- super ollie vs pebble corp
- webpack
- tiled
Posts relacionados
Publicar um comentário
Comentários
Nenhum comentário.