Phaser Jogo RTS

Phaser 3 Jogo de RTS – Devlog

Phaser é um Framework para criação de jogos 2D com JavaScript. Eu já havia criado pequenas coisas com ele, mas nunca havia começado um grande. Resolvi então colocar meus conhecimentos de Age of Empires no papel e me dei a meta de criar um jogo de RTS. O resultado vai te surpreender!! Acompanha ai o meu progresso até o momento.

Phaser e Mapas Isométricos

À Princípio eu queria criar algo bem parecido com o Age of Empires mesmo, mapas isométricos e artes em 3D. No entanto, eu me deparei com minha primeira dificuldade. Phaser não foi feito pra criar mundos isométricos! Dá pra criar, mas vai exigir um trabalho muuuito maior. Eu até cheguei a fazer o projeto isométrico, olha só!

Phaser 3 Mapa Isométrico
Phaser Mapa Isométrico

Para criar o mapa Isométrico eu utilizei um plugin Bem bacana PLUGIN , porém este plugin foi desenvolvido para o Phaser 2 e eu estou criando meu jogo com Phaser 3. Isso foi um grande problema. Eu até achei um port para a versão 3 que foi o que utilizei, Plugin Isométrico Phaser 3, mas este port para a nova versão estava pela metade e eu fiquei pensando em como eu iria utilizar recursos nativos do framework com este plugin. Foi ai que eu desisti da ideia Isométrica, achei que não valeria o esforço e se eu estava com dificuldades no começo, imagina lá na frente pra um plugin que não tem suporte. 🙃

Projeto 2D

Depois de bater bastante a cabeça com o Phaser Isométrico eu resolvi mudar para o 2D. Eu já havia criado aqui um joguinho de RPG onde um personagem matava um Morceguinho, mas nada mais que isso. Peguei alguns conceitos deste projeto, inclusive os Sprites e comecei por ali.

Phaser RPG
Phaser RPG

À Esta altura a minha preocupação era vencer o algoritmo de Pathfinding A* , no meu jogo de RPG eu já havia utilizado ele, mas o meu personagem se movimentava de uma forma muito estranha. Parecia que as informações do meu GRID (Matriz que armazena os dados do Mapa) estavam todas bagunçadas e a mesma coisa aconteceu no RTS. De fato, elas estavam. Descobri que o programa que eu uso para criar mapas, Tiled, exporta os dados bagunçados para eu acessar no Phaser. Para resolver isso eu tive que exportar os dados como CSV, desta forma o personagem passou a desviar dos edifícios da forma esperada.

Pathfinding A*

Look & Feel

Uma das coisas que mais chamam a atenção em um jogo, sem sombra de dúvidas é o visual. Eu não sou nenhum artista, muuuito longe disso. Cabeça de programador geralmente não é boa pra criar design. Eu queria usar algum Sprite maneiro pra deixar o jogo bem atrativo, a final a gente consome um produto com os olhos primeiro. Depois de alguns dias de pesquisa eu encontrei os Sprites do jogo que eu me inspirei pra criar este RTS chamado Little War Game, se você não jogou, jogue! Vale muito à pena. E olha só no que deu esta brincadeira.

Look & Feel de RTS

Assim fica muito mais agradável!! Me senti em um RTS De verdade.

Animação e Cardinalidade

A animação é um dos conceitos mais básicos de qualquer jogo que tenha ação de luta, porém, antes de Lutar é preciso caminhar, ou correr, e esta foi uma das primeiras implementações que eu fiz. A grande sacada aqui é que precisamos saber a posição atual e a posição de destino (Apenas o seguinte ao atual), com estas informações criamos um angulo entre origem e destino e baseado no ângulo em PI Radianos, animamos o Sprite na direção Correta.

Nosso algoritmo de Pathfinding nos retorna um Array com posições x e y, utilizamos as informações do Array e multiplicamos pelo tamanho do Tile ou Ladrilho do mapa para ter a posição exata em pixels do mapa.

// Exemplo de retorno do A*
path = [
 0: {x: 20, y: 21}
 1: {x: 21, y: 22}
 2: {x: 22, y: 23}
 3: {x: 23, y: 23}
 4: {x: 24, y: 23}
 5: {x: 25, y: 23}
]

Com estas informações calculamos o ângulo e movimentamos o personagem.

let angle = Phaser.Math.Angle.Between(
  path[i].x * this.tileSize,
  path[i].y * this.tileSize,
  path[i + 1].x * this.tileSize,
  path[i + 1].y * this.tileSize
);

Mecânica de Combate e Barra de vida

À esta altura eu queria mais é ver as Unidades se matarem 😂, para isso eu precisava implementar a mecânica de combate. Lá no começo do projeto eu imaginava que eu iria utilizar de uma forma bem pesada as Colisões e HitBoxes, porém eu tomei uma abordagem um tanto diferente.

Minha abordagem de combate é baseada em eventos, sempre que eu clico com o botão direito do mouse em um personagem eu começo a atacar. Desta forma eu tenho mais controle e um ganho de performance violento no jogo já que colisões são baseadas em loops infinitos checando um raio de colisão. Vamos combinar que jogar com pelo menos 60FPS é Tudo de bom! Ninguém quer que o jogo trave em um momento decisivo.

Pra confirmar que o personagem realmente estava morto eu criei uma barra de HP utilizando os elementos gráficos do Fazer, é apenas uma linha que muda de cor conforme o HP do personagem vai descendo.

Mecânica de Combate

Seguindo Inimigo

E se o inimigo correr de você? Bom, ai você precisa correr atrás dele! hahaha… Essa foi fácil, é só verificar se o inimigo está no alcance do ataque, se estiver, continua atacando, se não, corre atrás dele e ataca somente quando estiver no alcance.

Buscando Inimigo

Steering Behavior e Movimentação em Grupo

De longe, a movimentação em Grupo foi o que me tomou mais tempo e pra minha infelicidade eu não consegui implementar. No entanto, aprendi muito no processo. Passei 1 mês estudando Vetores, Steering Behaviors, Flocking Simulation e toda sorte de artigo que falasse sobre o assunto. Me tornei quase um Mestre SQN 🤣. Sério, eu estudei muito e fiquei muito estressado por não conseguir, mas frustrar-se faz parte do dia-a-dia. Quando comecei a estudar Javascript foi a mesma frustração, mas veja só aonde eu cheguei, criando Jogos com JS, então eu sei que vou conseguir implementar esta funcionalidade mais pra frente.

Mas… Voltando ao assunto, o que eu queria implementar aqui era um algoritmo conhecido como Boids por Craig Reynolds, a ideia é fazer com que os meus agentes fossem autônomos e tivessem um comportamento independente ao se movimentar em grupo. Para Reynolds este comportamento imitaria o de pássaros e seu algoritmo implementa 3 conceitos de Steering Behavior, Separação, Alinhamento e Coesão.

Eu não vou me demorar muito, o próprio nome já diz muito. O Algoritmo garante que os agentes tenham uma separação, para não baterem uns nos outros. Alinhamento, Garante que eles andem de forma alinhada em um mesmo sentido e a Coesão é o que garante que eles fiquem o mais próximo possível uns dos outros. Olha só estes caras em ação.

See the Pen Flocking Simulation by Jonatan Pietroski dos Santos (@pietroski) on CodePen.

Bom, parece bem fácil no conceito, mas… sabe como é a lei de Murphy. Eu até consegui implementar, mas não ficou como eu queria. O primeiro vídeo é a movimentação em grupo que eu mesmo fiz e o segundo com Boids.

Sem Boids

Com Boids

Eu imagino que você conseguiu ver qual a diferença dos vídeos, o primeiro é muito mais programático com uma posição final parecendo uma matriz, já o segundo é muito mais natural. No fim das contas eu achei que era melhor seguir em frente do que ficar travado neste assunto.

Novos Sprites

Bom, não da pra ter um RTS somente com um tipo de unidade, embora a unidade arqueiro seja a minha preferida nos jogos de RTS. Eu adicionei um Cavaleiro, que mais uma vez, é original do Little War Game (LWG), todos os direitos são deles. Ah! Eu nem falei… A minha ideia é disponibilizar este projeto como um Template, ou Game Engine para criar Jogos de RTS com JavaScript. Acho que é uma forma de devolver um pouco pra comunidade tudo aquilo que eu já recebi.

Arqueiro e Cavaleiro

Interface de Usuário e Construções

Como eu já falei anteriormente eu não sou bom pra fazer coisas bonitas, a não ser que já seja pra copiar um design feito previamente. Bom, dito isto, eu tive que fazer uma Interface de usuário para que o jogador tenha um Feedback, Eu criei algumas barras e quadrados para mostrar as unidades selecionadas, status da unidade e para as construções, algumas ações que podem ser feitas, por enquanto apenas criar novas unidades. Para testar eu usei a construção mais básica do LWG um Castelo.

UI e Queue de Ações

Criação de Times e Sprites Dinâmicos

Até agora você viu apenas unidades da mesma cor atacando umas às outras e isso me incomodava demais. Eu queria fazer uma cena de Warfare com 2 Times diferentes, mas não com unidades idênticas. Comecei a estudar como isso seria possível e eu encontrei 2 opções, utilização de Shaders ou Criação de Novos Sprites de forma dinâmica.

Utilizar Shaders parecia ser mais pesada para o meu jogo e como eu preciso criar centenas de Unidades, eu certamente acabaria com um problema de performance. A melhor opção então foi a Criação de Sprites de forma dinâmica. Eu vou explicar como eu fiz isso num próximo post, mas basicamente funciona assim: Eu crio um novo Sprite baseado em um pré-existente e utilizo uma paleta de cores para mudar apenas o pixel com aquela cor específica.

Com toda esta parte pronta, eu pude fazer o meu tão aguardado showcase.

Showcase

Na tela eu tenho 250 Unidades, nenhuma delas apresentou lentidão. Fiquei bem satisfeito com o resultado.

Conclusão

Criar jogos com Javascript é muito divertido, ainda mais que eu não precisei aprender uma nova linguagem de programação para fazer isso. Estes quase 3 meses foram de um processo de auto conhecimento também, percebi que posso fazer o que eu quiser, apenas preciso de um pouco de esforço e tempo. Eu sei que coloquei uma meta bem ambiciosa para alcançar, mas “pra quem não sabe pra onde quer ir, qualquer lugar serve” e eu sei muito bem onde quero ir, quero criar grandes experiências e no processo quem sabe ganhar bastante dinheiro 🤑🤑🤑🤑.

Agradecimentos especiais

Quero agradecer de uma maneira especial ao Criador do Jogo Little War Game que disponibilizou os Sprites para criação de MODS, sem eles eu não poderia ter feito um protótipo tão interessante e completo. Também quero agradecer à comunidade da WEB que tem me ajudado muito nestes últimos dias a resolver os meus problemas, especialmente ao HTML5 Game Devs.

Referências

Comunidade de Jogos HTML5
Overlap Rectangle
Como mudar as cores da Unidades
RTS Group Movement
Little War Game
Feudal Wars
A to Z Guide to do Pathfinding
Super Combat Squadron Palettes
Understanding Steering Behaviors – Guia Completo com Pseudo Code
Palette Swap Phaser 3
AOE Sprites
Ferramenta para Criação de Paletas
Creating an Isometric View in Phaser 3
Open Game Art
Piskel – Criador de Pixel Art

Leave a comment

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Copyright © – Jonatan Pietroski