Criando um game de operações matemáticas com MetalJSX

Adriano Interaminense
Liferay Engineering Brazil
7 min readMar 7, 2018

Ola, me chamo Adriano Interaminense, Front-End Engineer na Liferay, e o objetivo deste meu primeiro post é mostrar como criar um game simples com operações matemáticas como soma, subtração e multiplicação, feito em MetalJSX.

preview do game Mathematics

Há um tempo atrás, eu construi este game em AngularJS, e agora estou criando um game do zero mas com as mesmas características, desta vez com MetalJS, porque estou curtindo muito trabalhar com MetalJS tanto em projetos pessoais como em projetos dentro da Liferay, além de utilizar ES6, criar componentes facilmente e atualiza-los utilizando incremental DOM.

Então MetalJS seria mais uma lib de componentes?
O MetalJS não é uma lib de componentes, é uma base para criar componentes, semelhante ao React. Você pode ver mais detalhes no site.

MetalJS foi projetado para ser agnóstico de template e já vem com suporte integrado tanto para SOY (Google Closure) quanto para JSX (React). Para este jogo, eu escolhi utilizar o JSX, mas ja tive a oportunidade de trabalhar também com o SOY e curti muito o server side rendering e a organização.

Antes de começarmos, estou disponibilizando o link do game hospedado no Wedeploy para que vocês possam testar. Eu criei duas versões do componente Mathematics, uma mais completa que está no link e outra versão mais simples que vou utilizar neste post para que não fique tão extenso e cansativo, focando no que realmente interessa que é o game feito em MetalJSX.

O código do game se encontra no meu github, quem se interessar para contribuir e melhorar o game, agradeceria muito!

Fork do metal-jsx-boilerplate no Github.

Eu criei um boilerplate para adiantar o processo de desenvolvimento do game. Este boilerplate utiliza metal-jsx, webpack e sass. E para utiliza-lo, basta fazer um fork do projeto no github e clonar para a sua máquina.

Estrutura de pastas

Para iniciarmos o desenvolvimento do game, iremos adicionar algumas pastas ao boilerplate. Dentro da pasta src iremos criar as pastas:

  • components (responsável por armazenar todos os componentes)
  • style (responsável por armazernar todos os arquivos .scss)
  • utils (responsável por armazenar todos os métodos úteis para nosso game)

A estrutura de pastas irá ficar da seguinte forma:

└── src
├── componentes
├── styles
└── utils

Criando componentes com MetalJSX

Vamos usar como exemplo o componente <Button />, ele também será necessário para o nosso game.

Dentro da pasta src vamos criar dois arquivos chamados index.js e Button.js. No arquivo index.js iremos importar e exportar o componente <Button /> da seguinte forma:

Iremos fazer isto para todos os componentes que criados.

Dentro do arquivo Button.js, vamos importar a classe Component da lib 'metal-jsx', criar a classe Button e exporta-la, desta forma:

Definindo a API do componente

Vamos criar duas propriedades,:

  1. isActive (tipo boolean)
  2. style do (tipo string)

Exemplo:

Para validar estas propriedades, vamos utilizar a classe Config do MetalJS. Para isso, vamos importa-la dentro do componente Button:

Agora vamos validar nossas propriedades. No código abaixo, eu estou dizendo que a propriedade isActive é um booleano e recebe o valor false, caso não seja passado nenhum valor para essa propriedade.

Neste outro exemplo, vou definir um valor padrão 'default' para a propriedade style. E nós só poderemos passar nesta propriedade, os valores que são pré-definidos utilizando o método oneOf da classe Config:

Neste próximo passo, vou utilizar o JSDoc para criar a documentação da API:

Implementando o componente

Depois que definimos a API, vamos implementar o componente Button. Para isso, vamos renderizar o componente utilizando o lifecycle render():

MetalJS Lifecycles
saiba mais sobre todos os lifecycles do MetalJS

Perceba que no código acima, foi utilizado uma lib utilitária chamada classnames para unir as classes passadas no método getCN. Desta forma, fica mais fácil o gerenciamento das classes css. Saiba mais no Github do projeto.

Outra observação é o uso deste trecho de código:

Adicionando este código, nós podemos passar qualquer outra propriedade que não foi definida para o componente Button. Por exemplo, eu posso passar para o meu componente Button uma propriedade onClick, que por causa do {…this.otherProps()} essa propriedade será adicionada ao meu componente:

E para finalizar nosso componente Button, utilizamos a propriedade children. Esta propriedade serve para que possamos passar qualquer outro conteúdo dentro do nosso componente. Exemplo:

Criando o componente principal Mathematics.js

A primeira coisa que iremos fazer é criar a classe para o nosso componente e importar algumas libs e utilitários necessários:

Definindo a API do componente Mathematics

Uma documentação sobre cada parâmetro da API

O exemplo de como será a API no state

PROPS e STATE do componente Mathematics

Precisamos adicionar o PROPS do componente Mathematics para receber os dados passados pela API e alguns STATES que deve ser usado no próprio componente:

Lifecycles do componente Mathematics

Antes de renderizar o componente, vamos utilizar o lifecycle created() para capturar o countdown e passar para o state, aplicar o nível e inicializar o game respectivamente:

Agora usando o lifecycle render(), vamos renderizar as telas do game que inicialmente, o método renderInitGame() irá renderizar primeiro porque definimos no lifecycle created().

Renderizando a tela init (renderInitGame)

Explicando o código abaixo, vamos utilizar o componente <Layout /> que basicamente facilita a criação da UI e faz com que o layout fique igual para todas as telas porque o markup html está no componente.

Utilizamos o método renderUIButtons() que renderiza os botões com os níveis para serem selecionados.

Adiciona um botão start para dar início ao game. Neste botão, vamos adicionar o evento de click que vai chamar o método _handleClickStartGame(). o método handleClickStartGame chama o método setStartGame().

O método setStartGame basicamente chama a próxima operação, esconde as telas init e finish, zera os erros e hits, remove a mensagem e inicia o tempo de contagem regressiva com o método setCountDown().

O método setNextOperation() passa para o STATE algumas informações necessárias para o cálculo das operações.

  • n1: primeiro número do cálculo.
  • n2: segundo número do cálculo.
  • operator: tipo do operador para realizar o cálculo.

E por fim, o código do renderInitGame():

Renderizando a tela start (renderStartGame)

Explicando o código abaixo, vamos armazenar o resultado da operação na constante result, o componente <Operation /> irá renderizar a UI da operação como mostra a figura abaixo:

Componente <Operation />

Iremos também criar um formulário com todos os números de 0 a 9 para que o usuário tente acertar o resultado. Outra forma de digitar os números é digitando o campo de texto.

Ao digitar um número, iremos chamar o método _handleClickButton()

Ao pressionar "enter" ou clicar no botão do tipo "submit", o método _handleClickValidateExpression() irá ser chamado

E por fim, vamos renderizar o método renderStartGame():

Renderizando a tela finish (renderFinishGame)

Ao finalizar a contagem regressiva através do método setCountDown(), a tela "finish" será renderizada. Explicando um pouco o código abaixo, novamente vamos renderizar os botoões para selecionar o nível, e mais um botão para reiniciar o game.

Ufaaaa…! Finalizamos o componente Mathematics. Não expliquei tudo que existe neste componente, apenas mostrei o principal, pois este post ficaria muito extenso e cansativo 😓. Então vou deixar aqui o link do componente completo.

E agora como faremos para renderizar este componente?

Com o componente principal finalizado, vamos importa-lo no nosso App.js e passar todos os parâmetros necessários para a API

Utilitários

Vamos adicionar um arquivo Utils.js dentro da pasta utils, ele será necessário no nosso componente Mathematics

Aplicando o visual do componente

Para finalizar o game, vamos adicionar os arquivos css disponibilizado neste link dentro da pasta style, que será responsável por aplicar o visual do game e torna-lo responsivo, podendo ser jogado por smartphones.

Mas para não deixar apenas um copy & paste na parte de CSS, dentro do arquivo _variables.scss (responsável pelas variáveis css do game) existe um array que nós podemos passar como parâmetros algumas informações como:

  1. Nome do nível adicionado na API do componente Mathematics;
  2. Cor que deve ser aplicada referente ao nível selecionado;

O resultado será como neste gif

Finalizo por aqui. Espero que eu tenha atingido o objetivo de conseguir passar um pouco do meu conhecimento para criar um game simples em MetalJSX.

Gostaria de receber feedbacks de qualquer coisa que vocês tenham achado neste post e no game. Deixo meu email e Github para qualquer dúvidas abaixo:

adriano.interaminense@liferay.com
https://github.com/interaminense

Obrigado e até a próxima!

community.liferay.com

--

--