Programação de videogames em Java - Evil Genius #1

Estou aprendendo Java usando o livro Evil Genius, neste capítulo, vou explicar como criar um jogo simples.

sexta-feira, 6 de fevereiro de 2026

Desde o ano passado que resolvi aprender Java, a princípio o objetivo era apenas a trabalho. Mas logo percebi que pode ser muito divertido usar Java para fazer uma infinidade de outras coisas, como, por exemplo, jogos retrô.

Livro Deitel Java

Acontece que iniciei meus estudos por meio do livro Java – Como programar do Deitel que é um livro muito bom por sinal, mas não é bem o que eu queria pra agora, não para esse início.

Minha ideia é aprender a sintaxe do Java, conhecer algumas classes/métodos built-in úteis para daí começar a se aprofundar mais nos detalhes da linguagem. Só que o livro do Deitel é um pouco extenso haha, fiz alguns capítulos e acabei deixando de lado por um tempo.

Livro Evil Genius

Foi aí que encontrei o livro Evil Genius, que tinha a mesma premissa de ensinar Java, só que por meio da criação de jogos e de cara já me interessei pelo livro, tem uma didática bem direta ao ponto, além de ser bem prático.

E para continuar, vou criar um jogo simples de adivinhação de números com ranking usando os conceitos vistos até o projeto 5 do livro:

  • Variáveis
  • Operadores
  • Estruturas de controle
  • JOptionPane (entrada e saída de dados)
  • File I/O (leitura e gravação em arquivo)

Como vai funcionar

Ao iniciar o jogo, o programa deve gerar um número aleatório entre 1 e 100 e mostrar uma caixa de diálogo para o jogador tentar adivinhar o número.

Toda vez que o jogador errar, o programa deve mostrar uma mensagem informando se o número é maior ou menor que o número digitado e pedir para tentar novamente.

Caso o jogador acerte, o programa deve mostrar uma mensagem dizendo que ele acertou em X tentativas, em seguida, perguntar o nome do jogador e mostrar o ranking dos jogadores com os nomes e pontuações.

Números aleatórios e o loop do...while

Iremos construir esse jogo em um único arquivo chamado Game.java, nele, iremos criar uma classe chamada Game com um método main que será o ponto de entrada do programa:

public class Game {

    public static void main(String[] args) {}
}

Não irei fazer métodos auxiliares, iremos trabalhar apenas no método main para ficar mais simples de entender.

Vamos começar criando uma variável para armazenar o número aleatório entre 1 e 100 gerado pelo programa.

Podemos fazer isso usando a classe Math e o método random multiplicando por 100:

public class Game {

    public static void main(String[] args) {
      int random = Math.random() * 100;
    }
}

Porém, temos um erro aqui. O método random retorna um número entre 0 e 1; então, multiplicar por 100 não vai gerar um número entre 1 e 100. Para corrigir isso podemos converter o resultado para int usando casting e somar 1 para garantir que o número seja maior que 0:

public class Game {

    public static void main(String[] args) {
      int random = (int) (Math.random() * 100) + 1;
    }
}

Agora que temos o número aleatório gerado, vamos declarar duas variáveis importantes:

  • attempts Será um contador que irá armazenar o número de tentativas do jogador.
  • cont Será nossa condição de parada do loop que iremos usar no do...while.

E também já podemos escrever a estrutura base do loop do...while:

public class Game {

    public static void main(String[] args) {
      int random = (int) (Math.random() * 100) + 1;
      int attempts = 0;

      boolean cont;

      do {
        cont = true;
      } while(cont);
    }
}

Até agora temos o número aleatório gerado e as variáveis attempts e cont declaradas. Agora precisamos implementar a interação com o jogador para que ele possa tentar adivinhar o número gerado pelo programa.

Interagindo com o Jogador

Para interagir com o jogador, precisamos ler a entrada do teclado e comparar com o número gerado pelo programa. Para isso podemos usar a classe JOptionPane que permite exibir caixas de diálogo para entrada e saída de dados.

Vamos começar criando uma variável para armazenar a entrada do jogador chamada rawInput:

import javax.swing.JOptionPane;

public class Game {

    public static void main(String[] args) {
      int random = (int) (Math.random() * 100) + 1;
      int attempts = 0;

      boolean cont;

      do {
        cont = true;
        String rawInput = JOptionPane.showInputDialog("Adivinhe o Número Secreto! (1 a 100)");

      } while(cont);
    }
}

Agora precisamos converter a entrada do jogador para um número inteiro e compará-lo com o número gerado pelo programa. Mas antes de fazer isso, precisamos verificar se a entrada é válida.

Se não for, precisamos pedir novamente para o jogador.

Existem algumas validações que podemos fazer, mas para esse caso iremos apenas verificar se a entrada está vazia, caso sim, pedimos novamente:

import javax.swing.JOptionPane;

public class Game {

    public static void main(String[] args) {
      int random = (int) (Math.random() * 100) + 1;
      int attempts = 0;

      boolean cont;

      do {
        cont = true;
        String rawInput = JOptionPane.showInputDialog("Adivinhe o Número Secreto! (1 a 100)");

        do {
          rawInput = JOptionPane.showInputDialog("Adivinhe o Número Secreto! (1 a 100)");
        } while (rawInput.isEmpty());
      } while(cont);
    }
}

Agora podemos converter a entrada para um número inteiro e incrementar o contador de tentativas. Além disso, também temos que informar ao jogador se o número é maior ou menor que o número gerado pelo programa para ajudá-lo a acertar, podemos fazer isso com uma estrutura condicional:

import javax.swing.JOptionPane;

public class Game {

    public static void main(String[] args) {
      <...>

      do {
        cont = true;
        String rawInput = JOptionPane.showInputDialog("Adivinhe o Número Secreto! (1 a 100)");

        do {
          rawInput = JOptionPane.showInputDialog("Adivinhe o Número Secreto! (1 a 100)");
        } while (rawInput.isEmpty());

        int input = Integer.parseInt(rawInput);
        attempts++;

        if (input > random) {
            JOptionPane.showMessageDialog(null, "Muito alto");
        } else if (input < random) {
            JOptionPane.showMessageDialog(null, "Muito baixo");
        } else {
          cont = false;
        }
      } while(cont);

      JOptionPane.showMessageDialog(null, "Você acertou em " + attempts + " tentativas!");
    }
}

Se o jogador acertar o número secreto, significa que podemos parar o loop e, por isso, na instrução else atualizamos o valor de cont para false, indicando que o loop deve ser encerrado.

Até aqui o jogo já está funcionando bem, mas podemos melhorar ainda mais adicionando um ranking de pontuações simples.

Implementando o Ranking

Para implementar o ranking, precisamos armazenar as pontuações dos jogadores em algum lugar. O que iremos fazer é gravar essas pontuações em um arquivo de texto chamado ranking.txt, onde cada linha irá conter o nome do jogador e sua pontuação.

Ainda não temos o nome do jogador, então vamos pedir o nome do jogador antes de gravar a pontuação no arquivo:

String playerName = JOptionPane.showInputDialog("Qual seu nome? (Ranking)");

Para gravação e leitura do ranking, vamos utilizar as classes:

  • BufferedWriter e FileWriter para gravar as pontuações.
  • BufferedReader e FileReader para ler as pontuações.

Esse tipo de operação pode lançar erros ao tentar gravar ou ler o arquivo, para isso vamos usar a instrução try e indicar, na assinatura do método main, que ele pode lançar exceções, caso ocorra algum erro o programa será interrompido:

<...>

public class Game {

  public static void main(String[] args) throws Exception {
      <...>

      try (<...>) {
      }
  }
}

O BufferedWriter recebe um FileWriter como argumento, que é responsável por criar ou abrir o arquivo de texto. O FileWriter recebe o nome do arquivo e um booleano para indicar o modo de append. Isso vai permitir que as pontuações sejam adicionadas ao final do arquivo sem apagar o conteúdo existente:

String fileName = "ranking.txt";

try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName, true))) {
    writer.write(playerName + " – " + attempts + " tentativa(s)");
    writer.newLine();
}

O método .write recebe uma string como argumento e escreve essa string no arquivo de texto. O método .newLine() adiciona uma quebra de linha.

Com isso nosso programa já consegue gravar as pontuações no arquivo de texto. Mas ainda falta ler as pontuações do arquivo e exibi-las para o jogador.

Para ler, iremos usar as classes BufferedReader e FileReader.

Como podemos ter várias pontuações no arquivo, precisamos ler todas elas e armazená-las em uma espécie de lista; nesse caso, irei usar a StringBuilder:

StringBuilder ranking = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
    String line;
    while ((line = reader.readLine()) != null) {
        ranking.append(line).append("\n");
    }
}

O método .readLine retorna uma string com a próxima linha do arquivo de texto ou null se o arquivo estiver vazio ou se não tiver mais linhas para ler.

Já o método .append adiciona uma string ao final da string atual.

Agora só falta exibir o ranking usando a nossa StringBuilder e o nosso jogo estará pronto:

import javax.swing.*;
import java.io.*;

public class Game {

  public static void main(String[] args) throws Exception {
      <...>

      JOptionPane.showMessageDialog(null, "Você acertou em " + attempts + " tentativas!");

      String playerName = JOptionPane.showInputDialog("Qual seu nome? (Ranking)");
      String fileName = "ranking.txt";

      try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName, true))) {
          writer.write(playerName + " – " + attempts + " tentativa(s)");
          writer.newLine();
      }

      StringBuilder ranking = new StringBuilder();
      try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
          String line;
          while ((line = reader.readLine()) != null) {
              ranking.append(line).append("\n");
          }
      }

      JOptionPane.showMessageDialog(null, ranking.toString());
  }
}