RetroSketch2025
Published © GPL3+

Diy ArduBoy Micro

I wanted To Create an Arduboy That is Micro sized and The Dream came True! Just Follow my Steps And You'll Make one Yourself!!

IntermediateFull instructions provided4 hours66
Diy ArduBoy Micro

Things used in this project

Hardware components

Arduino micro PRO (HID)
×1
OLED Display 128x64 0.96 inch, I2C Interface
DIYables OLED Display 128x64 0.96 inch, I2C Interface
×1
Slide Switch
Slide Switch
×1
Pushbutton switch 12mm
SparkFun Pushbutton switch 12mm
×6
Buzzer
Buzzer
×1
9V battery (generic)
9V battery (generic)
×1
9V Battery Clip
9V Battery Clip
×1
Resistor 220 ohm
Resistor 220 ohm
×1
5 mm LED: Red
5 mm LED: Red
×1
6-pin Header & Gender Changer (5-pack)
Digilent 6-pin Header & Gender Changer (5-pack)
Optional.
×2

Software apps and online services

Arduino IDE
Arduino IDE
For Programming and Compiling To Development Boards.
KiCad
KiCad
For PCB design. Optional.

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
For Soldering Components.
Soldering Iron Holder, With Sponge
Soldering Iron Holder, With Sponge
For Placing The Soldering Iron into the Holder. (Used For Better Results)
Solder Flux, Soldering
Solder Flux, Soldering
using Soldering Flux makes The Solder wires Not Stick To The Next part or Components Solder wires.
Solder Wire, Lead Free
Solder Wire, Lead Free
IMPORTANT as We need To Solder Wires and Components With An Solder Wire.
3D Printer (generic)
3D Printer (generic)
I dont Have A 3D Printer But If someone Could Make A design For My Homemade Arduboy If not Then ill try to edit this Page and Add 3D Prints of My Design!

Story

Read more

Custom parts and enclosures

Arduboy Micro PCB Design In Fritzing GTL File:

i Also Made A PCB design If You Want To Make your own Arduboy!!
Just Use A Service Like PCBWay Or etc to get the PCB done!! GTL Version

Arduboy Micro PCB Design In Fritzing SVG File:

i Also Made A PCB design If You Want To Make your own Arduboy!!
Just Use A Service Like PCBWay Or etc to get the PCB done!! SVG Version

Schematics

Wiring Diagram Of Homemade Arduboy With Pro Micro And i2c Oled Made By Mr.Blinky:

This is An Wiring Schematic That Mr.Blinky Made but I couldnt make The wiring So Credits To Mr.Blinky For The Schematic! And Couldnt Find The Part Arduino Pro Micro on Fritzing..

Breadboard Wiring Diagram Of Homemade Arduboy With Arduino Micro And i2c Oled Made By Me:

This is A Breadboard Wiring Diagram Made by me That is Made in Fritzing!
Also This Wiring is For Arduino Micro with Standard Wiring not Alternative Wiring as i could not find the part Arduino Pro Micro in Fritzing...

Code

ArduBomber (An ArduBoy Game Made By Me!)

Arduino
This Is the Code For My Arduboy Game But You Can also make Your own Arduboy Game By Following The Steps of This Post: Part 1: https://community.arduboy.com/t/make-your-own-arduboy-game-part-1-setting-up-your-computer/7924 Part 2: https://community.arduboy.com/t/make-your-own-arduboy-game-part-2-printing-text/7925 And More On The Arduboy Site Community! Or just Find games In The Community and download the source codes of Arduboy games or the .ino File Of The Games.
  /*
  ArduBomber — Bomberman-inspired game for Pro Micro with Arduboy2
  Author: RetroSketch2025 (Mehrad) concept; implemented modularly for remixing

  Hardware: Pro Micro (ATmega32U4) + Arduboy-compatible 128x64 OLED & buttons
  Library: Arduboy2
*/

#include <Arduboy2.h>
#include <ArduboyTones.h>
#include <EEPROM.h>

Arduboy2 arduboy;
ArduboyTones tones(arduboy.audio.enabled);

// ------------------------ Constants ------------------------

enum GameState {
  STATE_BOOT,
  STATE_MENU,
  STATE_OPTIONS,
  STATE_LEVEL_INTRO,
  STATE_PLAY,
  STATE_GAMEOVER,
  STATE_SCORES
};

const uint8_t GRID_W = 18; // 18 columns (clips on 128px width)
const uint8_t GRID_H = 6;  // 6 rows
const uint8_t TILE = 8;    // 8x8 tiles

// Map codes
const uint8_t TILE_EMPTY = 0;
const uint8_t TILE_SOLID = 2;       // Non-destroyable (iron/main)
const uint8_t TILE_SOFT = 6;        // Destroyable
const uint8_t TILE_ENEMY = 3;       // Enemy marker in level layout

// Player tracked separately (code 4 reserved for reference)
const uint8_t PLAYER_CODE = 4;

// Bomb settings
const uint16_t BOMB_FUSE_MS = 4000; // 4 seconds fuse
const uint8_t BLAST_W = 4;          // 4x4 blast area width
const uint8_t BLAST_H = 4;          // 4x4 blast area height

// Levels
const uint8_t MAX_LEVELS = 8;
const uint8_t BASE_ENEMIES = 2;     // Level 1 has 2 enemies
// Level N has BASE_ENEMIES + (N-1) enemies

// EEPROM addresses (simple fixed layout)
const int EEPROM_ADDR_MAGIC = 0;
const int EEPROM_ADDR_SOUND = 1;
const int EEPROM_ADDR_SCORE_A = 2;
const int EEPROM_ADDR_SCORE_B = 6;
const int EEPROM_ADDR_SCORE_C = 10;
const int EEPROM_ADDR_SCORE_E = 14;

// Magic value for save validation
const uint8_t EEPROM_MAGIC = 0xA7;

// Auto-return to menu after scores timeout
const uint16_t SCORES_TIMEOUT_MS = 5000;

// ------------------------ Data structures ------------------------

struct Bomb {
  bool active = false;
  int8_t gx = 0;         // grid x
  int8_t gy = 0;         // grid y
  uint32_t placedMs = 0; // timestamp
};

struct Enemy {
  bool alive = false;
  int8_t gx = 0;
  int8_t gy = 0;
  int8_t dir = 0;        // 0=left,1=right,2=up,3=down
  uint32_t moveTickMs = 0;
};

struct Player {
  int8_t gx = 1;
  int8_t gy = 1;
};

// ------------------------ Globals ------------------------

GameState state = STATE_BOOT;
uint8_t currentLevel = 1;
bool soundOn = true;

// Scores categories as requested (A:, B:, C:, E:)
uint32_t scoreA = 0;
uint32_t scoreB = 0;
uint32_t scoreC = 0;
uint32_t scoreE = 0;

uint32_t stateEnterMs = 0;

// Map: 18x6 bytes
uint8_t mapGrid[GRID_W * GRID_H];

// Entities
Player player;
Bomb bomb;
const uint8_t MAX_ENEMIES = 12; // safety cap
Enemy enemies[MAX_ENEMIES];
uint8_t enemyCount = 0;

// ------------------------ Sprites (8x8) ------------------------
// Hand-coded bytes (1-bit per pixel, top-left origin)

const uint8_t PROGMEM SPR_PLAYER[8] = {
  0b01110000,
  0b00111100,
  0b01101010,
  0b01101010,
  0b01111110,
  0b10111101,
  0b01100110,
  0b01110111
};

const uint8_t PROGMEM SPR_ENEMY[8] = {
  0b11110010,
  0b01011101,
  0b01101011,
  0b01101010,
  0b01111110,
  0b10111101,
  0b01100110,
  0b01110111
};

const uint8_t PROGMEM SPR_BOMB[8] = {
  0b01000000,
  0b10100000,
  0b01011000,
  0b00100100,
  0b01011010,
  0b00100100,
  0b00011000,
  0b00000000
};

const uint8_t PROGMEM SPR_SOLID[8] = {
  0b11111111,
  0b10000001,
  0b10111101,
  0b10000001,
  0b10111101,
  0b10000001,
  0b10111101,
  0b11111111
};

const uint8_t PROGMEM SPR_SOFT[8] = {
  0b01010101,
  0b10101010,
  0b01010101,
  0b10101010,
  0b01010101,
  0b10101010,
  0b01010101,
  0b10101010
};

// ------------------------ Menu Aseprite placeholders ------------------------
// Replace with your exported 128x64 bitmaps from Aseprite.
// For now, we draw text over these dummy images.

const uint8_t PROGMEM BMP_MENU_PLAY[1024] = { /* 128x64 placeholder: all zero */ };
const uint8_t PROGMEM BMP_MENU_OPTIONS[1024] = { /* 128x64 placeholder: all zero */ };

bool menuOnPlay = true; // toggle between PLAY and OPTIONS with up/down or left/right

// ------------------------ Utility ------------------------

uint8_t clampU8(int v, uint8_t lo, uint8_t hi) {
  if (v < (int)lo) return lo;
  if (v > (int)hi) return hi;
  return (uint8_t)v;
}

uint8_t idx(int x, int y) {
  return (uint8_t)(y * GRID_W + x);
}

bool inBounds(int x, int y) {
  return x >= 0 && y >= 0 && x < GRID_W && y < GRID_H;
}

// ------------------------ EEPROM ------------------------

void saveEEPROM() {
  EEPROM.update(EEPROM_ADDR_MAGIC, EEPROM_MAGIC);
  EEPROM.put(EEPROM_ADDR_SOUND, soundOn);
  EEPROM.put(EEPROM_ADDR_SCORE_A, scoreA);
  EEPROM.put(EEPROM_ADDR_SCORE_B, scoreB);
  EEPROM.put(EEPROM_ADDR_SCORE_C, scoreC);
  EEPROM.put(EEPROM_ADDR_SCORE_E, scoreE);
}

void loadEEPROM() {
  uint8_t magic = EEPROM.read(EEPROM_ADDR_MAGIC);
  if (magic == EEPROM_MAGIC) {
    EEPROM.get(EEPROM_ADDR_SOUND, soundOn);
    EEPROM.get(EEPROM_ADDR_SCORE_A, scoreA);
    EEPROM.get(EEPROM_ADDR_SCORE_B, scoreB);
    EEPROM.get(EEPROM_ADDR_SCORE_C, scoreC);
    EEPROM.get(EEPROM_ADDR_SCORE_E, scoreE);
  } else {
    soundOn = true;
    scoreA = scoreB = scoreC = scoreE = 0;
    saveEEPROM();
  }
}

// ------------------------ Sound helpers ------------------------

void beepPlaceBomb() {
  if (!soundOn) return;
  tones.tone(600, 60);
}

void beepExplosion() {
  if (!soundOn) return;
  tones.tone(140, 120);
  tones.tone(90, 80);
}

void beepEnemyPop() {
  if (!soundOn) return;
  tones.tone(440, 60);
}

// ------------------------ Level generation ------------------------

void clearMap() {
  for (uint8_t y = 0; y < GRID_H; y++) {
    for (uint8_t x = 0; x < GRID_W; x++) {
      mapGrid[idx(x,y)] = TILE_EMPTY;
    }
  }
}

void addBorderSolids() {
  for (uint8_t x = 0; x < GRID_W; x++) {
    mapGrid[idx(x,0)] = TILE_SOLID;
    mapGrid[idx(x,GRID_H-1)] = TILE_SOLID;
  }
  for (uint8_t y = 0; y < GRID_H; y++) {
    mapGrid[idx(0,y)] = TILE_SOLID;
    mapGrid[idx(GRID_W-1,y)] = TILE_SOLID;
  }
}

// Simple pattern: checker soft blocks leaving player start free
void addSoftBlocksPattern() {
  for (uint8_t y = 1; y < GRID_H-1; y++) {
    for (uint8_t x = 1; x < GRID_W-1; x++) {
      if ((x + y) % 2 == 0) mapGrid[idx(x,y)] = TILE_SOFT;
    }
  }
  // Player start area clear
  mapGrid[idx(1,1)] = TILE_EMPTY;
  mapGrid[idx(2,1)] = TILE_EMPTY;
  mapGrid[idx(3,1)] = TILE_EMPTY;
  mapGrid[idx(1,2)] = TILE_EMPTY;
  mapGrid[idx(2,2)] = TILE_EMPTY;
  mapGrid[idx(1,3)] = TILE_EMPTY;
}

void spawnEnemiesForLevel(uint8_t level) {
  enemyCount = clampU8(BASE_ENEMIES + (int)(level - 1), 0, MAX_ENEMIES);
  uint8_t placed = 0;
  for (uint8_t i = 0; i < MAX_ENEMIES; i++) {
    enemies[i].alive = false;
  }
  // Place enemies in empty tiles away from player start
  for (uint8_t y = 1; y < GRID_H-1 && placed < enemyCount; y++) {
    for (uint8_t x = GRID_W-2; x >= 1 && placed < enemyCount; x--) {
      if (mapGrid[idx(x,y)] == TILE_EMPTY && !(x <= 2 && y <= 2)) {
        enemies[placed].alive = true;
        enemies[placed].gx = x;
        enemies[placed].gy = y;
        enemies[placed].dir = (placed % 4);
        enemies[placed].moveTickMs = millis();
        placed++;
      }
      if (x == 1) break; // avoid underflow for uint8_t loop
    }
  }
}

// ------------------------ State management ------------------------

void enterState(GameState s) {
  state = s;
  stateEnterMs = millis();
}

void startLevel(uint8_t level) {
  currentLevel = level;
  clearMap();
  addBorderSolids();
  addSoftBlocksPattern();
  player.gx = 1; player.gy = 1;
  bomb.active = false;
  spawnEnemiesForLevel(level);
  enterState(STATE_LEVEL_INTRO);
}

// ------------------------ Movement and collisions ------------------------

bool isWalkable(int gx, int gy) {
  if (!inBounds(gx, gy)) return false;
  uint8_t t = mapGrid[idx(gx,gy)];
  return (t == TILE_EMPTY || t == TILE_SOFT); // walking over soft block allowed? Typically no; set empty only if you want strict.
}

bool isSolidBlock(int gx, int gy) {
  if (!inBounds(gx, gy)) return true;
  return mapGrid[idx(gx,gy)] == TILE_SOLID;
}

// Enemies move stepwise at intervals
void updateEnemies() {
  const uint16_t MOVE_INTERVAL_MS = 300;
  for (uint8_t i = 0; i < enemyCount; i++) {
    if (!enemies[i].alive) continue;
    if (millis() - enemies[i].moveTickMs < MOVE_INTERVAL_MS) continue;
    enemies[i].moveTickMs = millis();

    int nx = enemies[i].gx;
    int ny = enemies[i].gy;
    switch (enemies[i].dir) {
      case 0: nx--; break;
      case 1: nx++; break;
      case 2: ny--; break;
      case 3: ny++; break;
    }
    // Bounce if blocked
    if (!inBounds(nx, ny) || mapGrid[idx(nx,ny)] == TILE_SOLID || mapGrid[idx(nx,ny)] == TILE_SOFT) {
      enemies[i].dir = (enemies[i].dir + 1) % 4;
    } else {
      enemies[i].gx = nx;
      enemies[i].gy = ny;
    }
  }
}

// If enemy touches player => game over
bool checkPlayerEnemyCollision() {
  for (uint8_t i = 0; i < enemyCount; i++) {
    if (!enemies[i].alive) continue;
    if (enemies[i].gx == player.gx && enemies[i].gy == player.gy) {
      return true;
    }
  }
  return false;
}

// ------------------------ Bomb and explosion ------------------------

void placeBomb() {
  if (bomb.active) return;
  bomb.active = true;
  bomb.gx = player.gx;
  bomb.gy = player.gy;
  bomb.placedMs = millis();
  beepPlaceBomb();
}

void explodeBomb() {
  if (!bomb.active) return;

  // Center blast — destroy the bomb tile itself
  uint8_t &centerTile = mapGrid[idx(bomb.gx, bomb.gy)];
  if (centerTile == TILE_SOFT) {
    centerTile = TILE_EMPTY;
    scoreB += 10;
  }

  // Check if player is on bomb tile
  if (player.gx == bomb.gx && player.gy == bomb.gy) {
    enterState(STATE_GAMEOVER);
    bomb.active = false;
    beepExplosion();
    return;
  }

  // Check if any enemy is on bomb tile
  for (uint8_t i = 0; i < enemyCount; i++) {
    if (enemies[i].alive && enemies[i].gx == bomb.gx && enemies[i].gy == bomb.gy) {
      enemies[i].alive = false;
      scoreE += 25;
    }
  }

  // Directions: left, right, up, down
  const int dirs[4][2] = { {-1,0}, {1,0}, {0,-1}, {0,1} };
  const uint8_t blastLength = 3; // blast range in each direction

  for (uint8_t d = 0; d < 4; d++) {
    int x = bomb.gx;
    int y = bomb.gy;
    int dx = dirs[d][0];
    int dy = dirs[d][1];

    for (uint8_t step = 0; step < blastLength; step++) {
      x += dx;
      y += dy;
      if (!inBounds(x, y)) break;

      uint8_t &tile = mapGrid[idx(x, y)];

      // Stop at iron wall
      if (tile == TILE_SOLID) break;

      // Destroy soft block
      if (tile == TILE_SOFT) {
        tile = TILE_EMPTY;
        scoreB += 10;
        break; // blast stops after destroying soft block
      }

      // Check for enemy
      for (uint8_t i = 0; i < enemyCount; i++) {
        if (enemies[i].alive && enemies[i].gx == x && enemies[i].gy == y) {
          enemies[i].alive = false;
          scoreE += 25;
        }
      }

      // Check for player
      if (player.gx == x && player.gy == y) {
        enterState(STATE_GAMEOVER);
        bomb.active = false;
        beepExplosion();
        return;
      }
    }
  }

  bomb.active = false;
  beepExplosion();

  // Check if all enemies are dead
  bool anyAlive = false;
  for (uint8_t i = 0; i < enemyCount; i++) {
    if (enemies[i].alive) {
      anyAlive = true;
      break;
    }
  }

  if (!anyAlive) {
    scoreA += 100;
    scoreC += 50;
    if (currentLevel < MAX_LEVELS) {
      startLevel(currentLevel + 1);
    } else {
      enterState(STATE_SCORES);
      saveEEPROM();
    }
  }
}

void updateBomb() {
  if (!bomb.active) return;
  if (millis() - bomb.placedMs >= BOMB_FUSE_MS) {
    explodeBomb();
  }
}

// ------------------------ Input ------------------------

void handlePlayInput() {
  if (arduboy.pressed(LEFT_BUTTON)) {
    int nx = player.gx - 1;
    if (nx >= 0 && mapGrid[idx(nx, player.gy)] == TILE_EMPTY) player.gx = nx;
  }
  if (arduboy.pressed(RIGHT_BUTTON)) {
    int nx = player.gx + 1;
    if (nx < GRID_W && mapGrid[idx(nx, player.gy)] == TILE_EMPTY) player.gx = nx;
  }
  if (arduboy.pressed(UP_BUTTON)) {
    int ny = player.gy - 1;
    if (ny >= 0 && mapGrid[idx(player.gx, ny)] == TILE_EMPTY) player.gy = ny;
  }
  if (arduboy.pressed(DOWN_BUTTON)) {
    int ny = player.gy + 1;
    if (ny < GRID_H && mapGrid[idx(player.gx, ny)] == TILE_EMPTY) player.gy = ny;
  }

  if (arduboy.justPressed(A_BUTTON)) {
    placeBomb();
  }
}

// ------------------------ Rendering ------------------------

void drawTile8(int x, int y, const uint8_t *spr) {
  // x,y in pixels
  for (uint8_t row = 0; row < 8; row++) {
    uint8_t bits = pgm_read_byte(spr + row);
    for (uint8_t col = 0; col < 8; col++) {
      if (bits & (0x80 >> col)) {
        arduboy.drawPixel(x + col, y + row, 1);
      }
    }
  }
}

void drawMap() {
  // Clip 18x6 to 128x64; only columns that fit
  uint8_t maxCols = min(GRID_W, (uint8_t)(WIDTH / TILE)); // 16 columns visible
  uint8_t maxRows = min(GRID_H, (uint8_t)(HEIGHT / TILE)); // 8 rows visible

  // Center horizontally if fewer than GRID_W draw
  int xOffsetTiles = 0;
  if (maxCols < GRID_W) {
    // We’ll show leftmost portion; could scroll or center. For simplicity: left aligned.
    xOffsetTiles = 0;
  }

  for (uint8_t gy = 0; gy < maxRows; gy++) {
    for (uint8_t gx = 0; gx < maxCols; gx++) {
      uint8_t t = mapGrid[idx(gx + xOffsetTiles, gy)];
      int px = gx * TILE;
      int py = gy * TILE;
      if (t == TILE_SOLID) {
        drawTile8(px, py, SPR_SOLID);
      } else if (t == TILE_SOFT) {
        drawTile8(px, py, SPR_SOFT);
      }
    }
  }

  // Draw enemies
  for (uint8_t i = 0; i < enemyCount; i++) {
    if (!enemies[i].alive) continue;
    int gx = enemies[i].gx - xOffsetTiles;
    if (gx < 0 || gx >= (int)maxCols) continue;
    drawTile8(gx * TILE, enemies[i].gy * TILE, SPR_ENEMY);
  }

  // Draw player
  int pgx = player.gx - xOffsetTiles;
  if (pgx >= 0 && pgx < (int)maxCols) {
    drawTile8(pgx * TILE, player.gy * TILE, SPR_PLAYER);
  }

  // Draw bomb
  if (bomb.active) {
    int bgx = bomb.gx - xOffsetTiles;
    if (bgx >= 0 && bgx < (int)maxCols) {
      drawTile8(bgx * TILE, bomb.gy * TILE, SPR_BOMB);
    }
  }
}

void drawLevelIntro() {
  arduboy.setCursor(30, 28);
  arduboy.print(F("LEVEL "));
  arduboy.print(currentLevel);
}

void drawMenu() {
  arduboy.setCursor(24, 16);
  arduboy.print(F("ARDUBOMBER"));
  arduboy.setCursor(40, 32);
  arduboy.print(menuOnPlay ? F("> PLAY") : F("  PLAY"));
  arduboy.setCursor(40, 42);
  arduboy.print(!menuOnPlay ? F("> OPTIONS") : F("  OPTIONS"));
}

void drawOptions() {
  arduboy.setCursor(36, 20);
  arduboy.print(F("OPTIONS"));
  arduboy.setCursor(24, 36);
  arduboy.print(F("SOUND: "));
  arduboy.print(soundOn ? F("ON") : F("OFF"));
  arduboy.setCursor(24, 48);
  arduboy.print(F("(B to Menu)"));
}

void drawGameOver() {
  arduboy.setCursor(32, 26);
  arduboy.print(F("GAME OVER"));
}

void drawScores() {
  arduboy.setCursor(6, 8);
  arduboy.print(F("SCORES:"));
  arduboy.setCursor(6, 22);
  arduboy.print(F("A: "));
  arduboy.print(scoreA);
  arduboy.setCursor(6, 32);
  arduboy.print(F("B: "));
  arduboy.print(scoreB);
  arduboy.setCursor(6, 42);
  arduboy.print(F("C: "));
  arduboy.print(scoreC);
  arduboy.setCursor(6, 52);
  arduboy.print(F("E: "));
  arduboy.print(scoreE);

  arduboy.setCursor(74, 54);
  arduboy.print(F("L+R reset"));
}

// ------------------------ State update ------------------------

void updateMenu() {
  // Toggle selection with up/down or left/right
  if (arduboy.justPressed(UP_BUTTON) || arduboy.justPressed(DOWN_BUTTON) ||
      arduboy.justPressed(LEFT_BUTTON) || arduboy.justPressed(RIGHT_BUTTON)) {
    menuOnPlay = !menuOnPlay;
  }
  if (arduboy.justPressed(A_BUTTON)) {
    if (menuOnPlay) {
      startLevel(1);
    } else {
      enterState(STATE_OPTIONS);
    }
  }
}

void updateOptions() {
  // Toggle sound with A
  if (arduboy.justPressed(A_BUTTON)) {
    soundOn = !soundOn;
    saveEEPROM();
  }
  // Back to menu with B
  if (arduboy.justPressed(B_BUTTON)) {
    enterState(STATE_MENU);
  }
}

void updateLevelIntro() {
  if (millis() - stateEnterMs > 1000) {
    enterState(STATE_PLAY);
  }
}

void updatePlay() {
  handlePlayInput();
  updateEnemies();
  updateBomb();

  if (checkPlayerEnemyCollision()) {
    enterState(STATE_GAMEOVER);
  }
}

void updateGameOver() {
  // Short pause then go to scores
  if (millis() - stateEnterMs > 1000) {
    enterState(STATE_SCORES);
    saveEEPROM();
  }
}

void updateScores() {
  // Reset scores if LEFT+RIGHT pressed simultaneously
  if (arduboy.pressed(LEFT_BUTTON) && arduboy.pressed(RIGHT_BUTTON)) {
    scoreA = scoreB = scoreC = scoreE = 0;
    saveEEPROM();
  }
  // Auto-return after timeout
  if (millis() - stateEnterMs > SCORES_TIMEOUT_MS) {
    enterState(STATE_MENU);
  }
}

// ------------------------ Setup & loop ------------------------

void setup() {
  arduboy.begin();
  arduboy.bootLogo();
  arduboy.setFrameRate(60);
  loadEEPROM();
  enterState(STATE_MENU);
}

void loop() {
  if (!arduboy.nextFrame()) return;
  arduboy.pollButtons();
  arduboy.clear();

  switch (state) {
    case STATE_MENU:
      updateMenu();
      drawMenu();
      break;
    case STATE_OPTIONS:
      updateOptions();
      drawOptions();
      break;
    case STATE_LEVEL_INTRO:
      updateLevelIntro();
      drawLevelIntro();
      break;
    case STATE_PLAY:
      updatePlay();
      drawMap();
      break;
    case STATE_GAMEOVER:
      updateGameOver();
      drawGameOver();
      break;
    case STATE_SCORES:
      updateScores();
      drawScores();
      break;
    default:
      enterState(STATE_MENU);
      break;
  }

  arduboy.display();
}

Credits

RetroSketch2025
1 project • 0 followers
Im an Arduino and ESP32 Professional which i Make Projects Like ArduBoy and Gaming Handhelds!! I Started By using Arduino or ESP32!
Thanks to Kevin Bates and Mr.Blinky.

Comments