• 082226375801
  • alfianmaarif@ee.uad.ac.id
  • Yogyakarta
Materi
Bikin GAME DINO OLED DISPLAY Pake Arduino UNO di Simulator WOKWI

Bikin GAME DINO OLED DISPLAY Pake Arduino UNO di Simulator WOKWI


Tutorial Pembuatan Game Dino Runner Menggunakan Arduino UNO dan OLED Display SSD1306 pada Simulator WOKWI

Pada bagian ini akan dibahas pembuatan proyek game interaktif Dino Runner berbasis logika lompat rintangan sederhana menggunakan mikrokontroler Arduino UNO, layar OLED Display SSD1306 0.96 inci (I2C), dan tombol push button sebagai kontroler, serta software simulasi WOKWI. Skematik rangkaian ditunjukkan pada Gambar 1, program Arduino pada Kode 1, dan video pembuatan pada tautan Video 1. Grafis dinosaurus dan rintangan dirender secara bitmapped langsung pada layar OLED. Pada saat tombol ditekan, karakter Dino akan melompat untuk menghindari rintangan yang bergerak, serta skor permainan akan terus bertambah dan ditampilkan secara real-time hingga kondisi game over tercapai.

Gambar 1. Skematik rangkaian sistem

Pemetaan PIN:
* OLED DISPLAY 0.96 INCH I2C
GND => GND
VCC => 5V
SDA => SDA
SCL => SCL
* SEVEN SEGMENT
VCC => 5V
DCLK => 6
DIO => 5
GND => GND
* BUTTON
GND – GND
OUT – 3
* BUZZER
GND – GND
OUT – 10

Kode Arduino 1. Kode Program

//ALFIAN CENTER
//SUBSCRIBE
//=-=-=-=-=--=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=--=-=


#include <U8glib.h>
#include "TM1637Display.h"

// HW Pins required
#define SW 3 // RotaryEncoder SW Pin
#define DT 4 // RotaryEncoder DT Pin
#define CLK 2 // RotaryEncoder CLK Pin

// Define the display connections pins:
#define DCLK 6
#define DIO 5

// pin 10 drives the buzzer
#define buzzer  10

// In Addition, this sketch uses the I2C Pins for the U8G Panel
// A1+A2

// Create display object of type TM1637Display
// The display shows the current score
TM1637Display OurDisplay = TM1637Display(DCLK, DIO);

// Create array that turns all segments off:
const uint8_t blank[] = {0x00, 0x00, 0x00, 0x00}; // 0xff is a hexidecimal number whose binary
// representation is all zeros

// Create the oled display object - this shows gameplay
// and welcome/win/loss messages

U8GLIB_SH1106_128X64  My_u8g_Panel(U8G_I2C_OPT_NONE); // I2C / TWI

/*
  For documentation on u8glib functions:
  https://github.com/olikraus/u8glib/wiki/userreference
*/

// Sounds we use for the hit effects
#define jumpSound 700
#define blahSound 125
#define speedSound 1000
#define DBOUNCE 180

// Game states
#define gameStart 0
#define gameEnd 1
#define gamePlaying 2

volatile int gameStatus = gameStart;

// PRE-SAVED BITMAP CONSTANTS
//20 x 21
static unsigned char dinoJump [] U8G_PROGMEM = {
  0x00, 0xFC, 0x07, 0x00, 0xFE, 0x07, 0x00, 0xEE, 0x0F, 0x00, 0xFE, 0x0F,
  0x00, 0xFE, 0x0F, 0x00, 0xFE, 0x0F, 0x00, 0xFE, 0x07, 0x06, 0xFF, 0x03,
  0xC3, 0xFF, 0x00, 0xE7, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  0xFF, 0x3F, 0x00, 0xFE, 0x3F, 0x00, 0xFC, 0x1F, 0x00, 0xF8, 0x1F, 0x00,
  0xF0, 0x1F, 0x00, 0xF0, 0x0E, 0x00, 0x60, 0x0E, 0x00, 0xE0, 0x0E, 0x00,
  0xE0, 0x1E, 0x00,
};
//20 x 21
static unsigned char dinoLeft [] U8G_PROGMEM = {
  0x00, 0xFC, 0x07, 0x00, 0xFE, 0x07, 0x00, 0xEE, 0x0F, 0x00, 0xFE, 0x0F,
  0x00, 0xFE, 0x0F, 0x00, 0x7E, 0x08, 0x00, 0x7E, 0x00, 0x06, 0xFF, 0x03,
  0x87, 0x3F, 0x00, 0xE7, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  0xFF, 0x3F, 0x00, 0xFE, 0x3F, 0x00, 0xFC, 0x1F, 0x00, 0xF8, 0x1F, 0x00,
  0xF0, 0x1F, 0x00, 0xE0, 0x1E, 0x00, 0x60, 0x00, 0x00, 0xE0, 0x00, 0x00,
  0xE0, 0x00, 0x00,
};
//20 x 21
static unsigned char dinoRight [] U8G_PROGMEM = {
  0x00, 0xFC, 0x07, 0x00, 0xEE, 0x07, 0x00, 0xE6, 0x0F, 0x00, 0xFE, 0x0F,
  0x00, 0xFE, 0x0F, 0x00, 0xFE, 0x0F, 0x00, 0x7C, 0x00, 0x06, 0xFF, 0x03,
  0xC3, 0xFF, 0x00, 0xE7, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  0xFF, 0x3F, 0x00, 0xFE, 0x3F, 0x00, 0xFC, 0x1F, 0x00, 0xF8, 0x1F, 0x00,
  0xF0, 0x1F, 0x00, 0xF0, 0x0F, 0x00, 0xE0, 0x0E, 0x00, 0x00, 0x0E, 0x00,
  0x00, 0x1E, 0x00,
};
//shape 0:  39 x 12
static unsigned char cloud [] U8G_PROGMEM = {
  0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xBE, 0x03, 0x00, 0x00, 0x00,
  0x03, 0x06, 0x00, 0x00, 0x80, 0x01, 0x04, 0x00, 0x00, 0x40, 0x00, 0x1C,
  0x00, 0x00, 0x40, 0x00, 0xE4, 0x03, 0x00, 0x18, 0x00, 0x00, 0x02, 0xE0,
  0x0F, 0x00, 0x00, 0x0E, 0x30, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,
  0x00, 0x20, 0x12, 0x00, 0x00, 0x00, 0x40, 0x03, 0xFF, 0xFF, 0xFF, 0x7F,
};
//shape 1: 10 x 20
static unsigned char oneCactus [] U8G_PROGMEM = {
  0x30, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x01, 0xFB, 0x03,
  0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x01,
  0xFE, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00,
  0x78, 0x00, 0x00, 0x00,
};
//shape 2: 20 x 20
static unsigned char twoCactus [] U8G_PROGMEM = {
  0x30, 0xC0, 0x00, 0x38, 0xE0, 0x00, 0x38, 0xE8, 0x00, 0x38, 0xEC, 0x00,
  0x38, 0xED, 0x04, 0xBB, 0xED, 0x0E, 0xBB, 0xED, 0x0E, 0xBB, 0xFD, 0x0E,
  0xBB, 0xFD, 0x0E, 0xBB, 0xF9, 0x0E, 0xFF, 0xF1, 0x0F, 0xFF, 0xE0, 0x07,
  0x7E, 0xE0, 0x01, 0x38, 0xE0, 0x00, 0x38, 0xE0, 0x00, 0x38, 0xE0, 0x00,
  0x38, 0xE0, 0x00, 0x38, 0xE0, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x00, 0x00,
};

//shape 3: 20 x 20
static unsigned char threeCactus [] U8G_PROGMEM = {
  0x00, 0xC0, 0x00, 0x18, 0xC0, 0x01, 0x18, 0xC0, 0x01, 0x58, 0xD8, 0x01,
  0x58, 0xFC, 0x01, 0x58, 0xFC, 0x0F, 0x78, 0xDC, 0x0F, 0x7F, 0xFC, 0x0F,
  0x3B, 0xFD, 0x0D, 0x1B, 0xF9, 0x0C, 0x5B, 0xF5, 0x0F, 0x5B, 0xC5, 0x07,
  0x5F, 0xE7, 0x03, 0xDE, 0xE7, 0x01, 0xD8, 0xC3, 0x01, 0x98, 0xC1, 0x01,
  0x18, 0xC1, 0x01, 0x18, 0xC1, 0x01, 0x18, 0xE1, 0x01, 0x00, 0x00, 0x00,
};
//shape 4: 6 x 12
static unsigned char oneCactusSmall [] U8G_PROGMEM = {
  0x0C, 0x0C, 0x3C, 0x3D, 0x2D, 0x3D, 0x1D, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C,
};
//shape 5: 12 x 12
static unsigned char twoCactusSmall [] U8G_PROGMEM = {
  0x0C, 0x03, 0x0C, 0x03, 0x6C, 0x0B, 0x6D, 0x0B, 0x6D, 0x0B, 0xBD, 0x0B,
  0x1F, 0x0F, 0x0E, 0x03, 0x0C, 0x03, 0x0C, 0x03, 0x0C, 0x03, 0x0C, 0x03,
};
//shape 6: 17 x 12
static unsigned char threeCactusSmall [] U8G_PROGMEM = {
  0x04, 0x41, 0x00, 0x0C, 0x61, 0x00, 0xFC, 0x79, 0x01, 0xFD, 0x7D, 0x01,
  0x7D, 0x6D, 0x01, 0x7D, 0x7D, 0x01, 0xCF, 0xE5, 0x01, 0xCE, 0x67, 0x00,
  0x8C, 0x67, 0x00, 0x0C, 0x63, 0x00, 0x0C, 0x61, 0x00, 0x0C, 0x61, 0x00,
};

static unsigned char dinoBlah [] U8G_PROGMEM = {
  0x00, 0xFC, 0x07, 0x00, 0xFE, 0x07, 0x00, 0xC6, 0x0F, 0x00, 0xC6, 0x0F,
  0x00, 0xCE, 0x0F, 0x00, 0xFE, 0x0F, 0x00, 0xFE, 0x0F, 0x06, 0xFF, 0x03,
  0x87, 0x7F, 0x00, 0xE7, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  0xFF, 0x3F, 0x00, 0xFE, 0x3F, 0x00, 0xFC, 0x1F, 0x00, 0xF8, 0x1F, 0x00,
  0xF0, 0x1F, 0x00, 0xF0, 0x0E, 0x00, 0x60, 0x0E, 0x00, 0x60, 0x0E, 0x00,
  0xE0, 0x1E, 0x00,
};

static unsigned char gameOver [] U8G_PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0xF8, 0x01, 0x02, 0x0C, 0x0C, 0x3E, 0x00, 0x78, 0x60,
  0x30, 0x7C, 0xF0, 0x01, 0x0C, 0x01, 0x07, 0x14, 0x0A, 0x02, 0x00, 0x84,
  0x40, 0x10, 0x04, 0x10, 0x02, 0x04, 0x00, 0x05, 0x14, 0x0A, 0x02, 0x00,
  0x02, 0x41, 0x10, 0x04, 0x10, 0x02, 0x04, 0x00, 0x05, 0x14, 0x0A, 0x02,
  0x00, 0x02, 0xC1, 0x18, 0x04, 0x10, 0x02, 0xC4, 0x81, 0x0D, 0x34, 0x0B,
  0x3E, 0x00, 0x02, 0x81, 0x08, 0x7C, 0xF0, 0x01, 0x04, 0x81, 0x08, 0x24,
  0x09, 0x02, 0x00, 0x02, 0x81, 0x0D, 0x04, 0x10, 0x01, 0x04, 0x81, 0x0F,
  0x64, 0x09, 0x02, 0x00, 0x02, 0x01, 0x05, 0x04, 0x10, 0x02, 0x0C, 0xC1,
  0x18, 0xC4, 0x08, 0x02, 0x00, 0x84, 0x00, 0x05, 0x04, 0x10, 0x02, 0xF8,
  0x41, 0x10, 0xC4, 0x08, 0x3E, 0x00, 0x78, 0x00, 0x07, 0x7C, 0x10, 0x02,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00,
};


// Other Game attributes

// various variables
int currentStateCLK;
int lastStateCLK;
int MyScore = 0;
int dinoMove = 0;
volatile int jumping = 0;
int cloudx = 128;
int obstacles [2] = {1, 4};
int obstaclex [2] = {128, 200};
int speed = 8;
unsigned long startTime = millis(), curTime;
int lastBeep = 0;

// Display the score on the 7seg display
void ShowScore () {
  if (gameStatus == gamePlaying) {
    curTime = millis ();
    MyScore = (curTime - startTime) * speed / 1000;
    OurDisplay.showNumberDecEx(MyScore);
    if (MyScore / 100 > lastBeep) {
      tone (buzzer, 1000, 100);
      delay (150);
      tone (buzzer, 1250, 100);
      lastBeep = MyScore / 100;
    }
  }
}


void StartStopGame () {
  static unsigned long last_interrupt = 0;
  if (millis() - last_interrupt > DBOUNCE) {
    if (gameStatus == gamePlaying) {
      if (jumping == 0) {
        jumping = 1;
        tone (buzzer, jumpSound, 100);
      }
    }
    else if (gameStatus == gameStart) gameStatus = gamePlaying;
    else gameStatus = gameStart;
  }
  last_interrupt = millis(); //note the last time the ISR was called

}

void resetGame () {
  MyScore = 0;
  startTime = millis();
  obstaclex[0] = 128;
  obstaclex [1] = 200;
  dinoMove = 0;
}

void setup() {
  OurDisplay.setBrightness(7);
  OurDisplay.clear();
  resetGame();
  ShowScore ();
  pinMode(CLK, INPUT);
  pinMode(DT, INPUT);
  pinMode(SW, INPUT_PULLUP);
  pinMode(buzzer, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(SW), StartStopGame, FALLING);
}

// ************************************************
void loop(void) {
  ShowScore();
  My_u8g_Panel.firstPage();
  do {
    draw();
  } while ( My_u8g_Panel.nextPage() );
  if (gameStatus == gamePlaying) {
    moveDino();
    moveCloud();
    moveObstacles();
  }
}

void moveDino () {
  if (jumping == 0) dinoMove = (dinoMove + 1) % 3;
  else {
    if (jumping == 1) {
      dinoMove = dinoMove + 8;
      if (dinoMove > 32) jumping = 2;
    } else {
      dinoMove = dinoMove - 8;
      if (dinoMove < 8) {
        jumping = 0;
        dinoMove = 0;
      }
    }
  }
  checkCollision ();
}

void moveCloud () {
  cloudx --;
  if (cloudx < -38) cloudx = 128;
}

void moveObstacles() {
  int obx = obstaclex [0];
  obx = obx - speed;
  if (obx < -20) {
    obstaclex[0] = obstaclex[1];
    obstaclex[1] = obstaclex[0] + random(80, 125);
    obstacles[0] = obstacles[1];
    obstacles[1] = random (1, 6);
  }
  else {
    obstaclex[0] = obx;
    obstaclex[1] -= speed;
  }

}
// ************************************************

void draw(void) {
  u8g_prepare();
  if (gameStatus == gamePlaying) {
    drawDino ();
    drawShape (0, cloudx);
    drawObsticles ();
  }
  else if (gameStatus == gameStart) {
    My_u8g_Panel.drawStr( 0, 0, "ALFIAN CENTER");
    My_u8g_Panel.drawStr( 0, 20, "Welcome to Dino!!");
    My_u8g_Panel.drawStr( 0, 50, "Push to begin");
    resetGame();
    ShowScore();
  }
  else {
    My_u8g_Panel.drawXBMP( 14, 12, 100, 15, gameOver);
    drawDino ();
    drawShape (0, cloudx);
    drawObsticles ();
  }

}


void drawDino (void) {
  if (gameStatus == gameEnd) {
    My_u8g_Panel.drawXBMP( 0, 43 - dinoMove, 20, 21, dinoBlah);
    return;
  }
  switch (dinoMove) {
    case -1: My_u8g_Panel.drawXBMP( 0, 43, 20, 21, dinoBlah); break;
    case 0: My_u8g_Panel.drawXBMP( 0, 43, 20, 21, dinoJump); break;
    case 1: My_u8g_Panel.drawXBMP( 0, 43, 20, 21, dinoLeft); break;
    case 2: My_u8g_Panel.drawXBMP( 0, 43, 20, 21, dinoRight); break;
    default: My_u8g_Panel.drawXBMP( 0, 43 - dinoMove, 20, 21, dinoJump); break;
  }
}

void drawShape (int shape, int x) {
  switch (shape) {
    case 0: My_u8g_Panel.drawXBMP( x, 5, 39, 12, cloud); break;
    case 1: My_u8g_Panel.drawXBMP( x, 44, 10, 20, oneCactus); break;
    case 2: My_u8g_Panel.drawXBMP( x, 44, 20, 20, twoCactus); break;
    case 3: My_u8g_Panel.drawXBMP( x, 44, 20, 20, threeCactus); break;
    case 4: My_u8g_Panel.drawXBMP( x, 52, 6, 12, oneCactusSmall); break;
    case 5: My_u8g_Panel.drawXBMP( x, 52, 12, 12, twoCactusSmall); break;
    case 6: My_u8g_Panel.drawXBMP( x, 52, 17, 12, threeCactusSmall); break;
  }
}

void checkCollision () {
  int obx = obstaclex [0];
  int obw, obh;

  switch (obstacles[0]) {
    case 0: obw =  39; obh = 10; break;
    case 1: obw = 10; obh = 20; break;
    case 2: obw = 17; obh = 20; break;
    case 3: obw = 17; obh = 20; break;
    case 4: obw = 6; obh = 12; break;
    case 5: obw = 12; obh = 12; break;
    case 6: obw = 17; obh = 12; break;
  }
  if (obx > 15 || obx + obw < 5 || dinoMove > obh - 3) {}
  else {
    gameStatus = gameEnd;
    tone (buzzer, 125, 100);
    delay(150);
    tone (buzzer, 125, 100);
  }
}
void drawObsticles () {
  drawShape (obstacles[0], obstaclex[0]);
  drawShape (obstacles[1], obstaclex[1]);
}
void u8g_prepare(void) {
  My_u8g_Panel.setFont(u8g_font_6x10);
  My_u8g_Panel.setFontRefHeightExtendedText();
  My_u8g_Panel.setDefaultForegroundColor();
  My_u8g_Panel.setFontPosTop();
}

Video 1. Video tutorial pembuatan simulasi di WOKWI

Leave a Reply

Your email address will not be published. Required fields are marked *