Aprendendo orientação a objeto enquanto recrio o Breakout em Java

Recriando o jogo para atari Breakout e aplicando conceitos basicos de orientação a objeto usando java e fazendo rodar em um Raspberry PI model B.

Olá pessoal, me chamo Vaneska Sousa e estou no 2º semestre da faculdade de Sistemas e Mídias Digitais na Universidade Federal do Ceará. Ou seja, ainda estou engatinhando no aprendizado de desenvolvimento de software. 

E como projeto de final de semestre eu precisei criar um remake, em Java, do jogo Breakout de 1978 para Atari 2600 e – como foi uma experiência bastante desafiadora, mas igualmente vantajosa, já que pude aprender muita coisa, em especial como melhor aplicar os conceitos de orientação a objeto – decidi compartilhar com vocês esse desafio. 

Atari Breakout
Gameplay do Atari Breakout Original

Então no final desse artigo você terá um projeto estruturado com a raquete se movendo na tela.

Por que Java e por que TotalCross?

Todos os exercícios, tanto da cadeira de Matemática Aplicada à Multimídia quanto de Programação II, no meu curso, são ensinadas usando Processing Engine, que usa Java. Isso porque Java é uma ótima linguagem para aprender melhor os conceitos por ser fortemente tipada

Então, apesar de ser livre para escolher qualquer linguagem ou framework, optei por continuar no Java para tentar aplicar melhor o que eu aprendi nesse semestre conturbado (EAD é sempre na base do desespero), porém utilizando algum framework para não precisar fazer tudo do zero. Considerei seriamente usar Godot, entretanto eu pouco precisaria programar, além de não conhecer tão bem esta ferramenta. 

TotalCross é um framework que tem uma game engine simples e me permite gerar para Linux Arm e smartphones, o que poderia encantar os meus professores – além do fato de eu trabalhar nesta empresa, tendo acesso a desenvolvedores bem mais experientes que eu e que conhecem muito bem a plataforma para me ajudar -. Logo me pareceu o caminho mais seguro e, apesar de alguns perrengues, não me arrependo nem um pouco. É muito legal conseguir desenrolar todo o projeto e no final vê-lo rodando no celular e no Raspberry Pi

Atari Breakout

Minha versão do Atari Breakout feito com Java e TotalCross rodando no Raspberry Pi 3 Model B

Definindo mecânicas e estrutura do projeto Atari Breakout

Ao iniciar o desenvolvimento de qualquer que seja a aplicação, em especial quando se trata de jogos, precisamos ter em mente as principais features ou as mecânicas que serão implementadas. Para isso precisei assistir algumas muitas vezes a gameplay original e jogar algumas versões pelas internet.

No final eu obtive as seguintes mecânicas:

  1. Plataforma se move para esquerda ou para direita, conforme o comando do usuário. Quando chega numa extremidade, ela bata na “parede” (borda). 
  2. A bolinha bate na plataforma e volta na direção contrária a que veio;
  3. Cada vez que a bolinha bate em um “tijolo” (azul, verde, amarelo, laranja, vermelho) ele some.
  4. Quando se destrói todos os tijolos do nível 01, aparecem novos (na mesma posição do anterior) e a velocidade da bolinha aumenta. 
  5. Quando todos os tijolos do nível 02 são destruídos, o joguinho continua mesmo sem nenhum obstáculo na tela. 
  6. O jogo só acaba quando a bolinha cai.

E estes são os pontos que precisamos construir em nosso programa, com base neles já podemos notar que teremos 3 objetos principais: plataforma, bola e tijolos. Com isso já podemos ter uma noção da estrutura do nosso projeto.

Estrutura do Projeto

Pensei da seguinte maneira:

  • RunBreakoutApplication.java: Classe responsavél por chamar a classe que herda a Game Engine e roda o simulador;
  • Breakout.java: Nossa classe principal, que herda da GameEngine e “monta” o jogo, onde vamos chamar os objetos, definir posições e etc.; 
  • sprites: pacote onde irá todas as classes que são responsáveis pelos sprites (imagem e comportamento dos blocos, plataforma e bola);
  • util: pacotes com as classes usadas para facilitar a manutenção do projeto como constantes, inicialização de imagens e cores. 

Dito isso, vamos para o código! 

Mão no código

Para começar você precisa instalar o plugin TotalCross no VS Code e criar um projeto. Caso você esteja usando outra IDE pode seguir o passo a passo da documentação

Como eu estou usando o plugin, basta pressionar ctrl + P, digitar totalcross e clicar na opção create new project. Depois disso preenchi o que pede da seguinte maneira:

  • Nome da pasta selecionada: gameTC 
  • Artifactid: com.totalcross 
  • Nome do projeto: Breakout
  • Versão do TotalCross: 6.1.1 (a mais recente) 
  • Plataformas para build:  -android e -linux_arm (mas você pode selecionar as plataformas que desejar)

Ao gerar, se você for na classe RunBreakoutApplication.java clicar com o botão direito em cima dela e em seguida clicar em run irá abrir o simulador e aparecer o hello world na sua tela. Se isso acontecer, você está com o projeto Java com TotalCross devidamente criado.

Atari Breakout
Estrutura do projeto e hello world sendo exibido

Caso aconteça algum problema, você pode conferir a documentação ou perguntar na comunidade Totalcross no Discord ou Telegram

Depois do projeto devidamente configurado, o próximo passo é adicionar as imagens que serão usadas no projeto dentro de resources > sprites. Todos os arquivos estão aqui. E logo em seguida você pode criar dois pacotes chamados util e sprites. Vamos trabalhar neles nos próximos tópicos. 

No final a estrutura do seu projeto estará da seguinte maneira: 

Atari Breakout

Preparando o terreno

Para facilitar a manutenção do código e mudanças de imagens nas cores que você quer utilizar, vamos criar algumas classes que centralizam isso. Você pode ler mais sobre os benefícios que essa prática clicando aqui. Todas as classes que tem essa função vai dentro do pacote util. 

Constants.java

A primeira classe a ser criada é a de constantes, onde vai padrões de posicionamentos (como a borda entre a tela e onde a plataforma começa), velocidade, número de blocos e etc. Essa parte é boa para brincar, mudar os números e entender onde muda e porquê. É um ótimo exercício para quem está começando.

Se você quiser saber mais sobre o que é a unidade DP, recomendo essa leitura aqui.

Colors.java

Como o nome já diz, nessa classe vamos colocar as cores que iremos utilizar durante o jogo. Recomendo que coloque os nomes de acordo com o objetivo daquela cor, como background, color font e etc. Dessa forma fica mais fácil no futuro você atualizar a paleta de cores do seu projeto em uma só classe e não piorar a leiturabilidade do seu código. 

A seguir a classe utilizada neste projeto:

Images.java

A classe das imagens é sem dúvida a mais utilizada.

O método getScaledInstance() vai manipular a imagem para ficar de acordo com os valores que passamos através da constante. Vale a pena tentar alterar estes valores e observar o impacto no jogo. 

Recapitulando

Só para conferir, nosso projeto até agora está da seguinte maneira:

image 37

Criando a primeira sprite (plataforma)

Agora que estamos com o projeto devidamente estruturado, vamos criar nossa primeira classe dentro do pacote sprite, que será o paddle.java: a plataforma/bastão que é o objeto de interação com o usuário. 

Paddle.java

Essa classe deve herdar de Sprite, que é a classe responsável por esses objetos em jogos e são conceitos básicos que games engines já são estruturados para facilitar ao desenvolvedor. Com TotalCross não é diferente, ao herdar de sprites o framework já vai se preocupar em delimitar a movimentação dentro da tela, detectar colisão entre sprites e outros pontos importantes. Você pode conferir tudo em detalhes no javadoc. 

Se lembrar bem, o paddle se move em uma determinada velocidade no eixo X de acordo com o comando do usuário. Sendo assim, a classe Paddle.java será responsável por definir essa movimentação e qual a imagem (o “rosto”) que a sprite deve assumir. 

Veja o código abaixo:

Dentro do construtor indicamos qual a imagem (Images.paddle) e dentro do método move (uma facilidade do TotalCross) vamos receber a velocidade que foi definida no início da classe. Você pode depois experimentar outros valores e observar a movimentação.Então vamos testar se o paddle se movendo, caso seja para a esquerda é definimos que o centro da paddle naquele momento é ele mesmo menos a velocidade, e caso o contrário (para a direita), acrescentamos a velocidade. 

No final definimos a posição da sprite na tela.

Com isso nossa sprite está pronta, precisamos agora adicioná-la na tela e pegar qual é o movimento do usuário com o mouse para, assim, poder chamar o método move e criar a movimentação. Para isso vamos para a nossa classe principal, Breakout.java

Adicionando na tela e interação com o usuário

Como estamos construindo nossa game engine agora, teremos alguns pontos padrões para nos atentar. Para não ficar muito extenso, vou adicionar comentários no código. 

Entretanto, basicamente, você irá apagar o método initUI que foi gerado automaticamente e, ao invés de herdar de MainWindow, vai herdar de GameEngine. Com isso, aparecerá um “vermelhinho” no nome da sua classe, basta clicar na lâmpada ou no símbolo de sugestão da IDE que você usa e clicar na opção “add unimplemented methods”. Dessa forma, será gerado automaticamente o método onGameInit() que, como o nome já diz, será responsável pelo o momento que o jogo é inicializado, ou seja, a classe breakout é chamada. 

Outro ponto é que dentro do construtor você deve adicionar o tipo de estilo (MaterialUI), o tempo refresh na tela (70) e sinalizar que o jogo tem uma interface (gameHasUI = true;). Caso você queira saber mais sobre esses padrões, basta comentar aqui ou no Fórum da TotalCross!

Por último, mas não menos importante, para fazer o jogo realmente iniciar você tem que indicar isso através do this.start(); no onGameInit() e, então, se atentar para alguns outros métodos:

  • onGameInit(): como comentei anteriormente, esse é o primeiro método chamado. Nele, você deve inicializar as sprites e as imagens (Images.loadImages) e dizer ao jogo que pode começar;
  • onGameStart(): aqui é quando o jogo começa. É nesse método que iremos setar a posição inicial da plataforma (no centro da tela no eixo x e embaixo com uma borda no eixo Y);
  • onPaint(): é onde dizemos o que vai ser desenhado a cada frame, primeiro pintando o fundo de preto para não deixar rastros das sprites e, em seguida, dizendo que a sprite pode ser exibida (através do .show());
  • onPenDrag e onPenDown: são métodos que identificarão se o usuário está arrastando o dedo na tela (caso seja touch) ou movendo o mouse, enquanto o botão esquerdo é pressionado. É nesses métodos que mudaremos o movimento da paddle através do método setPos(), que irá acionar o método move lá em nossa classe Paddle.java. Note que o último parâmetro do método racket.setPos é true, justamente para delimitar a movimentação da paddle dentro da tela, de forma que ela nunca suma do campo de vista do usuário.

E agora, o código!

Executando

Para executar, basta ir na classe RunBreakoutApplication.java com o botão direito e clicar em run para ver como ficou (imagem abaixo);

image 1

E se você mudar os parâmetros na classe RunBreakoutApplication.java para a linha abaixo:

De forma que fique com o tamanho da tela do Raspberry Pi, você obterá o seguinte resultado:

image 2

E está pronto a primeira sprite e mecânica!

Próximos passos

E agora no próximo artigo vamos adicionar o sprite da bola e fazer a colisão, mas para isso preciso saber se vocês realmente gostaram e em que posso melhorar para no próximo conteúdo. Por isso conta nos comentários!

Se precisar de ajuda com qualquer coisa, pode nos chamar no grupo do discord ou publicar em nosso fórum que estou à disposição para ajudar! 

E se você chegou até aqui e pôs em prática qualquer etapa desse artigo, compartilhe comigo a experiência. Todo feedback é importante! E, se puder, favorite o TotalCross no GitHub pois ajuda o projeto a ter relevância na plataforma. 


Muito obrigada, pessoal! 

Saiba mais

Como utilizar o framework TotalCross na Raspberry Pi 3

Expansão de portas de um Raspberry Pi usando serial e Arduino – Parte 1

Criando um dashboard de automação industrial

Notificações
Notificar
guest
1 Comentário
recentes
antigos mais votados
Inline Feedbacks
View all comments
Fábio Gil
Fábio Gil
18/02/2021 10:38

Vaneska, gostei bastante da intro do seu projeto, e principalmente por estar bastante organizado por sinal. Totalcross parece bem legal de se trabalhar, lembro que cheguei a usar o Superwaba, em 2004, rodando no meu PalmTop, sei que faz um bom tempo mas já naquela época achava bem legal trabalhar com a plataforma. Por favor, continue a mostrar o desenvolvimento do seu projeto, estou gostando bastante e acompanhando sua evolução aqui. Parabéns pelo projeto! Abraços

Last edited 8 meses atrás by Fábio Gil

WEBINAR

Imagens de Ultrassom: Princípios e Aplicações

DATA: 26/10 ÀS 19:30 H