ESP32 Defender: Building an Alien Attack-Inspired Game with T-Display ESP32 and Arduino IDE

ESP32 Defender: A Fun and Challenging Project Hello, community! Today, I want to share a project that has been incredibly fun and challenging: ESP32 Defender, a game inspired by the classic Alien Attack, developed using a T-Display ESP32 with Wi-Fi and Bluetooth, a 1.14-inch LCD screen, and programmed in the Arduino IDE. This project started as a hobby, but it quickly became a journey of learning and growth. I want to share a bit about the process, the challenges I faced, and how it inspired me to create other projects. Let’s dive in! What is ESP32 Defender? ESP32 Defender is a defense game where the player controls a spaceship and must destroy waves of approaching aliens. The game was developed to run on a T-Display ESP32, which combines an ESP32 microcontroller with a 1.14-inch LCD screen. The programming was done entirely in the Arduino IDE, using libraries like TFT_eSPI to control the display and ESP32TimerInterrupt to handle timing events. Components and Tools Used T-Display ESP32: A powerful module that integrates Wi-Fi, Bluetooth, and an LCD screen. 1.14-inch LCD Screen: Responsible for displaying the game with simple but effective graphics. Arduino IDE: The development environment used to program the ESP32. Libraries: TFT_eSPI: For controlling the LCD screen. ESP32TimerInterrupt: For managing timed events, like alien movement. Challenges and Learnings Memory Management: The ESP32 has limited resources, so I had to optimize the code to ensure the game ran smoothly without crashes. LCD Screen Control: Learning to use the TFT_eSPI library was challenging but rewarding. Game Logic: Creating the mechanics for alien movement, shooting, and collisions required a lot of logical thinking. Future Feature Integration: I’m already planning to add Wi-Fi and Bluetooth support to enable multiplayer mode or even an online leaderboard. Why Stepping Out of Your Comfort Zone Matters This project showed me how rewarding it is to tackle technical challenges and learn new things. When starting a project like this, it’s common to face problems that seem insurmountable, but overcoming each obstacle brings tremendous growth. Moreover, ESP32 Defender inspired me to start other projects, such as a home automation system and an IoT device for plant monitoring. As I make progress, I plan to share more about these ideas here in the community. Next Steps Add Wi-Fi and Bluetooth support to create a multiplayer mode. Improve graphics and add sound effects. Publish the source code on GitHub so others can contribute and learn from the project. Final Thoughts Projects like ESP32 Defender are more than just hobbies; they’re opportunities for personal and professional growth. They teach us to be persistent, creative, and to face problems head-on. What about you? Have you worked on any projects that pushed you out of your comfort zone? Share your experiences in the comments! Let’s exchange ideas and inspire more people to create amazing things. Useful Links ESP32 Documentation TFT_eSPI Library Arduino IDE Hashtags ESP32 #Arduino #IoT #GameDevelopment #TFT #PersonalGrowth #DIY #ElectronicsProjects #DevCommunity Code cpp #include TFT_eSPI tft = TFT_eSPI(); // Cria o objeto TFT int playerX = 60; // Posição inicial X do jogador (centro horizontal) int playerY = 220; // Posição inicial Y do jogador (próximo à parte inferior) int playerWidth = 16; // Largura do jogador int playerHeight = 8; // Altura do jogador uint16_t playerColor = TFT_GREEN; // Cor do jogador // Definições para o tiro int bulletX = 0; // Posição X do tiro int bulletY = 0; // Posição Y do tiro int bulletSpeed = 6; // Velocidade do tiro (aumentada) bool bulletActive = false; // Estado do tiro (ativo ou não) uint16_t bulletColor = TFT_RED; // Cor do tiro // Definições para os inimigos int enemyRows = 3; // Número de linhas de inimigos int enemyCols = 6; // Número de colunas de inimigos int enemyWidth = 12; // Largura do inimigo int enemyHeight = 8; // Altura do inimigo int enemySpacing = 20; // Espaçamento entre inimigos int enemySpeed = 2; // Velocidade dos inimigos (aumentada) uint16_t enemyColor = TFT_WHITE; // Cor dos inimigos bool enemies[3][6] = { // Matriz de inimigos (vivos ou mortos) {true, true, true, true, true, true}, {true, true, true, true, true, true}, {true, true, true, true, true, true} }; int enemyY = 10; // Posição Y inicial dos inimigos // Placar int score = 0; // Contador de naves inimigas destruídas const int maxScore = 20; // Número máximo de naves para vencer // Estado do jogo bool gameOver = false; // Indica se o jogo terminou bool gameWon = false; // Indica se o jogador venceu unsigned long restartTime = 0; // Tempo para reiniciar o jogo // Timers para controle de movimentação e disparo unsigned long lastEnemyMove = 0; unsigned long lastPlayerMove = 0; unsigned long lastBulletMove

Mar 16, 2025 - 21:31
 0
ESP32 Defender: Building an Alien Attack-Inspired Game with T-Display ESP32 and Arduino IDE

ESP32 Defender: A Fun and Challenging Project

Hello, community! Today, I want to share a project that has been incredibly fun and challenging: ESP32 Defender, a game inspired by the classic Alien Attack, developed using a T-Display ESP32 with Wi-Fi and Bluetooth, a 1.14-inch LCD screen, and programmed in the Arduino IDE.

This project started as a hobby, but it quickly became a journey of learning and growth. I want to share a bit about the process, the challenges I faced, and how it inspired me to create other projects. Let’s dive in!

What is ESP32 Defender?

ESP32 Defender is a defense game where the player controls a spaceship and must destroy waves of approaching aliens. The game was developed to run on a T-Display ESP32, which combines an ESP32 microcontroller with a 1.14-inch LCD screen. The programming was done entirely in the Arduino IDE, using libraries like TFT_eSPI to control the display and ESP32TimerInterrupt to handle timing events.

Components and Tools Used

  • T-Display ESP32: A powerful module that integrates Wi-Fi, Bluetooth, and an LCD screen.
  • 1.14-inch LCD Screen: Responsible for displaying the game with simple but effective graphics.
  • Arduino IDE: The development environment used to program the ESP32.
  • Libraries:
    • TFT_eSPI: For controlling the LCD screen.
    • ESP32TimerInterrupt: For managing timed events, like alien movement.

Challenges and Learnings

  1. Memory Management: The ESP32 has limited resources, so I had to optimize the code to ensure the game ran smoothly without crashes.
  2. LCD Screen Control: Learning to use the TFT_eSPI library was challenging but rewarding.
  3. Game Logic: Creating the mechanics for alien movement, shooting, and collisions required a lot of logical thinking.
  4. Future Feature Integration: I’m already planning to add Wi-Fi and Bluetooth support to enable multiplayer mode or even an online leaderboard.

Why Stepping Out of Your Comfort Zone Matters

This project showed me how rewarding it is to tackle technical challenges and learn new things. When starting a project like this, it’s common to face problems that seem insurmountable, but overcoming each obstacle brings tremendous growth.

Moreover, ESP32 Defender inspired me to start other projects, such as a home automation system and an IoT device for plant monitoring. As I make progress, I plan to share more about these ideas here in the community.

Next Steps

  • Add Wi-Fi and Bluetooth support to create a multiplayer mode.
  • Improve graphics and add sound effects.
  • Publish the source code on GitHub so others can contribute and learn from the project.

Final Thoughts

Projects like ESP32 Defender are more than just hobbies; they’re opportunities for personal and professional growth. They teach us to be persistent, creative, and to face problems head-on.

What about you? Have you worked on any projects that pushed you out of your comfort zone? Share your experiences in the comments! Let’s exchange ideas and inspire more people to create amazing things.

Useful Links
ESP32 Documentation
TFT_eSPI Library
Arduino IDE

Hashtags

ESP32 #Arduino #IoT #GameDevelopment #TFT #PersonalGrowth #DIY #ElectronicsProjects #DevCommunity

Here’s a snapshot of me playing ESP32 Defender, a retro-style space shooter game I developed using a T-Display ESP32 and a 1.14-inch LCD screen. The game features smooth controls, challenging alien waves, and a nostalgic vibe inspired by classic arcade games. It’s amazing how much fun you can pack into such a tiny screen!

Code


cpp
#include 

TFT_eSPI tft = TFT_eSPI();  // Cria o objeto TFT

int playerX = 60;  // Posição inicial X do jogador (centro horizontal)
int playerY = 220; // Posição inicial Y do jogador (próximo à parte inferior)
int playerWidth = 16; // Largura do jogador
int playerHeight = 8; // Altura do jogador
uint16_t playerColor = TFT_GREEN; // Cor do jogador

// Definições para o tiro
int bulletX = 0; // Posição X do tiro
int bulletY = 0; // Posição Y do tiro
int bulletSpeed = 6; // Velocidade do tiro (aumentada)
bool bulletActive = false; // Estado do tiro (ativo ou não)
uint16_t bulletColor = TFT_RED; // Cor do tiro

// Definições para os inimigos
int enemyRows = 3; // Número de linhas de inimigos
int enemyCols = 6; // Número de colunas de inimigos
int enemyWidth = 12; // Largura do inimigo
int enemyHeight = 8; // Altura do inimigo
int enemySpacing = 20; // Espaçamento entre inimigos
int enemySpeed = 2; // Velocidade dos inimigos (aumentada)
uint16_t enemyColor = TFT_WHITE; // Cor dos inimigos
bool enemies[3][6] = { // Matriz de inimigos (vivos ou mortos)
  {true, true, true, true, true, true},
  {true, true, true, true, true, true},
  {true, true, true, true, true, true}
};
int enemyY = 10; // Posição Y inicial dos inimigos

// Placar
int score = 0; // Contador de naves inimigas destruídas
const int maxScore = 20; // Número máximo de naves para vencer

// Estado do jogo
bool gameOver = false; // Indica se o jogo terminou
bool gameWon = false; // Indica se o jogador venceu
unsigned long restartTime = 0; // Tempo para reiniciar o jogo

// Timers para controle de movimentação e disparo
unsigned long lastEnemyMove = 0;
unsigned long lastPlayerMove = 0;
unsigned long lastBulletMove = 0;
const unsigned long enemyMoveInterval = 300; // Intervalo de movimentação dos inimigos (ms)
const unsigned long playerMoveInterval = 50; // Intervalo de movimentação do jogador (ms)
const unsigned long bulletMoveInterval = 20; // Intervalo de movimentação do tiro (ms)

// Protótipos das funções
void drawPlayer();
void movePlayer(int dx);
void shoot();
void moveBullet();
void drawEnemies();
void moveEnemies();
void checkCollisions();
void drawScore();
void showGameOver();
void showGameWon();
void resetGame();
void drawCountdown();

void setup() {
  tft.init();  // Inicializa o display
  tft.setRotation(0);  // Ajusta a rotação do display para 0 graus (padrão)
  tft.fillScreen(TFT_BLACK);  // Limpa a tela com fundo preto

  // Configura os botões
  pinMode(0, INPUT_PULLUP); // Botão esquerdo (move para a esquerda e atira)
  pinMode(35, INPUT_PULLUP);  // Botão direito (move para a direita e atira)

  // Inicializa o jogador
  drawPlayer();

  // Inicializa os inimigos
  drawEnemies();

  // Exibe o placar inicial zerado
  drawScore();
}

void loop() {
  unsigned long currentTime = millis();

  if (!gameOver && !gameWon) {
    // Movimentação do jogador e disparo
    if (digitalRead(0) == LOW && currentTime - lastPlayerMove >= playerMoveInterval) { // Botão esquerdo
      movePlayer(-2); // Move para a esquerda
      lastPlayerMove = currentTime;
      if (!bulletActive) { // Atira apenas se não houver tiro ativo
        shoot();
      }
    }
    if (digitalRead(35) == LOW && currentTime - lastPlayerMove >= playerMoveInterval) { // Botão direito
      movePlayer(2); // Move para a direita
      lastPlayerMove = currentTime;
      if (!bulletActive) { // Atira apenas se não houver tiro ativo
        shoot();
      }
    }

    // Movimentação do tiro
    if (bulletActive && currentTime - lastBulletMove >= bulletMoveInterval) {
      moveBullet();
      lastBulletMove = currentTime;
    }

    // Movimentação dos inimigos
    if (currentTime - lastEnemyMove >= enemyMoveInterval) {
      moveEnemies();
      lastEnemyMove = currentTime;
    }

    // Verifica colisões
    checkCollisions();

    // Verifica se os inimigos alcançaram o final da tela
    if (enemyY + enemyRows * enemySpacing >= playerY) {
      gameOver = true;
      restartTime = currentTime + 15000; // Reinicia em 15 segundos
      showGameOver();
    }

    // Verifica se o jogador venceu
    if (score >= maxScore) {
      gameWon = true;
      restartTime = currentTime + 15000; // Reinicia em 15 segundos
      showGameWon();
    }
  } else {
    // Exibe a contagem regressiva para reiniciar o jogo
    drawCountdown();

    // Reinicia o jogo após 15 segundos
    if (currentTime >= restartTime) {
      resetGame();
    }
  }
}

// Função para desenhar o jogador
void drawPlayer() {
  tft.fillRect(playerX, playerY, playerWidth, playerHeight, playerColor);
}

// Função para mover o jogador
void movePlayer(int dx) {
  tft.fillRect(playerX, playerY, playerWidth, playerHeight, TFT_BLACK); // Apaga o jogador
  playerX += dx;
  if (playerX < 0) playerX = 0;
  if (playerX > tft.width() - playerWidth) playerX = tft.width() - playerWidth;
  drawPlayer(); // Redesenha o jogador
}

// Função para disparar o tiro
void shoot() {
  bulletX = playerX + playerWidth / 2 - 1;
  bulletY = playerY;
  bulletActive = true;
  tft.fillRect(bulletX, bulletY, 2, 4, bulletColor);
}

// Função para mover o tiro
void moveBullet() {
  tft.fillRect(bulletX, bulletY, 2, 4, TFT_BLACK); // Apaga o tiro
  bulletY -= bulletSpeed;
  if (bulletY < 0) {
    bulletActive = false;
  } else {
    tft.fillRect(bulletX, bulletY, 2, 4, bulletColor); // Redesenha o tiro
  }
}

// Função para desenhar os inimigos
void drawEnemies() {
  for (int i = 0; i < enemyRows; i++) {
    for (int j = 0; j < enemyCols; j++) {
      if (enemies[i][j]) {
        tft.fillRect(j * enemySpacing + 10, i * enemySpacing + enemyY, enemyWidth, enemyHeight, enemyColor);
      }
    }
  }
}

// Função para mover os inimigos
void moveEnemies() {
  tft.fillRect(0, enemyY, tft.width(), enemyRows * enemySpacing, TFT_BLACK); // Apaga os inimigos
  enemyY += enemySpeed;
  drawEnemies(); // Redesenha os inimigos
}

// Função para verificar colisões
void checkCollisions() {
  if (bulletActive) {
    for (int i = 0; i < enemyRows; i++) {
      for (int j = 0; j < enemyCols; j++) {
        if (enemies[i][j] && bulletX >= j * enemySpacing + 10 && bulletX <= j * enemySpacing + 10 + enemyWidth &&
            bulletY >= i * enemySpacing + enemyY && bulletY <= i * enemySpacing + enemyY + enemyHeight) {
          enemies[i][j] = false;
          bulletActive = false;
          tft.fillRect(j * enemySpacing + 10, i * enemySpacing + enemyY, enemyWidth, enemyHeight, TFT_BLACK);
          score++; // Incrementa o placar
          drawScore(); // Atualiza o placar na tela
        }
      }
    }
  }
}

// Função para exibir o placar
void drawScore() {
  tft.setTextSize(1);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.setCursor(10, 10);
  tft.print("Score: ");
  tft.print(score);
}

// Função para exibir a tela de Game Over
void showGameOver() {
  tft.fillScreen(TFT_BLACK);
  tft.setTextSize(2);
  tft.setTextColor(TFT_RED, TFT_BLACK);
  tft.setCursor(20, 80);
  tft.print("Game Over");
  tft.setCursor(20, 110);
  tft.print("Score: ");
  tft.print(score);
}

// Função para exibir a tela de vitória
void showGameWon() {
  tft.fillScreen(TFT_BLACK);
  tft.setTextSize(2);
  tft.setTextColor(TFT_GREEN, TFT_BLACK);
  tft.setCursor(20, 80);
  tft.print("Você Venceu!");
  tft.setCursor(20, 110);
  tft.print("Score: ");
  tft.print(score);
}

// Função para reiniciar o jogo
void resetGame() {
  tft.fillScreen(TFT_BLACK);
  gameOver = false;
  gameWon = false;
  score = 0;
  enemyY = 10;
  bulletActive = false;
  for (int i = 0; i < enemyRows; i++) {
    for (int j = 0; j < enemyCols; j++) {
      enemies[i][j] = true;
    }
  }
  drawPlayer();
  drawEnemies();
  drawScore();
}

// Função para exibir a contagem regressiva
void drawCountdown() {
  int remainingTime = (restartTime - millis()) / 1000; // Tempo restante em segundos
  tft.setTextSize(1);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.setCursor(20, 140);
  tft.print("Reiniciando em: ");
  tft.print(remainingTime);
  tft.print("s");
}