Joshua Koskie
Published © LGPL

16x2 LCD Space Shooter game!

easy, and very fun game for the 16x2 display for Arduino. Challenge yourself and enjoy the experience!

BeginnerShowcase (no instructions)1 hour192
16x2 LCD Space Shooter game!

Things used in this project

Story

Read more

Schematics

screenshot_17-2-2026_195138_www_tinkercad_com_gAurmIxRIj.jpeg

Code

space_game

C/C++
Code
#include <LiquidCrystal.h>



// Matches your Pin Setup: RS, EN, D4, D5, D6, D7
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// Pins (Adjusted to match your Pong button pins)
const int btnMove = 8;   // Button to switch rows
const int btnShoot = 7;  // Button to fire
const int buzzer = 10;   // Optional: Connect to Pin 10 and GND

// Player Variables
int playerRow = 0;

// Bullet Variables
int bulletCol = -1; 
int bulletRow = -1;

// Enemy Variables
int enemyCol = 15;
int enemyRow = 0;

// Game State
int score = 0;
int wave = 1;
int enemiesInWave = 0;
int gameSpeed = 350; // Milliseconds between enemy moves
unsigned long lastMoveTime = 0;

void setup() {
  lcd.begin(16, 2);
 
  
  pinMode(btnMove, INPUT_PULLUP);
  pinMode(btnShoot, INPUT_PULLUP);
  pinMode(buzzer, OUTPUT);

  lcd.clear();
  lcd.print("SPACE SHOOTER");
  lcd.setCursor(0, 1);
  lcd.print("GET READY!");
  delay(2000);
}

void loop() {
  // 1. INPUT: Move Player (Toggle between Row 0 and Row 1)
  if (digitalRead(btnMove) == LOW) {
    playerRow = !playerRow; 
    delay(200); // Simple debounce
  }

  // 2. INPUT: Shoot Bullet
  if (digitalRead(btnShoot) == LOW && bulletCol == -1) {
    bulletCol = 1; // Start right in front of ship
    bulletRow = playerRow;
    tone(buzzer, 1000, 50); // Short beep
  }

  // 3. LOGIC: Move Bullet
  if (bulletCol != -1) {
    bulletCol++;
    if (bulletCol > 15) bulletCol = -1; // Remove if off-screen
  }

  // 4. LOGIC: Move Enemy based on gameSpeed
  if (millis() - lastMoveTime > gameSpeed) {
    enemyCol--;
    lastMoveTime = millis();

    // Check if Enemy hit the player
    if (enemyCol == 0 && enemyRow == playerRow) {
      gameOver();
    }

    // If enemy escapes to the left
    if (enemyCol < -1) { 
      gameOver();
    }
  }

  // 5. LOGIC: Collision Detection (Bullet hits Enemy)
  // We check if bullet is at enemyCol or enemyCol + 1 because enemy is "<}"
  if (bulletRow == enemyRow && (bulletCol == enemyCol || bulletCol == enemyCol + 1)) {
    bulletCol = -1; // Remove bullet
    score++;
    enemiesInWave++;
    tone(buzzer, 500, 50);
    spawnEnemy();

    // Wave Progression Logic (10 enemies per wave)
    if (enemiesInWave >= 10) {
      nextWave();
    }
  }

  drawGame();
  delay(50); // Small delay for stability
}

void drawGame() {
  lcd.clear();
  
  // Draw Player Ship
  lcd.setCursor(0, playerRow);
  lcd.print(">");

  // Draw Bullet
  if (bulletCol != -1) {
    lcd.setCursor(bulletCol, bulletRow);
    lcd.print("-");
  }

  // Draw Enemy (The <} character)
  if (enemyCol >= 0 && enemyCol <= 15) {
    lcd.setCursor(enemyCol, enemyRow);
    lcd.print("<}");
  }
}

void spawnEnemy() {
  enemyCol = 15;
  enemyRow = random(0, 2);
}

void nextWave() {
  wave++;
  enemiesInWave = 0;
  gameSpeed = gameSpeed - 50; // Make it faster
  if (gameSpeed < 100) gameSpeed = 100; // Speed cap

  if (wave > 6) {
    victory();
  } else {
    lcd.clear();
    lcd.setCursor(4, 0);
    lcd.print("WAVE ");
    lcd.print(wave);
    delay(1500);
  }
}

void gameOver() {
  lcd.clear();
  lcd.print("GAME OVER!");
  lcd.setCursor(0, 1);
  lcd.print("Score: ");
  lcd.print(score);
  tone(buzzer, 150, 1000);
  while(1); // Stop the game
}

void victory() {
  lcd.clear();
  lcd.print("VICTORY!");
  lcd.setCursor(0, 1);
  lcd.print("GALAXY SAVED!");
  while(1);
}

Credits

Joshua Koskie
1 project • 0 followers

Comments