Esse site utiliza cookies
Nós armazenamos dados temporariamente para melhorar a sua experiência de navegação e recomendar conteúdo do seu interesse.
Ao utilizar os nossos serviços, você concorda com as nossas políticas de privacidade.
Esse site utiliza cookies
Nós armazenamos dados temporariamente para melhorar a sua experiência de navegação e recomendar conteúdo do seu interesse.
Ao utilizar os nossos serviços, você concorda com as nossas políticas de privacidade.
Postado em 03 janeiro 2023
Atualizado em 03 janeiro 2023
Pacman é um jogo clássico criado no início dos anos 80. A lógica do jogo é bastante simples, o usuário controla o pacman e precisa comer todas as frutas dentro do labirinto. Enquanto isso, inimigos irão percorrer o labirinto atrás do pacman, caso o usuário seja capturado pelo inimigo o jogo acaba.
Esse tutorial irá usar javascript e pixi.js para o desenvolvimento do jogo e pode ser um pouco difícil para iniciantes de programação. Caso esteja apenas começando, os tutoriais abaixo são recomendados:
Os jogos acima utilizam a biblioteca p5.js, que é extremamente flexível e de fácil compreensão, permitindo que o programador consiga desenvolver com agilidade.
Esse tutorial será dividido em algumas partes devido ao grande número de informação. Nesse primeiro tutorial, os conceitos básicos serão planejados e desenvolvidos. Esses conceitos são:
Como explicado na introdução, esse tutorial utiliza a biblioteca pixi.js. A linguagem de programação será javascript. Os recursos de imagem serão todos obtidos a partir de um único arquivo idêntico a imagem abaixo:
O arquivo acima está comprimido.
Outras ferramentas serão:
Ferramenta | Papel |
---|---|
Node.js | Ambiente para javascript |
npm | gerenciador de pacotes |
O planejamento será divido em duas partes:
O mapa será representado por um array contendo os valores zero e um. A representação dos valores é a seguinte:
Valor | Representação |
---|---|
0 | Espaço vazio |
1 | Parede |
O espaço vazio é o local onde o pacman pode se movimentar. Porém, caso haja a presença de uma parede um espaço, o pacman não conseguirá se movimentar para esta direção.
Cada espaço terá 46px de altura e tamanho, assim como o pacman também terá os mesmos valores. Assim, podemos concluir que o tamanho da tela pode ser configurado usando as seguintes lógicas como base:
Por exemplo, observe o array abaixo:
// O tamanho do array abaixo é 5x5
let map = [
[1,1,1,1,1],
[1,0,0,0,1],
[1,0,1,0,1],
[1,0,0,0,1],
[1,1,1,1,1],
];
O array acima possui 5 valores na horizontal (x) e 5 valores na vertical (y). Logo, o tamanho desse array é de 5x5. Podemos concluir que ao utilizar o array acima como base, o tamanho da tela será de 230x230.
O pacman terá um tamanho de 46x46 com velocidade de 3px por frame. A movimentação do pacman é efetuada pelas setas do teclado. Antes de se locomover de fato para a direção pressionada pelo usuário, será checado se o pacman pode ou não se mover para o espaço desejado, ou seja, o programa irá verificar o futuro movimento e concluirá se esse movimento é possível ou não.
Como mostra a imagem acima, pacman pode ir para as direções direita e esquerda, porém não pode se mover para as direções cima e baixo.
Como planejado, o mapa será desenhado com um array contendo zeros e uns.
const stage = [
[1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 0, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1],
];
O mapa será bastante simples no primeiro tutorial, uma vez que queremos desenvolver e testar do modo mais simples possível.
A altura e a largura da tela irá se basear no array acima, como mostra nas seguintes linhas:
const spriteWidth = 46;
const spriteHeight = 46;
const gameWidth = stage[0].length * spriteWidth;
const gameHeight = stage.length * spriteHeight;
A largura da tela é o tamanho do array na primeira linha e a altura é a quantidade de linhas dentro do primeiro array. Esses valores serão multiplicados pelo tamanho de cada bloco que é 46.
Pelo fato da variável “stage” ser um array multidimensional, duas repetições (loops) serão necessárias.
for (let row = 0; row < stage.length; row++) {
for (let column = 0; column < stage[row].length; column++) {
if (stage[row][column] == 0) {
continue;
}
graphics.beginFill(0xDE3249);
graphics.drawRect(column * spriteWidth, row * spriteHeight, spriteWidth, spriteHeight);
graphics.endFill();
}
}
A repetição (loop) interna vai verificar todos os valores da esquerda para a direita em cada linha. Cada bloco irá ter um tamanho de 46x46. Caso o valor seja 1, a parede é desenhada na tela, caso o valor seja 0, a repetição simplesmente continua e não desenha nada na tela.
O pacman é controlado pelo usuário com as setas de direção do teclado. Assim como cada bloco do jogo, o pacman tem um tamanho de 46x46. O pacman será desenvolvido em 3 etapas:
Ao se deslocar o pacman irá abrir e fechar a boca continuamente. Esse efeito dará a impressão de que o pacman está caminhando. Quando o pacman estiver parado, sua boca para de abrir e fechar e permanece do modo que parou.
Felizmente, o pixi.js já possui várias funcionalidades para animação. Primeiro, iremos criar as texturas das quatro direções da imagem.
const baseTexture = PIXI.Texture.from('resources/characters.png');
const pacmanTextures = [];
pacmanTextures['right'] = [];
pacmanTextures['right'].push(new PIXI.Texture(baseTexture, new PIXI.Rectangle(spriteWidth * 10, 0, spriteWidth, 46)));
pacmanTextures['right'].push(new PIXI.Texture(baseTexture, new PIXI.Rectangle(spriteWidth * 11, 0, spriteWidth, 46)));
pacmanTextures['left'] = [];
pacmanTextures['left'].push(new PIXI.Texture(baseTexture, new PIXI.Rectangle(spriteWidth * 10, spriteHeight * 2, spriteWidth, 46)));
pacmanTextures['left'].push(new PIXI.Texture(baseTexture, new PIXI.Rectangle(spriteWidth * 11, spriteHeight * 2, spriteWidth, 46)));
pacmanTextures['up'] = [];
pacmanTextures['up'].push(new PIXI.Texture(baseTexture, new PIXI.Rectangle(spriteWidth * 10, spriteHeight * 3, spriteWidth, 46)));
pacmanTextures['up'].push(new PIXI.Texture(baseTexture, new PIXI.Rectangle(spriteWidth * 11, spriteHeight * 3, spriteWidth, 46)));
pacmanTextures['down'] = [];
pacmanTextures['down'].push(new PIXI.Texture(baseTexture, new PIXI.Rectangle(spriteWidth * 10, spriteHeight * 1, spriteWidth, 46)));
pacmanTextures['down'].push(new PIXI.Texture(baseTexture, new PIXI.Rectangle(spriteWidth * 11, spriteHeight * 1, spriteWidth, 46)));
Cada direção possuirá duas texturas, uma com a boca aberta e outra com a boca fechada. São duas texturas para cada direção e quatro direções ao todo, totalizando 8 texturas.
Uma vez que as texturas estão preparadas, criamos o sprite do pacman.
var pacman = new PIXI.CustomAnimatedSprite(pacmanTextures['right']);
pacman.animationSpeed = 0.1;
// O pacman inicia no centro da tela
pacman.x = (gameWidth - spriteWidth) / 2;
pacman.y = (gameHeight - spriteHeight) / 2;
pacman.play();
Em seguida, testamos a animação do pacman.
O deslocamento será efetuado com duas funções do javascript:
A função “onKeyDown” é executada quando o usuário pressiona alguma tecla. A função “onKeyUp” é executada quando o usuário solta um tecla. Com essas duas funções, sabemos se o usuário está com a tecla pressionada. Caso a tecla esteja pressionada, o pacman continua avançando na direção desejada.
Usaremos um simples objeto contendo 5 valores para gerenciar todas as direções do jogo.
const direction = {
"stand": 0,
"left": 1,
"up": 2,
"right": 3,
"down": 4,
};
O valor “stand” representa o pacman em repouso. Os outros valores indicam que o pacman está em movimento.
Além das direções, precisamos saber a direção atual do usuário. Isso pode ser feito com uma variável que armazena as coordenadas do pacman.
var currentDirection = {
horizontal: direction.stand,
vertical: direction.stand,
};
Como podemos observar, o pacman irá ser inicializado em repouso.
Toda vez que o usuário pressionar uma tecla, a função abaixo será executada:
function onKeyDown(key) {
if (key.code == "ArrowDown") {
currentDirection.vertical = direction.down;
}
if (key.code == "ArrowUp") {
currentDirection.vertical = direction.up;
}
if (key.code == "ArrowRight") {
currentDirection.horizontal = direction.right;
}
if (key.code == "ArrowLeft") {
currentDirection.horizontal = direction.left;
}
}
O código acima é bastante simples. Simplesmente renovamos a variável de direção atual. Ao largarmos a tecla, queremos que o pacman pare de avançar. Isso é feito com a função a seguir:
function onKeyUp(key) {
if (key.code == "ArrowLeft" || key.code == "ArrowRight") {
currentDirection.horizontal = direction.stand;
}
if (key.code == "ArrowUp" || key.code == "ArrowDown") {
currentDirection.vertical = direction.stand;
}
pacman.stop();
}
Ao largarmos a tecla, a animação do pacman também é pausada com o seguinte comando: pacman.stop(). Uma vez que já sabemos que direção o usuário quer se deslocar, a única coisa que falta é saber se o pacman pode ou não se deslocar na direção pressionada.
Na maioria dos jogos de pacman, o personagem principal é menor do que o tamanho de cada bloco. Nesse tutorial, pacman tem o mesmo tamanho que os blocos. Com isso, algoritmos de detecção de colisões podem ser diferentes dos algoritmos tradicionais.
O centro do pacman será o ponto principal de referência. Quando o usuário pressiona um tecla de direção, adicionaremos um valor que é a metade do tamanho de pacman para checar se pode ou não se movimentar para tal direção.
Direção | Cálculo |
---|---|
Direita | x + 23 |
Esquerda | x - 23 |
Cima | y - 23 |
Baixo | y + 23 |
O gráfico abaixo ilustra como funciona essa lógica:
Porém, existe um grande problema nesse método. A altura do pacman não é levada em consideração. Isso faz com que o usuário possa passar em espaços que contém paredes.
Para resolver esse problema, devemos calcular a direção desejada e o valor vertical do sprite de pacman como mostra na imagem abaixo:
O método acima quase funciona, porém surge outro problema. Por estar muito perto da parede, os pontos de detecção de colisões (ponto vermelho) detectam colisões. Isso acontece pelo fato de que o pacman tem o mesmo tamanho de um bloco. Se ele fosse um pouco menor, talvez não tivéssemos esse problema.
A resolução do problema é dividir a metade da altura pela sua própria metade, ou melhor dividir por 3.
Dessa forma, resolvemos todos os problemas de detecção de colisões.
// Ignora caso pacman esteja em repouso
if (currentDirection.horizontal == direction.stand && currentDirection.vertical == direction.stand) {
return;
}
// Calcula o centro do pacman
let currentVerticalPos = (pacman.y + (spriteHeight / 2)) / spriteHeight;
let currentHorizontalPos = (pacman.x + (spriteHeight / 2)) / spriteWidth;
// Calcula em qual bloco o pacman está situado
let roundedCurrentVerticalPos = Math.abs(Math.trunc(currentVerticalPos));
let roundedCurrentHorizontalPos = Math.abs(Math.trunc(currentHorizontalPos));
// Ativa a animação de abrir e fechar a boca do pacman
pacman.play();
// Caso a seta para direita for pressionada
if (currentDirection.horizontal == direction.right) {
// Calcula o próximo movimento do pacman usando a metade do tamanho na horizontal (23px)
let nextMov = currentHorizontalPos + ((spriteWidth / 2) / spriteWidth);
// Calcula o próximo movimento do pacman no sentido vertical embaixo (y + 7,6px)
let nextMovUp = currentVerticalPos + ((spriteHeight / 3) / spriteHeight);
// Calcula o próximo movimento do pacman no sentido vertical encima (y - 7,6px)
let nextMovDown = currentVerticalPos - ((spriteHeight / 3) / spriteHeight);
// Arredonda os valores para descobrir com bloco a porção do pacman está situada
let roundedNextMove = Math.abs(Math.trunc(nextMov));
let roundedNextMoveUp = Math.abs(Math.trunc(nextMovUp));
let roundedNextMoveDown = Math.abs(Math.trunc(nextMovDown));
// Calcula os limites da tela (direita)
let isRightBoundLimit = roundedCurrentHorizontalPos >= stage[roundedCurrentVerticalPos].length - 1;
// Detectores de colisão. Caso encotrem uma parede (1) cancela o movimento
let checkNextMovFromCenter = stage[roundedCurrentVerticalPos][roundedNextMove] == 1;
let checkNextMovFromCenterUp = stage[roundedNextMoveUp][roundedNextMove] == 1;
let checkNextMovFromCenterDown = stage[roundedNextMoveDown][roundedNextMove] == 1;
if (isRightBoundLimit || checkNextMovFromCenter || checkNextMovFromCenterUp || checkNextMovFromCenterDown){
return;
}
pacman.x += speed;
pacman.textures = pacmanTextures['right'];
}
...
A mesma lógica é aplicada para o resto das direções. Abaixo, temos uma pequena demonstração:
Nesse tutorial de pacman foram desenvolvidas as seguintes funcionalidades:
Apesar de parecer simples, pacman tem vários desafios. O detector de colisões é um exemplo. Talvez diminuir o tamanho do sprite de pacman pode facilitar o desenvolvimento do jogo devido a imprevistos nos cálculos do bloco atual que o usuário se encontra.
Diferente de outros detectores de colisões, o jogo do pacman calcula colisões com base do posicionamento atual do personagem.
Nos próximos tutoriais de pacman serão implementados:
O código do tutorial está disponível no Github.
Postagens mais vistas
Os 5 principais componentes do computador são a unidade de controle, unidade aritmética e lógica, memória, dispositivo de entrada e dispositivo de saída.
A porta é um número de 16 bits que é adicionado no final do endereço IP, insinuando qual aplicativo está vinculado e atuando nessa porta.
Rede local de computadores (LAN) é um conjunto de computadores ou dispositivos conectados uns aos outros de forma isolada em um pequeno local.