Arduino Playground is read-only starting December 31st, 2018. For more info please look at this Forum Post

Arduino Handheld Game Platform

This is an easy to build GameBoy-like system using only an Aruduino Uno, GLCD, and some momentary switches. You can write your game sketches using the u8glib graphics library. At the bottom of the page is the source code of some games my son wrote.

My son and I used a Radio Shack Sidekick box as the housing. We bolted the Arduino Uno onto it using short plastic standoffs, and cut holes to access the USB port and power jack. An external umbilical connects the 9V battery, or you can attach an external power supply.

The SainSmart 12864 GLCD is mounted on top using standoffs. We used the serial header as described in Brad's *duino blog:

 (Top Row) 
       SID ---> D11 
        CS ---> D12 
       SCK ---> D13

 (Bottom) 
       GND ---> GND 
       --- ---> --- 
       VCC ---> V5

We used the tiny momentary switches that came with the Sidekick for a D-pad. I heated up a paper clip to melt holes for the pins, and soldered leads to them on the interior. We then added some hot glue to help keep the leads from snapping off due to flexing. The "A" and "B" buttons on the right are Radio Shack submini momentary buttons.

The controller buttons all have +5v on one pin, and attach to an Arduino digital input on the other pin. We use 10k pulldown resistors on each input, but you can avoid them if you use the built-in 20k pullup resistors as described here.


Proof of Concept

Below is my son's proof of concept sketch that displays a different letter for each controller button. As we discovered later, large fonts can eat up all your available memory, so not a good idea!

  1. #include <U8glib.h>
  2. U8GLIB_ST7920_128X64 u8g(13, 11, 12, U8G_PIN_NONE);
  3. int aPin = 7;
  4. int bPin = 6;
  5. int upPin = 4;
  6. int downPin = 5;
  7. int leftPin = 3;
  8. int rightPin = 2;
  9. char key[] = {'\0', '\0'};
  10.  
  11. void setup(){
  12.   pinMode(upPin, INPUT);
  13.   pinMode(downPin, INPUT);
  14.   pinMode(leftPin, INPUT);
  15.   pinMode(rightPin, INPUT);
  16.   pinMode(aPin, INPUT);
  17.   pinMode(bPin, INPUT);
  18. }
  19.  
  20. void draw(void){
  21.   u8g.setFont(u8g_font_osb35);
  22.   u8g.drawStr(42, 50, key);
  23. }
  24.  
  25. void loop(){
  26.   int upReading = digitalRead(upPin);
  27.   int downReading = digitalRead(downPin);
  28.   int leftReading = digitalRead(leftPin);
  29.   int rightReading = digitalRead(rightPin);
  30.   int aReading = digitalRead(aPin);
  31.   int bReading = digitalRead(bPin);
  32.   if (upReading){
  33.     key[0] = 'U';
  34.   }else if (downReading){
  35.     key[0] = 'D';
  36.   }else if (leftReading){
  37.     key[0] = 'L';
  38.   }else if (rightReading){
  39.     key[0] = 'R';
  40.   }else if (aReading){
  41.     key[0] = 'A';
  42.   }else if (bReading){
  43.     key[0] = 'B';
  44.   }
  45.     u8g.firstPage();  
  46.   do{
  47.     draw();
  48.   }while(u8g.nextPage());
  49.   delay(15);
  50. }


Lunar Lander

"A" button to apply thrust, try to avoid crashing... Uses EEPROM to save high score.

  1. #include <U8glib.h>
  2. #include <EEPROM.h>
  3. U8GLIB_ST7920_128X64 u8g(13, 11, 12, U8G_PIN_NONE);
  4. const int aPin = 7;
  5. const int bPin = 6;
  6. const int upPin = 4;
  7. const int downPin = 5;
  8. const int leftPin = 3;
  9. const int rightPin = 2;
  10. int score;
  11. int section = 0;
  12. byte high;
  13. byte low;
  14. int highScore;
  15. const float acceleration = .1;
  16. int yPos = 10;
  17. float velocity = 0;
  18. float velInt = 0;
  19. char height[6];
  20. char velocityStr[6];
  21. char highScoreStr[6];
  22. char scoreStr[6];
  23. const uint8_t lunarLander[] = {
  24.   0x10,
  25.   0x10,
  26.   0x28,
  27.   0x28,
  28.   0x44,
  29.   0x44,
  30.   0x82,
  31.   0x7c
  32. };
  33.  
  34. const uint8_t rightLander[] = {
  35.   0x00,
  36.   0x06,
  37.   0x1a,
  38.   0x64,
  39.   0x84,
  40.   0x48,
  41.   0x28,
  42.   0x10
  43. };
  44.  
  45. char key[] = {'\0', '\0'};
  46.  
  47. void setup(){
  48.   pinMode(upPin, INPUT);
  49.   pinMode(downPin, INPUT);
  50.   pinMode(leftPin, INPUT);
  51.   pinMode(rightPin, INPUT);
  52.   pinMode(aPin, INPUT);
  53.   pinMode(bPin, INPUT);
  54. }
  55.  
  56. void draw(void){
  57.   high = EEPROM.read(0);
  58.   low = EEPROM.read(1);
  59.   highScore = (high << 8) + low;
  60.   sprintf(highScoreStr, "%d", highScore);
  61.   switch (section){
  62.     case 0:
  63.       u8g.setFont(u8g_font_timR10);
  64.       u8g.drawStr(28, 15, "Welcome to");
  65.       u8g.drawStr(20, 28, "Lunar Lander:");
  66.       u8g.drawStr(15, 41, "Arduino Edition!");
  67.       u8g.drawStr(1, 60, "Press any key to Start");
  68.       break;
  69.     case 1:
  70.       u8g.drawHLine(0, 54, 128);
  71.       u8g.drawBitmap(60, (int)yPos, 1, 8, lunarLander);
  72.       u8g.setFont(u8g_font_orgv01);
  73.       u8g.drawStr(80, 23, "ALTITUDE");
  74.       u8g.drawStr(85, 30, height);
  75.       u8g.drawStr(5, 23, "VELOCITY");
  76.       u8g.drawStr(10, 30, velocityStr);
  77.       break;
  78.     case 2:
  79.       u8g.setFont(u8g_font_orgv01);
  80.       u8g.drawStr(45, 10, "You won!");
  81.       u8g.drawStr(17, 50, "Press A to play again");
  82.       u8g.drawStr(25, 60, "Press B to quit");
  83.       score = abs(velInt);
  84.       if (score < highScore){
  85.         EEPROM.write(0, highByte(score));
  86.         EEPROM.write(1, lowByte(score));
  87.         u8g.drawStr(25, 40, "NEW HIGH SCORE!");
  88.       }
  89.       u8g.drawStr(20, 20, "Your score:");
  90.       u8g.drawStr(90, 20, scoreStr);
  91.       u8g.drawStr(20, 30, "Highscore:");
  92.       u8g.drawStr(90, 30, highScoreStr);
  93.       break;
  94.     case 3:
  95.       u8g.setFont(u8g_font_timR10);
  96.       u8g.drawStr(40, 15, "You lost!");
  97.       u8g.drawStr(3, 40, "Press A to play again");
  98.       u8g.drawStr(25, 55, "Press B to quit");
  99.       break;
  100.   }
  101.  
  102. }
  103.  
  104. void loop(){
  105.   int upReading = digitalRead(upPin);
  106.   int downReading = digitalRead(downPin);
  107.   int leftReading = digitalRead(leftPin);
  108.   int rightReading = digitalRead(rightPin);
  109.   int aReading = digitalRead(aPin);
  110.   int bReading = digitalRead(bPin);
  111.   velInt = velocity * 10;
  112.   if (section == 0 && (upReading || downReading || leftReading || rightReading || aReading || bReading)){
  113.     section = 1;
  114.   }
  115.   if (section == 1){
  116.     if (yPos <= 1){
  117.       velocity = 0;
  118.       yPos = 2;
  119.     } else if (yPos > 47){
  120.       if (abs(velInt) < 15){
  121.         section = 2;
  122.         score = abs(velInt);
  123.         sprintf(scoreStr, "%d", score);
  124.         delay(50);
  125.       } else {
  126.         section = 3;
  127.         delay(50);
  128.       }
  129.     }
  130.     if (upReading || aReading){
  131.       velocity += acceleration;
  132.     } else {
  133.       velocity -= acceleration;
  134.     }
  135.       yPos -= velocity;
  136.       sprintf(height, "%d.%d", (int)(47 - yPos), (1 - (yPos - (int)yPos)) * 10);
  137.       sprintf(velocityStr, "%d", (abs((int)velInt)));
  138.    } else if (section == 2 || section == 3){
  139.      if (aReading){
  140.        section = 1;
  141.        resetGame();
  142.      } else if (bReading){
  143.        section = 0;
  144.        resetGame();
  145.      }
  146.    }
  147.   u8g.firstPage();
  148.   do{
  149.     draw();
  150.   }while(u8g.nextPage());
  151.   delay(50);
  152. }
  153.  
  154. void resetGame(){
  155.   yPos = 10;
  156.   velocity = 0;
  157. }


Tron

Player 1 uses L and R buttons, Player 2 uses A and B buttons.

  1. #include <U8glib.h>
  2. U8GLIB_ST7920_128X64 u8g(13, 11, 12, U8G_PIN_NONE);
  3. const int aPin = 7;
  4. const int bPin = 6;
  5. const int upPin = 4;
  6. const int downPin = 5;
  7. const int leftPin = 3;
  8. const int rightPin = 2;
  9. int gameOver = 0;
  10. const int numX = 32;
  11. const int numY = 16;
  12. int cells[numX][numY];
  13. char printBuf[64];
  14. int count = 0;
  15. int loser = 0;
  16. const int freq = 2;
  17. int sleep = 50;
  18. struct Player {
  19.   int x,y;
  20.   int orient;
  21.   int value;
  22.   int left;
  23.   int right;
  24. };
  25. Player playerA;
  26. Player playerB;
  27. int whoLost(Player p);
  28. void where(Player p);
  29. void turnLeft(Player* pp);
  30. void turnRight(Player* pp);
  31. void advance(Player* pp);
  32.  
  33. void setup(){
  34.   Serial.begin(9600);
  35.   Serial.println("I am setting up");
  36.   pinMode(upPin, INPUT);
  37.   pinMode(downPin, INPUT);
  38.   pinMode(leftPin, INPUT);
  39.   pinMode(rightPin, INPUT);
  40.   pinMode(aPin, INPUT);
  41.   pinMode(bPin, INPUT);
  42.   playerA.value = 1;
  43.   playerB.value = 2;
  44.   reset();
  45. }
  46.  
  47. void draw(){
  48.   u8g.setFont(u8g_font_orgv01);
  49.   switch(gameOver){
  50.     case 0:
  51.       for (int x = 0; x < numX; x++){
  52.         for (int y = 0; y < numY; y++){
  53.           if (cells[x][y]){
  54.             u8g.drawBox(x*4, y*4, 4, 4);
  55.           }
  56.         }
  57.       }
  58.       break;
  59.     case 1:
  60.       Serial.println("Player 1 wins");
  61.       u8g.drawStr(20, 20, "Player 1 WINS!");
  62.       break;
  63.     case 2:
  64.       u8g.drawStr(20, 20, "Player 2 WINS!");
  65.       break;
  66.     case 3:
  67.       u8g.drawStr(60, 30, "3");
  68.       break;
  69.     case 4:
  70.       u8g.drawStr(60, 30, "2");
  71.       break;
  72.     case 5:
  73.       u8g.drawStr(60, 30, "1");
  74.       break;
  75.     case 6:
  76.       for (int x = 0; x < numX; x++){
  77.         for (int y = 0; y < numY; y++){
  78.           if (cells[x][y]){
  79.             u8g.drawBox(x*4, y*4, 4, 4);
  80.           }
  81.         }
  82.       }
  83.       u8g.drawDisc((loser == playerB.value ? playerA.x*4+1 : playerB.x*4+1), (loser == playerB.value ? playerA.y*4+1 : playerB.y*4+1), 4);
  84.       break;
  85.     case 7:
  86.       for (int x = 0; x < numX; x++){
  87.         for (int y = 0; y < numY; y++){
  88.           if (cells[x][y]){
  89.             u8g.drawBox(x*4, y*4, 4, 4);
  90.           }
  91.         }
  92.       }
  93.       u8g.drawDisc((loser == playerB.value ? playerA.x*4+1 : playerB.x*4+1), (loser == playerB.value ? playerA.y*4+1 : playerB.y*4+1), 7);
  94.       break;
  95.     case 8:
  96.       for (int x = 0; x < numX; x++){
  97.         for (int y = 0; y < numY; y++){
  98.           if (cells[x][y]){
  99.             u8g.drawBox(x*4, y*4, 4, 4);
  100.           }
  101.         }
  102.       }
  103.       u8g.drawDisc((loser == playerB.value ? playerA.x*4+1 : playerB.x*4+1), (loser == playerB.value ? playerA.y*4+1 : playerB.y*4+1), 10);
  104.       break;
  105.     default:
  106.       break;
  107.   }
  108. }
  109.  
  110. void loop(){
  111.   static int leftReading;
  112.   static int rightReading;
  113.   static int aReading;
  114.   static int bReading;
  115.   if (!gameOver){
  116.     if (digitalRead(leftPin) && !rightReading){
  117.       leftReading = 1;
  118.     }
  119.     if (digitalRead(rightPin) && !leftReading){
  120.       rightReading = 1;
  121.     }
  122.     if (digitalRead(aPin) && !bReading){
  123.       aReading = 1;
  124.     }
  125.     if (digitalRead(bPin) && !aReading){
  126.       bReading = 1;
  127.     }
  128.     if ((count % freq) == 0){
  129.       if (leftReading){
  130.         turnLeft(&playerA);
  131.       } else if (rightReading){
  132.         turnRight(&playerA);
  133.       }
  134.       if (aReading){
  135.         turnRight(&playerB);
  136.       } else if (bReading){
  137.         turnLeft(&playerB);
  138.       }
  139.       u8g.firstPage();
  140.       do{
  141.         draw();
  142.       }while(u8g.nextPage());
  143.       advance(&playerA);
  144.       advance(&playerB);
  145.       where(playerA);
  146.       where(playerB);
  147.     }
  148.     leftReading = rightReading = aReading = bReading = 0;
  149.   } else {
  150.     u8g.firstPage();
  151.     do{
  152.       draw();
  153.     }while(u8g.nextPage());
  154.     delay(500);
  155.     gameOver ++;
  156.     u8g.firstPage();
  157.     do{
  158.       draw();
  159.     }while(u8g.nextPage());
  160.     delay(500);
  161.     gameOver ++;
  162.     u8g.firstPage();
  163.     do{
  164.       draw();
  165.     }while(u8g.nextPage());
  166.     delay(500);
  167.     gameOver = loser;
  168.     u8g.firstPage();
  169.     do{
  170.       draw();
  171.     }while(u8g.nextPage());
  172.     delay(2000);
  173.     gameOver = 3;
  174.     u8g.firstPage();
  175.     do{
  176.       draw();
  177.     }while(u8g.nextPage());
  178.     delay(1000);
  179.     gameOver++;
  180.     u8g.firstPage();
  181.     do{
  182.       draw();
  183.     }while(u8g.nextPage());
  184.     delay(1000);
  185.     gameOver++;
  186.     u8g.firstPage();
  187.     do{
  188.       draw();
  189.     }while(u8g.nextPage());
  190.     delay(1000);
  191.     reset();
  192.   }
  193.   count++;
  194.   delay(sleep);
  195. }
  196.  
  197. void where(Player p){
  198.   if (p.x >= numX || p.x < 0 || p.y >= numY || p.y < 0 || cells[p.x][p.y]){
  199.     loser = whoLost(p);
  200.     gameOver = 6;
  201.   } else if (!cells[p.x][p.y]){
  202.       cells[p.x][p.y] = 1;
  203.   }
  204. }
  205.  
  206. int whoLost(Player p){
  207.   if (p.value == playerA.value){
  208.     return 2;
  209.   } else if (p.value == playerB.value){
  210.     return 1;
  211.   }
  212. }
  213.  
  214. void advance(Player* pp){
  215.   switch(pp->orient){
  216.     case 0:
  217.       pp->y -= 1;
  218.       break;
  219.     case 1:
  220.       pp->x += 1;
  221.       break;
  222.     case 2:
  223.       pp->y += 1;
  224.       break;
  225.     case 3:
  226.       pp->x -= 1;
  227.       break;
  228.     default:
  229.       break;
  230.   }
  231. }
  232.  
  233. void turnLeft(Player* pp){
  234.   if (pp->orient == 0){
  235.     pp->orient = 3;
  236.   } else {
  237.     pp->orient -= 1;
  238.   }
  239. }
  240.  
  241. void turnRight(Player* pp){
  242.   if (pp->orient == 3){
  243.     pp->orient = 0;
  244.   } else {
  245.     pp->orient += 1;
  246.   }
  247. }
  248.  
  249. void reset(){
  250.   playerB.x = numX/2;
  251.   playerB.y = numY/2 - 1;
  252.   playerB.orient = 0;
  253.   playerB.left = bPin;
  254.   playerB.right = aPin;
  255.   playerA.x = numX/2;
  256.   playerA.y = numY/2 + 1;
  257.   playerA.orient = 2;
  258.   playerA.left = leftPin;
  259.   playerA.right = rightPin;
  260.   for (int x = 0; x < numX; x++){
  261.     for (int y = 0; y < numY; y++){
  262.       cells[x][y] = 0;
  263.     }
  264.   }
  265.   gameOver = 0;
  266. }