Project tutorial
Space Hinvaders Color

Space Hinvaders Color © GPL3+

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.

  • 251 views
  • 2 comments
  • 1 respect

Components and supplies

Necessary tools and machines

Apps and online services

About this project

I really like old video games, and I had never found a space invader style game on Arduino that used Dot matrix LED display. One day seeing one of these displays I said to myself: I could make a space invader in a space approximately equal to what was originally occupied by a single sprite of an invader. It would be great.

I got started and tried it, and in spite of all the limitations I managed to do something playable and at the same time fascinating considering the limits of the hardware.As you can see on the title is named Space Hinvaders, because is a Survival Hacking (SH) creation, so altered original Space Invader name according to SH Initials.

This is an enacted version, that's add a color WS2812 RGB dot matrix display to the original project https://www.hackster.io/davide-gatti/space-hinvaders-c48731 based on MAX7219 Red LED dot matrix display.

This code drive both display in the same time, so you chan choose to make a monochrome version or color version or both together (like the video).

Have fun !!! (Background music on video is made by me too)


Code

Color Space HnvadersC/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 sequance
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 = 10000;     // 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 followind 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
  }

}

Custom parts and enclosures

Full Project Sources
space_hinvaders_color_jpSA9wqHSv.zip

Schematics

Nano Invaders Color.fzz
Nano Invaders Color.fzz
nano_invaders_color_3PwBKgV2D7.fzz

Comments

Similar projects you might like

Space Hinvaders

Project tutorial by Davide Gatti

  • 535 views
  • 2 comments
  • 6 respects

Color Sensor Prototype: Detect Color Name from Objects

Project tutorial by moretticb

  • 8,918 views
  • 7 comments
  • 33 respects

Motion Controlled Color Changer!

Project tutorial by Arduino “having11” Guy

  • 19,469 views
  • 18 comments
  • 25 respects

Arduino Color Recognition

by millerman4487

  • 16,307 views
  • 11 comments
  • 36 respects

Arduino RGB Color Mixer

Project tutorial by FIELDING

  • 10,296 views
  • 6 comments
  • 15 respects

Arduino Color Detection

Project tutorial by MJRoBot

  • 35,724 views
  • 6 comments
  • 35 respects
Add projectSign up / Login