My First Experience Playing Games on the Arduino
Ready for some Arduino gaming? Here’s a simple game for the Arduino and Mini-OLED called Deathstar Vs X-Wing. Well, I love Star Wars so this should be a fun project.
The aim is to defeat the Deathstar while piloting an X-wing. “Great! sound perfect”
Components:
To follow along with this guide, you will need the following components:
- Arduino board (e.g., Arduino Uno, Arduino Nano, etc.)
- Mini OLED display with compatible interface (I2C)
- Tactile Switches (X3)
- Buzzer (X1)
- Jumper wires
- Breadboard
Let's Build!
The Circuit:
Assemble your components in the exact same way as shown in the diagram below.
The Code:
Holy Cow! This was some bumpy spaghetti code, I have restructured it the best I could, I don’t understand half of it, as a lot is in Lithuanian. I added variables for the buttons and the sound effects, so you can alter the Arduino pins if you need to. Also, I declared the screen size for the 128×64 OLED display as the game was far too big for this screen. But I got it working in the end.
If you’re missing any libraries, you can install them from the Libraries Manager under the IDE’s Tools menu. To learn how to do this you can follow our guide here: How to Install Arduino Libraries
/********************************************************************* This is an example for our Monochrome OLEDs based on SSD1306 drivers Pick one up today in the adafruit shop! ------> http://www.adafruit.com/category/63_98 This example is for a 128x32 size display using I2C to communicate 3 pins are required to interface (2 I2C and one reset) Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Limor Fried/Ladyada for Adafruit Industries. BSD license, check license.txt for more information All text above, and the splash screen must be included in any redistribution *********************************************************************/ #include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Fonts/FreeSans9pt7b.h> //#include <Fonts/FreeSans12pt7b.h>
#define OLED_RESET 4 #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET); int Music=9; int UP=11; int DOWN=12; int FIRE=3; const int c = 261; const int d = 294; const int e = 329; const int f = 349; const int g = 391; const int gS = 415; const int a = 440; const int aS = 455; const int b = 466; const int cH = 523; const int cSH = 554; const int dH = 587; const int dSH = 622; const int eH = 659; const int fH = 698; const int fSH = 740; const int gH = 784; const int gSH = 830; const int aH = 880; const unsigned char PROGMEM dioda16 [] = { 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x3F, 0xF0, 0x3C, 0x00, 0x3C, 0x00, 0xFF, 0x00, 0x7F, 0xFF, 0x7F, 0xFF, 0xFF, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x1F, 0xF0, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00 }; const unsigned char PROGMEM storm [] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x07, 0x80, 0x01, 0xE0, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x20, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x04, 0x00, 0x00, 0x20, 0x00, 0x00, 0x04, 0x00, 0x00, 0x20, 0x00, 0x00, 0x04, 0x00, 0x00, 0x60, 0x00, 0x00, 0x02, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x01, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD7, 0xFF, 0xFF, 0xE1, 0x00, 0x01, 0xBF, 0xFC, 0x1F, 0xFA, 0x80, 0x01, 0xBF, 0xF1, 0xCF, 0xFA, 0x80, 0x01, 0x3F, 0xC2, 0x37, 0xF7, 0x80, 0x01, 0xEF, 0x9C, 0x01, 0xE7, 0xC0, 0x01, 0xE0, 0x70, 0x06, 0x06, 0x80, 0x01, 0xE0, 0xC0, 0x03, 0x06, 0x80, 0x01, 0xFF, 0x80, 0x01, 0xFF, 0x80, 0x01, 0xF8, 0x00, 0x00, 0x1D, 0xC0, 0x03, 0x70, 0x00, 0x80, 0x0C, 0x60, 0x05, 0xB0, 0x07, 0xF0, 0x08, 0x90, 0x09, 0x10, 0x1F, 0xF8, 0x09, 0xD0, 0x0B, 0x90, 0x1F, 0x7C, 0x03, 0xF0, 0x0F, 0xC0, 0xFC, 0x0F, 0x07, 0x90, 0x0D, 0x43, 0xC0, 0x03, 0x07, 0x90, 0x05, 0x64, 0x00, 0x00, 0xCF, 0x10, 0x07, 0xFC, 0x00, 0x00, 0x26, 0x10, 0x01, 0x80, 0x00, 0x00, 0x10, 0x20, 0x01, 0x00, 0x00, 0x00, 0x0E, 0x40, 0x01, 0x80, 0x07, 0xF0, 0x01, 0x80, 0x00, 0x80, 0x07, 0xC8, 0x00, 0x80, 0x00, 0x80, 0x0B, 0xE8, 0x00, 0x80, 0x00, 0x87, 0x97, 0xE9, 0xE0, 0x80, 0x00, 0x87, 0xDF, 0xEF, 0xA0, 0x80, 0x00, 0x4B, 0xFF, 0xFF, 0xA0, 0x80, 0x00, 0x6B, 0xDF, 0xFB, 0xA3, 0x00, 0x00, 0x24, 0x97, 0xE8, 0x24, 0x00, 0x00, 0x1E, 0x1F, 0xC0, 0x2C, 0x00, 0x00, 0x07, 0xF8, 0x1F, 0xF0, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00 };
void setup() { pinMode(FIRE,INPUT_PULLUP); //shoot pinMode(UP,INPUT_PULLUP); //up pinMode(DOWN,INPUT_PULLUP); //down pinMode(Music,OUTPUT); //sound display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.display(); display.clearDisplay(); display.setTextSize(0); display.drawBitmap(6, 11,storm, 48,48, 1); display.setFont(&FreeSans9pt7b); display.setTextColor(WHITE); display.setCursor(65,14); display.println("xWing"); display.setFont(); display.setCursor(65,17); display.setTextSize(0); display.println("vs"); display.setCursor(0,0); float voltaza=readVcc()/1000; display.println(voltaza);; display.setFont(&FreeSans9pt7b); display.setCursor(65,39); display.println("Death"); display.setFont(); display.setCursor(65,42); display.println("star "); display.setTextSize(0); display.setCursor(65,55); display.println("by Danko"); display.setCursor(65,20); display.display(); display.setFont(); beep(a, 500); beep(a, 500); beep(a, 500); beep(f, 350); beep(cH, 150); beep(a, 500); beep(f, 350); beep(cH, 150); beep(a, 650); delay(500); delay(500); } int metx=0; int mety=0; int postoji=0; int nep=8; int smjer=0; int go=0; int rx=95; int ry=0; int rx2=95; int ry2=0; int rx3=95; int ry3=0; int bodovi=0; int brzina=3; //speed of bullet int bkugle=1; int najmanja=600; int najveca=1200; int promjer=10; int rx4=95; int ry4=0; int zivoti=5; int poc=0; int ispaljeno=0; int nivo=1; int centar=95; unsigned long pocetno=0; unsigned long odabrano=0; unsigned long trenutno=0; unsigned long nivovrije=0; int poz=30;
void loop() { if(go==0){ display.clearDisplay(); display.drawPixel(50,30,1); display.drawPixel(30,17,1); display.drawPixel(60,18,1); display.drawPixel(55,16,1); display.drawPixel(25,43,1); display.drawPixel(100,43,1); display.drawPixel(117,52,1); display.drawPixel(14,49,1); display.drawPixel(24,24,1); display.drawPixel(78,36,1); display.drawPixel(80,57,1); display.drawPixel(107,11,1); display.drawPixel(150,11,1); display.drawPixel(5,5,1); display.drawPixel(8,7,1); display.drawPixel(70,12,1); display.drawPixel(10,56,1); display.drawPixel(70,25,1); if(poc==0){ pocetno=millis(); odabrano= random(400,1200); poc=1; } trenutno=millis(); //nivoi if((trenutno-nivovrije)>50000){ nivovrije=trenutno; nivo=nivo+1; brzina=brzina+1; //brizna neprijateljevog metka if ( nivo % 2 == 0 ){ bkugle=bkugle+1; promjer=promjer-1; } najmanja=najmanja-50; najveca=najveca-50; } if((odabrano+pocetno)<trenutno){ poc=0; ispaljeno=ispaljeno+1; if(ispaljeno==1){ rx=95; ry=nep;} if(ispaljeno==2){ rx2=95; ry2=nep;} if(ispaljeno==3){ rx3=95; ry3=nep; } if(ispaljeno==4){ rx4=95; ry4=nep; } } if(ispaljeno>0){ display.drawCircle(rx,ry,2,1); rx=rx-brzina; } if(ispaljeno>1){ display.drawCircle(rx2,ry2,1,1); rx2=rx2-brzina; } if(ispaljeno>2){ display.drawCircle(rx3,ry3,4,1); rx3=rx3-brzina; } if(ispaljeno>3){ display.drawCircle(rx4,ry4,2,1); rx4=rx4-brzina; } if(digitalRead(12)==0 && poz>=2){ poz=poz-2; } if(digitalRead(11)==0&& poz<=46){ poz=poz+2;} if(digitalRead(3)==0 && postoji==0){ postoji=1; metx=6; mety=poz+8; tone(Music,1200,20); } if(postoji==1){ metx=metx+8 ; display.drawLine(metx,mety,metx+4,mety,1); } display.drawBitmap(4, poz,dioda16, 16,16, 1); display.fillCircle(centar,nep,promjer,1); display.fillCircle(centar+2,nep+3,promjer/3,0); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(33,57); display.println("score:"); display.setCursor(68,57); display.println(bodovi); display.setCursor(33,0); display.println("lives:"); display.setCursor(68,0); display.println(zivoti); display.setCursor(110,0); display.println("L:"); display.setCursor(122,0); display.println(nivo); display.setCursor(108,57); display.println(trenutno/1000); display.display(); if(metx>128)postoji=0; if(smjer==0){ nep=nep+bkugle;} else{ nep=nep-bkugle;} if(nep>=(64-promjer)) smjer=1; if(nep<=promjer) smjer=0; if(mety>=nep-promjer && mety<=nep+promjer) if(metx>(centar-promjer)&&metx<(centar+promjer)) { metx=-20; tone(Music,500,20); bodovi=bodovi+1; postoji=0; } int pozicija=poz+8; if(ry>=pozicija-8 && ry<=pozicija+8) if(rx<12&&rx>4) { rx=95; ry=-50; tone(Music,100,100); zivoti=zivoti-1; } if(ry2>=pozicija-8 && ry2<=pozicija+8) if(rx2<12&&rx2>4){ rx2=-50; ry2=-50; tone(Music,100,100); zivoti=zivoti-1; } if(ry3>=pozicija-8 && ry3<=pozicija+8) if(rx3<12&&rx3>4){ rx3=-50; ry3=-50; tone(Music,100,100); zivoti=zivoti-1; } if(ry4>=pozicija-8 && ry4<=pozicija+8) if(rx4<12&&rx4>4){ rx4=200; ry4=-50; ispaljeno=0; tone(Music,100,100); zivoti=zivoti-1; } if(rx4<1){ ispaljeno=0; rx4=200;} if(zivoti==0) go=1; } if(go==1){ if(zivoti==0){ tone(Music,200,300); delay(300); tone(Music,250,200); delay(200); tone(Music,300,300); delay(300); zivoti=5; } display.clearDisplay(); display.setFont(); display.setTextSize(2); display.setTextColor(WHITE); display.setCursor(7,10); display.println("GAME OVER!"); display.setTextSize(1); display.setCursor(7,30); display.println("score:"); display.setCursor(44,30); display.println(bodovi); display.setCursor(7,40); display.println("level:"); display.setCursor(44,40); display.println(nivo); display.setCursor(7,50); display.println("time(s):"); display.setCursor(60,50); display.println(trenutno/1000); display.display(); if(digitalRead(3)==0){ tone(Music,280,300); delay(300); tone(Music,250,200); delay(200); tone(Music,370,300); delay(300); ponovo(); } } } void ponovo(){ metx=0; mety=0; postoji=0; nep=8; smjer=0; go=0; rx=95; ry=0; rx2=95; ry2=0; rx3=95; ry3=0; bodovi=0; brzina=3; //brizna neprijateljevog metka bkugle=1; najmanja=600; najveca=1200; promjer=12; rx4=95; ry4=0; zivoti=5; poc=0; ispaljeno=0; nivo=1; pocetno=0; odabrano=0; trenutno=0; nivovrije=0; } long readVcc() { // Read 1.1V reference against AVcc // set the reference to Vcc and the measurement to the internal 1.1V reference #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) ADMUX = _BV(MUX5) | _BV(MUX0); #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) ADMUX = _BV(MUX3) | _BV(MUX2); #else ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); #endif delay(2); // Wait for Vref to settle ADCSRA |= _BV(ADSC); // Start conversion while (bit_is_set(ADCSRA,ADSC)); // measuring uint8_t low = ADCL; // must read ADCL first - it then locks ADCH uint8_t high = ADCH; // unlocks both long result = (high<<8) | low; result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000 return result; // Vcc in millivolts } void beep(int note, int duration){ //Play tone on buzzerPin tone(Music, note, duration); delay(duration); noTone(Music); delay(50); }
Upload the Code:
Normally I’d recommend studying the code, but a lot of it is Lithuanian. Open your Arduino IDE then copy and paste the code below to a new sketch. Upload the code to your Arduino board and get ready to destroy the Deathstar.
Conclusion
Playing a game on an Arduino with a mini-OLED display is a delightful blend of creativity and technology. This compact setup offers endless opportunities for designing and enjoying your own custom games. Whether you’re creating retro classics or entirely new experiences, the synergy between Arduino and OLED displays provides an immersive and hands-on gaming adventure that showcases the magic of DIY electronics. So, dive into the world of gaming on Arduino and watch your imagination come to life on that tiny, mesmerizing screen.
“Special Thanks to Danko for creating and sharing this project!”
Happy Gaming!
Recommendations:
The Elegoo Super Starter Kit
If you don’t already own any Arduino hardware, we highly recommend this kit as it has everything you need to start programming with Arduino. You can find out more about this kit, including a list of its components here: Elegoo Super Starter Kit
You can find this kit on Amazon here: Elegoo Super Starter Kit
The 0.96-inch Mini-OLED Display
We highly recommend this mini-OLED bundle of five 0.96-inch OLED displays. We have bought these before and they all worked perfectly. You can read more about the mini-OLED here: Mini-OLED
You can find this bundle on Amazon here: OLED Displays
Elegoo Nano (Arduino Compatible)
We have bought these Nano boards many times and can highly recommend them. There are three Nano boards in this pack making them a total bargain for everyone.
You can find this pack on Amazon here: Arduino Nano