Филипп Тишкин
Published © GPL3+

PlateArcade

A simple portable game console everyone can make

BeginnerProtip65
PlateArcade

Things used in this project

Hardware components

Arduino Leonardo
Arduino Leonardo
×1
RGB Backlight LCD - 16x2
Adafruit RGB Backlight LCD - 16x2
×1
Switch Actuator, APEM A01 series Illuminated Push-Button Switches
Switch Actuator, APEM A01 series Illuminated Push-Button Switches
could not find another one
×3
Buzzer
Buzzer
×1
Pushbutton Switch, Momentary
Pushbutton Switch, Momentary
×1
Jumper wires (generic)
Jumper wires (generic)
×1

Hand tools and fabrication machines

Breadboard, 170 Pin
Breadboard, 170 Pin

Story

Read more

Code

Jump

Arduino
Game
#include <LiquidCrystal.h>

#define PIN_BUTTON 3
#define PIN_AUTOPLAY 1
#define PIN_BUZZER 9
#define PIN_READWRITE 10
#define PIN_CONTRAST 13

#define SPRITE_RUN1 1
#define SPRITE_RUN2 2
#define SPRITE_JUMP 3
#define SPRITE_JUMP_UPPER '.'         // Use the '.' character for the head
#define SPRITE_JUMP_LOWER 4
#define SPRITE_TERRAIN_EMPTY ' '      // User the ' ' character
#define SPRITE_TERRAIN_SOLID 5
#define SPRITE_TERRAIN_SOLID_RIGHT 6
#define SPRITE_TERRAIN_SOLID_LEFT 7

#define HERO_HORIZONTAL_POSITION 1    // Horizontal position of hero on screen

#define TERRAIN_WIDTH 16
#define TERRAIN_EMPTY 0
#define TERRAIN_LOWER_BLOCK 1
#define TERRAIN_UPPER_BLOCK 2

#define HERO_POSITION_OFF 0          // Hero is invisible
#define HERO_POSITION_RUN_LOWER_1 1  // Hero is running on lower row (pose 1)
#define HERO_POSITION_RUN_LOWER_2 2  //                              (pose 2)

#define HERO_POSITION_JUMP_1 3       // Starting a jump
#define HERO_POSITION_JUMP_2 4       // Half-way up
#define HERO_POSITION_JUMP_3 5       // Jump is on upper row
#define HERO_POSITION_JUMP_4 6       // Jump is on upper row
#define HERO_POSITION_JUMP_5 7       // Jump is on upper row
#define HERO_POSITION_JUMP_6 8       // Jump is on upper row
#define HERO_POSITION_JUMP_7 9       // Half-way down
#define HERO_POSITION_JUMP_8 10      // About to land

#define HERO_POSITION_RUN_UPPER_1 11 // Hero is running on upper row (pose 1)
#define HERO_POSITION_RUN_UPPER_2 12 //                              (pose 2)

LiquidCrystal lcd(12,11,4,5,6,7);
static char terrainUpper[TERRAIN_WIDTH + 1];
static char terrainLower[TERRAIN_WIDTH + 1];
static bool buttonPushed = false;
static byte buttonGraphic[8] = {
  B00100,
  B01010,
  B01010,
  B01010,
  B01110,
  B01010,
  B01010,
  B01010};

void initializeGraphics(){
  static byte graphics[] = {
    // Run position 1
    B01100,
    B01100,
    B00000,
    B01110,
    B11100,
    B01100,
    B11010,
    B10011,
    // Run position 2
    B01100,
    B01100,
    B00000,
    B01100,
    B01100,
    B01100,
    B01100,
    B01110,
    // Jump
    B01100,
    B01100,
    B00000,
    B11110,
    B01101,
    B11111,
    B10000,
    B00000,
    // Jump lower
    B11110,
    B01101,
    B11111,
    B10000,
    B00000,
    B00000,
    B00000,
    B00000,
    // Ground
    B11111,
    B11011,
    B10101,
    B11011,
    B10101,
    B11011,
    B10101,
    B11111,
    // Ground right
    B00011,
    B00011,
    B00011,
    B00011,
    B00011,
    B00011,
    B00011,
    B00011,
    // Ground left
    B11000,
    B11000,
    B11000,
    B11000,
    B11000,
    B11000,
    B11000,
    B11000,
  };
   
  int i;
  // Skip using character 0, this allows lcd.print() to be used to
  // quickly draw multiple characters
  for (i = 0; i < 7; ++i) {
    lcd.createChar(i + 1, &graphics[i * 8]);
    lcd.createChar(0, buttonGraphic);
  }
  for (i = 0; i < TERRAIN_WIDTH; ++i) {
    terrainUpper[i] = SPRITE_TERRAIN_EMPTY;
    terrainLower[i] = SPRITE_TERRAIN_EMPTY;
  }
}

// Slide the terrain to the left in half-character increments
//
void advanceTerrain(char* terrain, byte newTerrain){
  for (int i = 0; i < TERRAIN_WIDTH; ++i) {
    char current = terrain[i];
    char next = (i == TERRAIN_WIDTH-1) ? newTerrain : terrain[i+1];
    switch (current){
      case SPRITE_TERRAIN_EMPTY:
        terrain[i] = (next == SPRITE_TERRAIN_SOLID) ? SPRITE_TERRAIN_SOLID_RIGHT : SPRITE_TERRAIN_EMPTY;
        break;
      case SPRITE_TERRAIN_SOLID:
        terrain[i] = (next == SPRITE_TERRAIN_EMPTY) ? SPRITE_TERRAIN_SOLID_LEFT : SPRITE_TERRAIN_SOLID;
        break;
      case SPRITE_TERRAIN_SOLID_RIGHT:
        terrain[i] = SPRITE_TERRAIN_SOLID;
        break;
      case SPRITE_TERRAIN_SOLID_LEFT:
        terrain[i] = SPRITE_TERRAIN_EMPTY;
        break;
    }
  }
}

bool drawHero(byte position, char* terrainUpper, char* terrainLower, unsigned int score) {
  bool collide = false;
  char upperSave = terrainUpper[HERO_HORIZONTAL_POSITION];
  char lowerSave = terrainLower[HERO_HORIZONTAL_POSITION];
  byte upper, lower;
  switch (position) {
    case HERO_POSITION_OFF:
      upper = lower = SPRITE_TERRAIN_EMPTY;
      break;
    case HERO_POSITION_RUN_LOWER_1:
      upper = SPRITE_TERRAIN_EMPTY;
      lower = SPRITE_RUN1;
      break;
    case HERO_POSITION_RUN_LOWER_2:
      upper = SPRITE_TERRAIN_EMPTY;
      lower = SPRITE_RUN2;
      break;
    case HERO_POSITION_JUMP_1:
    case HERO_POSITION_JUMP_8:
      upper = SPRITE_TERRAIN_EMPTY;
      lower = SPRITE_JUMP;
      break;
    case HERO_POSITION_JUMP_2:
    case HERO_POSITION_JUMP_7:
      upper = SPRITE_JUMP_UPPER;
      lower = SPRITE_JUMP_LOWER;
      break;
    case HERO_POSITION_JUMP_3:
    case HERO_POSITION_JUMP_4:
    case HERO_POSITION_JUMP_5:
    case HERO_POSITION_JUMP_6:
      upper = SPRITE_JUMP;
      lower = SPRITE_TERRAIN_EMPTY;
      break;
    case HERO_POSITION_RUN_UPPER_1:
      upper = SPRITE_RUN1;
      lower = SPRITE_TERRAIN_EMPTY;
      break;
    case HERO_POSITION_RUN_UPPER_2:
      upper = SPRITE_RUN2;
      lower = SPRITE_TERRAIN_EMPTY;
      break;
  }
  if (upper != ' ') {
    terrainUpper[HERO_HORIZONTAL_POSITION] = upper;
    collide = (upperSave == SPRITE_TERRAIN_EMPTY) ? false : true;
  }
  if (lower != ' ') {
    terrainLower[HERO_HORIZONTAL_POSITION] = lower;
    collide |= (lowerSave == SPRITE_TERRAIN_EMPTY) ? false : true;
  }
  
  byte digits = (score > 9999) ? 5 : (score > 999) ? 4 : (score > 99) ? 3 : (score > 9) ? 2 : 1;
  
  // Draw the scene
  terrainUpper[TERRAIN_WIDTH] = '\0';
  terrainLower[TERRAIN_WIDTH] = '\0';
  char temp = terrainUpper[16-digits];
  terrainUpper[16-digits] = '\0';
  lcd.setCursor(0,0);
  lcd.print(terrainUpper);
  terrainUpper[16-digits] = temp;  
  lcd.setCursor(0,1);
  lcd.print(terrainLower);
  
  lcd.setCursor(16 - digits,0);
  lcd.print(score);

  terrainUpper[HERO_HORIZONTAL_POSITION] = upperSave;
  terrainLower[HERO_HORIZONTAL_POSITION] = lowerSave;
  return collide;
}

// Handle the button push as an interrupt
void buttonPush() {
  buttonPushed = true;
}

void setup(){
  pinMode(PIN_READWRITE, OUTPUT);
  digitalWrite(PIN_READWRITE, LOW);
  pinMode(PIN_CONTRAST, OUTPUT);
  digitalWrite(PIN_CONTRAST, LOW);
  pinMode(PIN_BUTTON, INPUT);
  digitalWrite(PIN_BUTTON, HIGH);
  pinMode(PIN_AUTOPLAY, OUTPUT);
  digitalWrite(PIN_AUTOPLAY, HIGH);
  pinMode(PIN_BUZZER,OUTPUT);//initialize the buzzer pin as an output
  digitalWrite(PIN_BUZZER, LOW);
  
  // Digital pin 2 maps to interrupt 0
  attachInterrupt(0/*PIN_BUTTON*/, buttonPush, FALLING);
  lcd.begin(16, 2);
  initializeGraphics();
  

  
}

void loop(){
  static byte heroPos = HERO_POSITION_RUN_LOWER_1;
  static byte newTerrainType = TERRAIN_EMPTY;
  static byte newTerrainDuration = 1;
  static bool playing = false;
  static bool blink = false;
  static unsigned int distance = 0;
  
  if (!playing) {
    drawHero((blink) ? HERO_POSITION_OFF : heroPos, terrainUpper, terrainLower, distance >> 3);
    if (blink) {
      lcd.setCursor(0,0);
      lcd.print("Press A");
    }
    delay(250);
    blink = !blink;
    if (buttonPushed) {
       
    
     
      initializeGraphics();
      heroPos = HERO_POSITION_RUN_LOWER_1;
      playing = true;
      buttonPushed = false;
      distance = 0;
    }
    return;
  }

  // Shift the terrain to the left
  advanceTerrain(terrainLower, newTerrainType == TERRAIN_LOWER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY);
  advanceTerrain(terrainUpper, newTerrainType == TERRAIN_UPPER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY);
  
  // Make new terrain to enter on the right
  if (--newTerrainDuration == 0) {
    if (newTerrainType == TERRAIN_EMPTY) {
      newTerrainType = (random(3) == 0) ? TERRAIN_UPPER_BLOCK : TERRAIN_LOWER_BLOCK;
      newTerrainDuration = 2 + random(10);
    } else {
      newTerrainType = TERRAIN_EMPTY;
      newTerrainDuration = 10 + random(10);
    }
  }
    
  if (buttonPushed) {
    if (heroPos <= HERO_POSITION_RUN_LOWER_2) heroPos = HERO_POSITION_JUMP_1;
    buttonPushed = false;
    tone(PIN_BUZZER, 525);
    delay(100);
    
  }  
noTone(PIN_BUZZER); 
  if (drawHero(heroPos, terrainUpper, terrainLower, distance >> 3)) {
    playing = false; // The hero collided with something. Too bad.
      lcd.setCursor(0,0);
      lcd.print("Game over!");
      tone(PIN_BUZZER, 400);
      delay(200);
      tone(PIN_BUZZER, 350);
      delay(200);
      tone(PIN_BUZZER, 300);
      delay(200);
      tone(PIN_BUZZER, 250);
      delay(200);
      tone(PIN_BUZZER, 300);
      delay(200);
    noTone(PIN_BUZZER); 
  } else {
    if (heroPos == HERO_POSITION_RUN_LOWER_2 || heroPos == HERO_POSITION_JUMP_8) {
      heroPos = HERO_POSITION_RUN_LOWER_1;
    } else if ((heroPos >= HERO_POSITION_JUMP_3 && heroPos <= HERO_POSITION_JUMP_5) && terrainLower[HERO_HORIZONTAL_POSITION] != SPRITE_TERRAIN_EMPTY) {
      heroPos = HERO_POSITION_RUN_UPPER_1;
    } else if (heroPos >= HERO_POSITION_RUN_UPPER_1 && terrainLower[HERO_HORIZONTAL_POSITION] == SPRITE_TERRAIN_EMPTY) {
      heroPos = HERO_POSITION_JUMP_5;
    } else if (heroPos == HERO_POSITION_RUN_UPPER_2) {
      heroPos = HERO_POSITION_RUN_UPPER_1;
    } else {
      ++heroPos;
    }
    ++distance;
    
    digitalWrite(PIN_AUTOPLAY, terrainLower[HERO_HORIZONTAL_POSITION + 2] == SPRITE_TERRAIN_EMPTY ? HIGH : LOW);
  }
  delay(100);
  digitalWrite(PIN_BUZZER,LOW);
}

Pex

Arduino
Another version of Dollar Ninja
#include <LiquidCrystal.h>


const int rs = 12, en = 11, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
const int buttonPin1=3;
const int buttonPin2=1;
const int buzzer=9;
unsigned long pts=0;
//set buttonstates
bool buttonState1=0;
bool buttonState2=0;
//random number for position of obstacles
int randomNums[6];
//random number for number of obstacles
int randomNum=0;
//random number for position of pts
int randomNums1[3];
//random number for number of pts
int randomNum1=0;
//start delay time, which decreases gradually
unsigned int myDelay=500;
//made this boolean to check if button2 is pressed because if it's pressed once in the first for loop i want obstacles not to be written until the end of it
bool temp=0;
//this variable stores the positions of the warrior while he shoots. there can be 16 positions because the warrior has 16 positions.
int tempI[16];
//i use this boolean to check if the point is catched
bool temp1=0;

//use this variable to store the position of the catched point. it must be and array to store the position of all the points in one iteration of the first for loop. if it stored just one position, then the "old" point would come back in the new iteration
int tempI1[3]; 
//use this variable to have a number of shots of the warrior, which is also the length of an array tempI
int button2IsPressed=0;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup() {
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  // set buttonpin mode
  pinMode(buttonPin1,INPUT);
  pinMode(buttonPin2,INPUT);
  pinMode(buzzer,OUTPUT);
  lcd.setCursor(4,0);
  lcd.print("THE GAME");
  lcd.setCursor(2,1);
  lcd.print("STARTS IN ");
  lcd.print("5");
  delay(1000);
  lcd.setCursor(12,1);
  lcd.print("4");
  delay(1000);
  lcd.setCursor(12,1);
  lcd.print("3");
  delay(1000);
  lcd.setCursor(12,1);
  lcd.print("2");
  delay(1000);
  lcd.setCursor(12,1);
  lcd.print("1");
  delay(1000);
  lcd.clear();

}

void loop() {
  here:
 

  randomNum=random(5);
  for(int i=0; i<randomNum; ++i){
  randomNums[i]=random(16);}
   randomNum1=random(3);
  for(int i=0; i<randomNum1; ++i){
  randomNums1[i]=random(16);
  }
  //i reset temp variable before every new for loop because the cycle of the moving warrior starts again
  temp=0;
  for(int j=0; j<3; ++j){
  tempI1[j]=0;
  }
  button2IsPressed=0;
//loop that writes dollar(warrior) on the lcd which jumps everytime button is pressed
    for(int i=0; i<16; ++i){
          
          
          //pts must be written here because of the lcd.clear() below (pts wouldn't be written down the whole time otherwise)
          //i must check if pts is greater than 9 or 99 etc to know how many gaps i should  leave for the number. the more points are collected, the smaller delay time is and the faster is dollar
          if(pts>9 && pts<20) 
          {lcd.setCursor(14,0);
           myDelay=400;
          }
          else if(pts>19 && pts<30) {
            lcd.setCursor(14,0);
            myDelay=300;
          }
          else if(pts>29 && pts<50) 
          {
            myDelay=200;
            lcd.setCursor(14,0);
          }
          
          else if(pts>=50){
            pts=0;
            myDelay=500;
            lcd.clear();
            lcd.setCursor(5,0);
            lcd.print("VICTORY");
            tone(buzzer,262);
            delay(200);
            tone(buzzer,330);
            delay(200);
            tone(buzzer,392);
            delay(100);
            tone(buzzer,330);
            delay(100);
            tone(buzzer,392);
            delay(100);
            tone(buzzer,523);
            delay(200);
            noTone(buzzer);
            delay(3000);
            lcd.clear();
            goto here;
            }
          else lcd.setCursor(15,0);
          lcd.print(pts);
            
         buttonState1=digitalRead(buttonPin1);
         buttonState2=digitalRead(buttonPin2);
 //setting obstacles if the warrior didn't shoot or he shot but he also jumped
 if(!temp){
          for(int j=0; j<randomNum; ++j){
          lcd.setCursor(randomNums[j],1);
          lcd.print("#");
       }
 }
 //checking if the warrior  had shot but there were obstacles before him, we want those obstacles to stay 
 else{
  for(int j=0; j<randomNum; ++j){
 //i check only the tempI[0] position because that's the when the warrior shot for the first time and he removed all the remaining obstacles 
    if(tempI[0]>randomNums[j]){
      lcd.setCursor(randomNums[j],1);
      lcd.print("#");
    
      }

      }
  }        
          

//setting pts
if(!temp1){
          for(int j=0; j<randomNum1; ++j){
          lcd.setCursor(randomNums1[j],0);
          
          lcd.print("*");
           
          }
} 
else{
  for(int j=0; j<randomNum1; ++j){
  
   if(randomNums1[j]!=tempI1[j]){
    lcd.setCursor(randomNums1[j],0);
    lcd.print("*");
    }
  }
  }
           
          
        
          //if the button is pressed we set the cursor up (so that our warrior jumps)
          if(buttonState1==HIGH) 
          {lcd.setCursor(i,0);
          tone(buzzer,131);
          delay(200);
          noTone(buzzer);
          }
          else lcd.setCursor(i,1);
          
          lcd.print("$");
         
          //if button2 is pressed our warrior shoots. i had to put this loop here so that warrior shoots from his current spot and then continues to move. that's why delay time is 5 so that this loop finishes as soon as possible
           if (buttonState2==HIGH) 
         {
          tone(buzzer,175);
         delay(100);
         noTone(buzzer);
         
          temp=1;
          //if the button1 is low then we remove all the obstacles. that's why the state if temp variable is changed
          if(buttonState1==LOW) 
          {
            
            tempI[button2IsPressed]=i;
          }
          ++button2IsPressed;
          for(int k=i+1; k<16; ++k){
          //if the buton1 is low then warrior shoots in the second row. otherwise it shoots in the first row
          if(buttonState1==LOW)
          lcd.setCursor(k,1);
          else lcd.setCursor(k,0);
          lcd.print("~");
          delay(5);
          if(buttonState1==LOW)
          lcd.setCursor(k-1,1);
          else lcd.setCursor(k-1,0);
          lcd.print(" ");
          delay(5);
          
     
          }
         }
         
           delay(myDelay);
          lcd.clear();
           //checking if the positions of the pts and the warrior are the same and if the button is pressed because then they collide and we gain 5 extra points
           for(int j=0; j<randomNum1; ++j){
          if(i==randomNums1[j] && buttonState1==HIGH){
           temp1=1;
           tempI1[j]=i;
           pts+=5;
           }
           }
        
             
      
      
          //checking if the positions of the obstacle and the warrior are the same and if the button1 is not pressed, then they collide and it is the end of the game
          for(int j=0; j<randomNum; ++j){
         if(i==randomNums[j] && buttonState1==LOW && temp==0) {
         
         pts=0;
         myDelay=500;
         lcd.clear();
         lcd.setCursor(6,0);
         lcd.print("GAME");
         lcd.setCursor(6,1);
         lcd.print("OVER");
         tone(buzzer,349);
         delay(200);
         tone(buzzer, 277);
         delay(200);
         tone(buzzer,220);
         delay(100);
         tone(buzzer,277);
         delay(100);
         tone(buzzer,220);
         delay(200);
         noTone(buzzer);
         delay(3000);
         lcd.clear();
         //i must skip the for loop because otherwise the game would continue where it ended
          goto here;
        
         }
         //counting number of skipped obstacles and that would be our points
         else if(i==randomNums[j] && buttonState1==HIGH) ++pts;
          }
       
          
         
      }



}

Credits

Филипп Тишкин
0 projects • 0 followers

Comments