// A simple T-rex Run game based on the 30DLIS kit
// Developed by Ishani and Arijit Sengupta
// Required libraries
#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 650
#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.setContrast(2);
My_u8g_Panel.drawStr( 34, 10, "Dino Game");
My_u8g_Panel.drawStr( 30, 30, "Push to start");
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();
}