Davide Gatti
Published © GPL3+

Space Hinvaders Color

A micro game with Color LED matrix where the pixels used for the entire game are about those of a single sprite of the original invader.

BeginnerFull instructions provided2 hours1,938
Space Hinvaders Color

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
8x8 WS2812 Color Dot Matrix Display
×2
Buzzer
Buzzer
Any piezoelectric buzzer is good for this project
×1
Proto Board
×1
Any momentary NA PCB Button
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron, Thermal controlled
Solder Wire LF
Smal Tweezer

Story

Read more

Custom parts and enclosures

Full Project Sources

Schematics

Nano Invaders Color.fzz

Nano Invaders Color.fzz

Code

Color Space Hnvaders

C/C++
// Space Hinvader by Davie Gatti of Survival Hacking Channel - wwww.survivalhacking.it
// A small Space Invaders game, with 2 MAX7219 8x8 dot matrix module or 2 WS2812 8x8 RGB LED Dot Matrix module.
// for a total of 8x16 pixels, that is a small game in a couple of original invaders sprites space.
// Due lack of display space, there are some game limitation. Aliens never shoot, and ther are not any saucers
// Also ther are not a barriers. Each time you kill all aliens, the game speed was increased.
// At the end of game, a Score is shown alternate with HigScore (underlined).
//

#include "LedControl.h"      // Need the library to be added from arduino ide
#include "pitches.h"         // Musical Notes include file 
#include "EEPROM.h"          // EEprom management include file

// Neopixel management
#include <FastLED.h>         // NeoPixel management include file 
#define LED_PIN  8           // Set I/O pin 8 for NeoPixel Display
#define COLOR_ORDER GRB      // Define color Order Array
#define CHIPSET     WS2811   // Define NeoPixel LED type
#define BRIGHTNESS 32        // Define NeoPixel Brightness
#define NUM_LEDS (8 * 16)    // Define LED quantity
CRGB LEDSS[NUM_LEDS + 1];    // Prepare array for RGB
CRGB* const leds(LEDSS + 1); // Fill RGB array

// Dot Matrix Led management
// pin 5 is connected to the DIN of MAX7219 pin 1
// pin 3 is connected to the CLK of MAX7219 pin 13
// pin 4 is connected to the LOAD of MAX7219 pin 12
// 2 as we are using 2 MAX7219
LedControl lc=LedControl(5,3,4,2); 

// Variables initialization
long Score = 0;          // Global Variable for Actual Score
long HighScore;          // Global Variable for HiScore 
long AlienSpeed = 70000; // Global Variable for Aliens speed
long AlienLevel = 0;     // Global Variable for Aliens speed on the level
long AlienMul = 5000;    // Global Variable for step speed increasing when shooting alien
long AlienDelay = 0;     // Global Variable for Aliens Delay
char AllKilled = 0;      // Global Variable that count active alien on screen
long HiscoreDelay =0;    // Global Variable used to flip score with hiscore
long CannonDelay = 0;    // Global Variable for Cannon Delay
byte CannonPos = 8;      // Global Variable for Cannon position
long Explode = 0;        // Global Variable for Explosion Marker
byte Ex = 0;             // Global Variable for Explosion X
byte Ey = 0;             // Global Variable for Explosion Y 
byte AlienXo = 16;       // Global Variable for Alien X previus (to remove aliens before redraw new position)
byte AlienYo = 0;        // Global Variable for Alien Y previus (to remove aliens before redraw new position)
byte AlienX = 16;        // Global Variable for Alien X
byte AlienY = 0;         // Global Variable for Alien Y
byte MaxAlienX = 0;      // Global Variable for Maximum Alien X position
byte MaxAlienY = 0;      // Global Variable for Maximum Alien Y position
byte MinAlienX = 0;      // Global Variable for Minimum Alien X position
byte AlienSound = 0;     // Global Variable for Alien Sound sequence
boolean AlienXs = false; // Global Variable for define alien direction march

long FireDelay = 0;      // Global Variable for Missile Delay
byte FireXPos = 8;       // Global Variable for Missile X
byte FireYPos = 1;       // Global Variable for Missile Y

const long CannonSpeed = 7000;    // Cannon speed
const long FireSpeed = 5000;      // Missile shooted speed
const byte FirstMax = 0;          // Addres of First DotMatrix display
const byte SecondMax = 1;         // Addres of Second DotMatrix display
const long ExplodeSpeed = 3000;   // Explosions Speed 
const long HiscoreSpeed = 100000; // Speed of flip from score to hiscore

const byte btnLeft = 12;          // Left Button assigned to IO 13 
const byte btnRight = 11;         // Right Button assigned to IO 12 
const byte btnFire = 10;          // Fire Button assigned to IO 11
const byte buzzer = 9;            // Buzzer assigned to IO 9

int win[] = {                     // Win melody array 
  NOTE_G3, NOTE_A4, NOTE_B4, NOTE_G3, NOTE_C4, 0, NOTE_C3
};
int noteDurations[] = {
  8, 8, 8, 8, 8, 8, 8
};



// Arrays for define X, Y and status (on off) of alien pattern
const byte MaxAliens = 16;        //  Number of aliens on screen (see following array)


byte AliensY[MaxAliens] = {       // Y coordinate of lighted led
  0,0,0,0, // row 0
  1,1,1,1, // row 1
  2,2,2,2, // row 2
  3,3,3,3, // row 3
  };
  
byte AliensX[MaxAliens] = {       // X coordinate of lighted led
  0,3,6,9,  // row 0
  1,4,7,10, // row 1
  2,5,8,11, // row 2
  0,3,6,9,  // row 3
  };
  
 boolean AliensStat[MaxAliens] = {// status (true=ON    false=OFF)
  true,true,true,true, // row 0
  true,true,true,true, // row 1
  true,true,true,true, // row 2
  true,true,true,true, // row 3
  };


// define a graphic array for draw number on dot matrix used with pointer multiplied by 5
byte Numbers[] =
{
   B00000111,  // Number 0
   B00000101,
   B00000101,
   B00000101,
   B00000111,
   B00000010,  // Number 1 
   B00000010,
   B00000010,
   B00000010,
   B00000010,
   B00000111,  // Number 2 
   B00000100,
   B00000111,
   B00000001,
   B00000111,
   B00000111,  // Number 3 
   B00000100,
   B00000110,
   B00000100,
   B00000111,
   B00000101,  // Number 4 
   B00000101,
   B00000111,
   B00000100,
   B00000100,
   B00000111,  // Number 5
   B00000001,
   B00000111,
   B00000100,
   B00000111,
   B00000111,  // Number 6
   B00000001,
   B00000111,
   B00000101,
   B00000111,
   B00000111,  // Number 7 
   B00000100,
   B00000100,
   B00000100,
   B00000100,
   B00000111,  // Number 8 
   B00000101,
   B00000111,
   B00000101,
   B00000111,
   B00000111,  // Number 9 
   B00000101,
   B00000111,
   B00000100,
   B00000111,
};


// Array space needed to be filled with graphic data for print the score
byte Score1[] =
{
   B00000000,  // Score 1
   B00000000,
   B00000000,
   B00000000,
   B00000000,
   B00000000,
   B00000000,
   B00000000,
};

byte Score2[] =
{
   B00000000,  // Score 2
   B00000000,
   B00000000,
   B00000000,
   B00000000,
   B00000000,
   B00000000,
   B00000000,
};


// Game Over Alien animation graphics array
byte invader1[] =
{
  B00011000,  // Invader 1
  B00111100,
  B01111110,
  B11011011,
  B11111111,
  B00100100,
  B01011010,
  B10100101
};
 
byte invader2[] =
{
  B00011000, // Invader 2
  B00111100,
  B01011010,
  B11111111,
  B11111111,
  B00100100,
  B01011010,
  B01000010
};

// Main Screen animation graphics array
byte SH1[] =
{
  B00100000,  // Main Screen 1
  B01000000,
  B11110011,
  B10111001,
  B11111011,
  B11101010,
  B00101011,
  B01100000,
};

byte SH2[] =
{
  B00000010,  // Main Screen 2
  B10100001,
  B10100111,
  B11101110,
  B10101111,
  B10101011,
  B00001010,
  B00000011,
};

byte SH3[] =
{
  B00110000,  // Main Screen 3
  B01000011,
  B11111001,
  B11111011,
  B10111010,
  B11101011,
  B00100000,
  B00110000,
};

byte SH4[] =
{
  B00000110,  // Main Screen 4
  B00000001,
  B10101111,
  B10101111,
  B11101110,
  B10101011,
  B10100010,
  B00000110,
};

// Startup function
void setup()
{
  // NeoPixel Initialization
  FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalSMD5050);
  FastLED.setBrightness( BRIGHTNESS );
  
  Serial.begin(9600);                             // Start Serial debug line
  Serial.println("Space Hinvader");               // Debug string
  pinMode(btnLeft, INPUT_PULLUP);                 // Define I/O data direction and pullups
  pinMode(btnRight, INPUT_PULLUP);
  pinMode(btnFire, INPUT_PULLUP);
  pinMode(buzzer, OUTPUT);

  lc.shutdown(FirstMax,false);                    // turn off power saving, enables display
  lc.setIntensity(FirstMax,8);                    // sets brightness (0~15 possible values)
  lc.clearDisplay(FirstMax);                      // clear screen
  lc.shutdown(SecondMax,false);                   // turn off power saving, enables display
  lc.setIntensity(SecondMax,8);                   // sets brightness (0~15 possible values)
  lc.clearDisplay(SecondMax);                     // clear screen

  MainScreen();                                   // Print Main Screen
  GameInit();                                     // Initialize Variables    
  HighScore=EEPROMReadlong(0);                    // Read Hiscore From EEprom
  PrintCannon(CannonPos);                         // Print initial cannon on screen
  PrintAliens(AlienX,AlienY,AlienXo,AlienYo);     // Print inital aliens
}

// read number (higscore) from eeprom
long EEPROMReadlong(long address) {
  long four = EEPROM.read(address);
  long three = EEPROM.read(address + 1);
  long two = EEPROM.read(address + 2);
  long one = EEPROM.read(address + 3);
  return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}

// write number (higscore) to eeprom
void EEPROMWritelong(int address, long value) {
  byte four = (value & 0xFF);
  byte three = ((value >> 8) & 0xFF);
  byte two = ((value >> 16) & 0xFF);
  byte one = ((value >> 24) & 0xFF);
  
  EEPROM.write(address, four);
  EEPROM.write(address + 1, three);
  EEPROM.write(address + 2, two);
  EEPROM.write(address + 3, one);
}


// Show main screen animation (Press fire to start)
void MainScreen() {
long MainAni = 0;
  while (digitalRead(btnFire) == true) {
    MainAni++;
    if (MainAni == 30000) {
      for (int i = 0; i < 8; i++) {
        lc.setColumn(FirstMax,i,SH1[i]);            // Print pixel on MAX
        lc.setColumn(SecondMax,i,SH2[i]);
        SetNeoColumnShade(0,i,SH1[i],0,i*30,0);     // Print pixel on neopixel
        SetNeoColumnShadeR(64,i,SH2[i],0,i*30,0);   
      }
    FastLED.show();                                 // Show neopixel
    } else if (MainAni == 60000) {
      for (int i = 0; i < 8; i++) {
        lc.setColumn(FirstMax,i,SH3[i]);            // Print pixel on MAX
        lc.setColumn(SecondMax,i,SH4[i]);
        SetNeoColumnShade(0,i,SH3[i],0,i*30,0);     // Print pixel on neopixel
        SetNeoColumnShadeR(64,i,SH4[i],0,i*30,0);
      }
      FastLED.show();                               // Show neopixel
      MainAni=0;
    }    
  }
  while (digitalRead(btnFire) == false);                    // wait release fire key to continue
}


// Caluclate max/min of X & Y coordinates of active aliens on screen, and detect if all aliens are killed
// In this function I add an offset of 16 to fix a negative calculation, next when this result was used.
void CalcMaxAlien() {
  MaxAlienX=0;
  MaxAlienY=0;
  MinAlienX=999;
  AllKilled=0;
  for (int nal = 0; nal < MaxAliens; nal++) {                      // cicle on all aliens in array
    if (AliensStat[nal] == true) {                                 // if alien is enabled, check coordinates
      AllKilled++;
      if (AliensX[nal] >= MaxAlienX) MaxAlienX = AliensX[nal];     // set new maximum X  
      if (AliensY[nal] >= MaxAlienY) MaxAlienY = AliensY[nal];     // set new maximum Y  
      if (MinAlienX >= AliensX[nal]) MinAlienX = AliensX[nal];     // set new minimum X  
    }
  }
  if (AllKilled==0) {                                              // if all alien are disabled mean you killed all aliens !!!
    PrintCannon(CannonPos);                                        // Print cannon on screen to refresh at last kill
    NextLevel();                                                   // go to next level 
  }
}

// Play WIN melody and set parameters for next level
void NextLevel() {
  delay(500);
  PlayMelody();                                                   // play WIN melody  
  AlienLevel+=500;                                                // increase alien speed
  Explode =ExplodeSpeed*4;                                        // set variable to clear last explosion starting new level
  GameInit();   
}

// Set Default Variable for initial Game values
void GameInit() {
  lc.clearDisplay(FirstMax);                                     // clear MAX screen
  lc.clearDisplay(SecondMax);                                    
  ClearNeoPixel();                                               // Clear neopixel screen 
  
  for (int nal = 0; nal < MaxAliens; nal++) {                    // Enable all aliens
    AliensStat[nal] = true;
  }  
  
  AlienX =16;                                                    // reset alien position on top (16 means position 0)
  AlienY =0;
  AlienXo =16;
  AlienYo =0;
  AlienXs = false;                                               // Se flag alien go to left direction    
  Explode = 0;
  PrintCannon(CannonPos);                                        // Print initial cannon on screen
  PrintAliens(AlienX,AlienY,AlienXo,AlienYo);                    // Print inital aliens
  CalcMaxAlien();                                                // calculate new aliens edges
  AlienSpeed = (AllKilled * AlienMul) + AlienMul;                // set alien speed 
  AlienSpeed -= AlienLevel;                                      // Add more speed trough level
  Serial.print("Speed=");
  Serial.println(AlienSpeed);
}

// Clear all neopixel display
void ClearNeoPixel() {
  for (int i = 0; i < 8; i++)
  {
    SetNeoColumn(0,i,0,0,0,0);
    SetNeoColumn(64,i,0,0,0,0);
  }
}


// Draw cannon on screen position, also clear all pixel around cannon to avoid shadows
void PrintCannon(byte pos)
{
  pos--;                          // fix offset due position variable go fro 1 to 16, bud display go fro 0 to 15
  SetLed(pos,6,true);             // draw cannon head on MAX
  SetLed(pos,7,true);             // draw cannon base1 on MAX
  SetNeoPixel(pos,6,0,127,0);     // draw cannon head green on neopixel
  SetNeoPixel(pos,7,0,55,0);     // draw cannon base1 green on neopixel
  if (pos>0) {                    // check if outside the screen
    SetLed(pos-1,6,false);        // remove head shadow left on MAX
    SetLed(pos-1,7,true);         // draw cannon base2 on MAX
    SetNeoPixel(pos-1,6,0,0,0);   //remove head shadow left on neopixel
    SetNeoPixel(pos-1,7,0,55,0); // draw cannon base2 green on neopixel
  } 
  if (pos>1) {                    // check if outside the screen
    SetLed(pos-2,7,false);        // remove base shadow left on MAX
    SetNeoPixel(pos-2,7,0,0,0);   // remove base shadow left on neopixel
  }
  if (pos<16) {                   // check if outside the screen  
    SetLed(pos+1,6,false);        // remove head shadow right on MAX
    SetLed(pos+1,7,true);         // draw cannon base3 on MAX
    SetNeoPixel(pos+1,6,0,0,0);   // remove head shadow right on neopixel
    SetNeoPixel(pos+1,7,0,55,0); // draw cannon base3 on neopixel 
  }
  if (pos<15) {                   // check if outside the screen  
    SetLed(pos+2,7,false);        // remove base shadow right on MAX
    SetNeoPixel(pos+2,7,0,0,0);   // remove base shadow right on neopixel
  }
  FastLED.show();                 // show neopixel
}

// Print Aliens giving values from array with X, Y offset and clear old alien with oX and oY offset
void PrintAliens(byte x, byte y,byte ox, byte oy){
  for (int nal = 0; nal < MaxAliens; nal++) {                           // Remove old Aliens
    if (AliensStat[nal] == true) {                                      // check if enabled     
      SetLed( (AliensX[nal]+ox)-16, AliensY[nal]+oy,false);             // Clear Aliens on MAX  
      SetNeoPixel((AliensX[nal]+ox)-16, AliensY[nal]+oy,0,0,0);         // Clear Aliens on neopixel
    }
  }
  for (int nal = 0; nal < MaxAliens; nal++) {                           // Draw Aliens if enabled   
    if (AliensStat[nal] == true) {                                      // check if enabled     
      SetLed((AliensX[nal]+x)-16, AliensY[nal]+y,true);                 // Print Aliens on MAX

      if (AliensY[nal]==0){
        SetNeoPixel((AliensX[nal]+x)-16, AliensY[nal]+y,128,0,128);     // Print Aliens row 1 purple on neopixel
      }else if (AliensY[nal]==1){
        SetNeoPixel((AliensX[nal]+x)-16, AliensY[nal]+y,0,128,128);     // Print Aliens row 2 cyan on neopixel
      }else if (AliensY[nal]==2){
        SetNeoPixel((AliensX[nal]+x)-16, AliensY[nal]+y,0,128,0);       // Print Aliens row 3 green on neopixel
      }else if (AliensY[nal]==3){
        SetNeoPixel((AliensX[nal]+x)-16, AliensY[nal]+y,128,0,0);       // Print Aliens row 4 red on neopixel
      }    
    }
  }
  FastLED.show();                                                       // show neopixel
}

// Game Over animation e scoring
void GameOver() {
int mel = 2600;                                             // setup local var
long InvaderAni = 0;
int AniCount = 0;
  
  while (AniCount <= 10) {                                  // 10 cycle animation 
    InvaderAni++;
    mel--;
    tone(buzzer, mel/10, 10);                               // Make sound
    if (InvaderAni == 100) {
      for (int i = 0; i < 8; i++)                          
      {
        lc.setColumn(FirstMax,i,invader1[i]);               // show invaders on MAX
        lc.setColumn(SecondMax,i,invader1[i]);
        SetNeoColumn(0,i,invader1[i],10+(i*20),0,0);        // show invaders on neopixel
        SetNeoColumn(64,i,invader1[i],10+(i*20),0,0);
      }
      FastLED.show();                                       // show neopixel
    } else if (InvaderAni == 200) {
      for (int i = 0; i < 8; i++)                          
      {
        lc.setColumn(FirstMax,i,invader2[i]);               // show invaders on MAX
        lc.setColumn(SecondMax,i,invader2[i]);
        SetNeoColumn(0,i,invader2[i],10+(i*20),0,0);        // show invaders on neopixel
        SetNeoColumn(64,i,invader2[i],10+(i*20),0,0);
      }
      FastLED.show();                                       // show neopixel
      InvaderAni=0;
      AniCount++;
    }    
  }
  ShowScore();                                              // show score and wait for a keypress
}

// Show final Score an High score
void ShowScore() {
  int waitk = 0;                                            // Set waikey flag
  PrintNumber(Score,false);                                 // Show score
  while (waitk == 0) {                                      // wait fire key to continue
    if (digitalRead(btnFire) == false) {                    // If Fire button pressed
      while (digitalRead(btnFire) == false);                // wait release fire key to continue
      MainScreen();                                         // Show Main Screen
      Serial.println ("Start");
      Score=0;                                              // reset score
      GameInit();                                           // Initialize game                                      
      waitk =1;                                             // reset waitkey flag  
    }
    HiscoreDelay++;
    if (HiscoreDelay >= HiscoreSpeed * 2) {
      PrintNumber(HighScore,true);                          // Show Hi score
      HiscoreDelay = 0;
    } else if (HiscoreDelay == HiscoreSpeed) {
      PrintNumber(Score,false);                             // Show score
    }
  }
}

// Function to play melody
void PlayMelody() {
  for (int mel = 0; mel < 7; mel++) {
    int noteDuration = 1000 / noteDurations[mel];
    tone(buzzer, win[mel], noteDuration);
    int pauseBetweenNotes = noteDuration * 1.30;
    delay(pauseBetweenNotes);
    noTone(buzzer);
    }
}


// Function to set pixel on 16x8 NeoPixel matrix addressed by column, row and RGB Color  0,0 = top left   0,0,0 = Black  255,255,255 = White
void SetNeoPixel( byte x, byte y, int8_t R,int8_t G,int8_t B) {
  leds[(x * 8) + 7-y]  = CRGB( R, G, B);
}

// Function to set column on 16x8 NeoPixel matrix addressed by column, row and RGB Color  0,0 = top left   0,0,0 = Black  255,255,255 = White
void SetNeoColumn(byte offset, byte column, byte value, int8_t R,int8_t G,int8_t B) {
  for (int bits = 0; bits < 8; bits++) {
    if ((value >> bits) % 2 == 1) {
      leds[(offset+(bits * 8)) + (7-column)]  = CRGB( R, G, B);
    }else{
      leds[(offset+(bits * 8)) + (7-column)]  = CRGB( 0, 0, 0);
    }
  }
}

// Function to set column on 16x8 NeoPixel matrix addressed by column, row and shaded RGB Color  0,0 = top left   0,0,0 = Black  255,255,255 = White
void SetNeoColumnShade(byte offset, byte column, byte value, int8_t R,int8_t G,int8_t B) {
  for (int bits = 0; bits < 8; bits++) {
    if ((value >> bits) % 2 == 1) {
      leds[(offset+(bits * 8)) + (7-column)]  = CRGB( R, G+(bits*10), B+(bits*10));
    }else{
      leds[(offset+(bits * 8)) + (7-column)]  = CRGB( 0, 0, 0);
    }
  }
}
// Function to set column on 16x8 NeoPixel matrix addressed by column, row and RGB Shaded reversed Color  0,0 = top left   0,0,0 = Black  255,255,255 = White
void SetNeoColumnShadeR(byte offset, byte column, byte value, int8_t R,int8_t G,int8_t B) {
  for (int bits = 0; bits < 8; bits++) {
    if ((value >> bits) % 2 == 1) {
      leds[(offset+(bits * 8)) + (7-column)]  = CRGB( R, G+(80-(bits*10)), B+(80-(bits*10)));
    }else{
      leds[(offset+(bits * 8)) + (7-column)]  = CRGB( 0, 0, 0);
    }
  }
}


// Function to set pixel on 16x8 matrix addressed by column, row and status 0,0 = top left
void SetLed(byte X,byte Y, boolean OnOff)
{
  if (X>7) { 
    lc.setLed(SecondMax,8-(X-7),Y,OnOff);                // turns on/off LED at X, Y
  } else {
    lc.setLed(FirstMax,7-X,Y,OnOff);                     // turns on/off LED at X, Y
  }
}

// Function to print decimal number from 0000 to 9999 on screen with underline flag
void PrintNumber(long num, boolean underline) {
  byte Digit1;                                                  // convert long to 4 digit BCD
  byte Digit2;
  byte Digit3;
  byte Digit4;

  Digit1 = num/1000;                                             // convert long to 4 digit BCD
  Digit2 = (num-(Digit1*1000))/100;
  Digit3 = (num-((Digit2*100)+(Digit1*1000)))/10;
  Digit4 = num-((Digit3*10)+(Digit2*100)+(Digit1*1000));
 
  for (int i = 0; i < 5; i++)                                    // create bitmap graphics 
  {
    Score1[i] = (Numbers[(Digit2*5)+i] << 4) + (Numbers[(Digit1*5)+i] & B00001111);
    Score2[i] = (Numbers[(Digit4*5)+i] << 4) + (Numbers[(Digit3*5)+i] & B00001111);
  }
  if (underline == true) {                                       // add underline
    Score1[6] = 255;
    Score2[6] = 255;
  } else {                                                       // remove underline 
    Score1[6] = 0;
    Score2[6] = 0;
  }

  for (int i = 0; i < 8; i++)                                     // show score
  {
    lc.setColumn(FirstMax,i,Score1[i]);                           // draw data on MAX
    lc.setColumn(SecondMax,i,Score2[i]);                          // draw data on MAX
    if (underline == true) {                                      // add underline
      SetNeoColumn(0,i,Score1[i],10+(i*20),000,000);              // draw data on Neopixel Color Shaded RED (HIScore)  
      SetNeoColumn(64,i,Score2[i],10+(i*20),000,000);             // draw data on Neopixel
    }else{
      SetNeoColumn(0,i,Score1[i],10+(i*20),10+(i*20),10+(i*20));  // draw data on Neopixel Color shaded white (Score) 
      SetNeoColumn(64,i,Score2[i],10+(i*20),10+(i*20),10+(i*20)); // draw data on Neopixel
    }
  }
  FastLED.show();                                                 // Show neopixel
}


// main loop game
void loop()
{
  if (digitalRead(btnRight) == false) {                  // If Right button pressed
    if (CannonDelay==0) {                                // check cannon delay counter to decide if cannon need be moved 
      CannonPos++;                                       // move the cannon
      if (CannonPos>16) CannonPos = 16;                  // check if position is outside display (right)
      PrintCannon(CannonPos);                            // draw cannon in updated position 
    }
    CannonDelay++;                                       // if button continu to be pressed increase cannon delay counter
    if (CannonDelay > CannonSpeed) CannonDelay = 0 ;     // if cannon delay reach cannon speed value, clear it to allow to move cannon again 
  } else if (digitalRead(btnLeft) == false) {            // If Left button pressed
    if (CannonDelay==0) {                                // check cannon delay counter to decide if cannon need be moved 
      CannonPos--;                                       // move the cannon
      if (CannonPos < 1) CannonPos = 1;                  // check if position is outside display (right)
      PrintCannon(CannonPos);                            // draw cannon in updated position 
    }
    CannonDelay++;                                       // if button continue to be pressed increase cannon delay counter
    if (CannonDelay > CannonSpeed) CannonDelay = 0 ;     // if cannon delay reach cannon speed value, clear it to allow to move cannon again 
  } else {                                               // If left and right are released
    CannonDelay=0;
  }

  if (digitalRead(btnFire) == false) {                   // Press fire button
    if (FireYPos==0) {                                   // check if fire is already shooted 
      FireXPos = CannonPos-1;                            // if not shooted, assign currect cannon X position to missile
      FireYPos = 7;                                      // set Missile Y position (Y>1 = shooting in progress)  
      tone(buzzer, NOTE_C6, 100);
    }
  }

// explosion management
  if (Explode != 0) {                                   // Check if active Explosion to Ex and Ey Position
    Explode++;
    if (Explode > ExplodeSpeed * 6) {
     SetLed(Ex,Ey,false);                               // Clear explosion on MAX
     SetNeoPixel(Ex,Ey,0,0,0);                          // Clear explosion on neopixel
     Explode =0;
     FastLED.show();
    } else if (Explode == (ExplodeSpeed * 5)) {
     SetLed(Ex,Ey,true);                                // Set explosion step4 on MAX
     SetNeoPixel(Ex,Ey,50,50,50);                       // Set explosion step4 on neopixel
     FastLED.show();
    } else if (Explode == (ExplodeSpeed * 4)) {
     SetLed(Ex,Ey,true);                                // Set explosion step3 on MAX
     SetNeoPixel(Ex,Ey,100,100,100);                    // Set explosion step3 on neopixel
     FastLED.show();
    } else if (Explode == (ExplodeSpeed * 3)) {
     SetLed(Ex,Ey,false);                               // set explosion step2 on MAX
     SetNeoPixel(Ex,Ey,150,150,150);                    // Set explosion step2 on neopixel
     FastLED.show();
    } else if (Explode == (ExplodeSpeed * 2)) {
     SetLed(Ex,Ey,true);                                // Set explosion step1 on MAX
     SetNeoPixel(Ex,Ey,200,200,200);                    // Set explosion step1 on neopixel
     FastLED.show();
    } else if (Explode == ExplodeSpeed) {
     SetLed(Ex,Ey,false);                               // Clear explosion on max
     SetNeoPixel(Ex,Ey,0,0,0);                          // Clear explosion on neopixel
     FastLED.show();                                    // Show neopixel
    }
  }


// move aliens with tipycal pattern
  if (AlienDelay > AlienSpeed) {                       // speed checking function                 
    AlienDelay = 0;
    if (AlienXs == false) {                            // check direction 
      AlienX++;                                        // direction R 
    } else {
      AlienX--;                                        // direction L 
    } 

    if (MaxAlienX+AlienX > 31) {                       // if last alien on screen reach right edge of screen, invert direction and step down   
      AlienXs = true;                                  // invert direction
      AlienX--;                                         
      AlienY++;                                        // scrolling down aliens
    } else if (MinAlienX+AlienX < 16) {                // if last alien on screen reach left edge of screen, invert direction and step down 
      AlienXs = false;                                 // invert direction
      AlienX++; 
      AlienY++;                                        // scolling down aliens
    }

    if (MaxAlienY+AlienY >= 6) {                       // If aliens reached cannon GAME OVER 
      GameOver();                                      // call gameover function    
    }
    
    PrintAliens(AlienX, AlienY, AlienXo, AlienYo);     // draw updated aliens on screen

    if (AlienSound == 0) {                             // Generate alien sound pattern
      tone(buzzer, NOTE_F3, 100);
      AlienSound =1;
    } else if (AlienSound == 1) {
      tone(buzzer, NOTE_E3, 100);
      AlienSound =2;
    } else if (AlienSound == 2) {
      tone(buzzer, NOTE_D3, 100);
      AlienSound =3;
    } else if (AlienSound == 3) {
      tone(buzzer, NOTE_C3, 100);
      AlienSound =0;
    } else {
      AlienSound = 0;
    }
    AlienXo = AlienX;                                   // store previous alien position offset, used to clear it when moving
    AlienYo = AlienY;
  } else {
    AlienDelay++;
  }
 
// missile management
  if (FireYPos != 0) {                                   // Fire active and check for collisions
    if (FireDelay==0) {                                  // Wait until firedelay was 0 to do next fire step
      FireYPos--;                                        // decrease missile position 
      if (FireYPos < 1) {                                // if missile reached top 
        SetLed(FireXPos,FireYPos,false);                 // Clear missile shadow when reach top on MAX
        SetNeoPixel(FireXPos,FireYPos,0,0,0);            // Clear missile shadow when reach top on neopixel
        FireYPos = 0;
      }
      SetLed(FireXPos,FireYPos-1,true);                  // Draw missile on MAX
      SetNeoPixel(FireXPos,FireYPos-1,255,255,0);        // Draw Missile on neopixel

      for (int nal = 0; nal < MaxAliens; nal++) {        // Check for missile collided Alien
        if ((((AliensX[nal]+AlienX)-16) == FireXPos) && (AliensY[nal]+AlienY == FireYPos-1) && (AliensStat[nal] == true)){
          tone(buzzer, NOTE_C4, 100);
          SetLed(FireXPos,FireYPos,false);               // clear Alien fired on MAX
          SetNeoPixel(FireXPos,FireYPos,0,0,0);          // clear Alien fired on Neopixel
          Ex=FireXPos;                                   // Set X of explosion 
          Ey=FireYPos-1;                                 // Set y of explosion
          Explode = 1;                                   // Activate Explosion 
          Score += (6-FireYPos)*4;                       // Increase score
          if (Score >= 9999) Score = 9999;               // Set 9999 limit to High Score due limitation to 4 digit
          if (Score >= HighScore) HighScore = Score;     // Set HiScore
          EEPROMWritelong(0, HighScore);                 // Write HigScore on EEprom
          Serial.print ("Score=");
          Serial.println (Score);
          FireYPos = 0;                                  // Reset Fire status  
          AliensStat[nal] = false;                       // Set alien as fired in the array
          CalcMaxAlien();                                // Calculate new alien edges on screen
          AlienSpeed = (AllKilled * AlienMul) + AlienMul;// set alien speed 
        }      
      }

      if ((FireYPos > 0) && (FireYPos < 6)) {
        SetLed(FireXPos,FireYPos,false);                 // clear missile shadow on MAX 
        SetNeoPixel(FireXPos,FireYPos,0,0,0);            // clear missile shadow on Neopixel
      }
      FastLED.show();                                    // Show neopixel    
    }
    FireDelay++;                                         // increase delay variable
    if (FireDelay > FireSpeed) FireDelay = 0 ;           // check if fredelay reached firespped setting
  }

}

Credits

Davide Gatti

Davide Gatti

2 projects • 4 followers
Actually I'm an IT manager and Hardware/Software/Firmware designer for an electronic manufacturer company.

Comments