Skip to content

Instantly share code, notes, and snippets.

@JenkinsDev
Last active July 18, 2021 14:15
Show Gist options
  • Save JenkinsDev/7c4cf7863e6c638968add751ac5ace53 to your computer and use it in GitHub Desktop.
Save JenkinsDev/7c4cf7863e6c638968add751ac5ace53 to your computer and use it in GitHub Desktop.
LCD Game
#include <LiquidCrystal.h>
#define JOYSTICK_X_ANALOG_PIN A0
#define JOYSTICK_Y_ANALOG_PIN A1
const int LCD_SCREEN_CHAR_WIDTH = 16;
const int LCD_SCREEN_ROW_HEIGHT = 2;
const int BUTTON_PIN = 7;
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
const int FPS = 50;
const float SCREEN_REFRESH_RATE = 60 / FPS;
const float SCREEN_REFRESH_RATE_MS = SCREEN_REFRESH_RATE * 100;
unsigned long lastRefresh;
int score = 0;
char gameBoard[LCD_SCREEN_ROW_HEIGHT][LCD_SCREEN_CHAR_WIDTH] = {
{ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 5 },
{ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }
};
int highlightedCell[2] = {0, 0};
int nextHighlightedCell[2] = {0, 0};
bool didFlash = false;
byte ground[8] = {
B00000,
B00000,
B00000,
B00000,
B00000,
B00000,
B00000,
B11111
};
byte birdFlapUp[8] = {
B00000,
B00000,
B00100,
B11110,
B00000,
B00000,
B00000,
B00000
};
byte birdFlapDown[8] = {
B00000,
B00000,
B00000,
B11110,
B00100,
B00000,
B00000,
B00000
};
byte enemyBirdFlapUp[8] = {
B00000,
B00000,
B00100,
B01111,
B00000,
B00000,
B00000,
B00000
};
byte enemyBirdFlapDown[8] = {
B00000,
B00000,
B00000,
B01111,
B00100,
B00000,
B00000,
B00000
};
///// RENDERING LOGIC
void setupLcd() {
lcd.begin(LCD_SCREEN_CHAR_WIDTH, LCD_SCREEN_ROW_HEIGHT);
}
void addGround(byte graphic[8]) {
for (int i=0; i<8; i++) {
graphic[i] = graphic[i] | ground[i];
}
}
void writeGameboardStateToLcd() {
for (int row = 0; row < LCD_SCREEN_ROW_HEIGHT; row++) {
lcd.setCursor(0, row);
lcd.write(gameBoard[row]);
}
lcd.setCursor(highlightedCell[1], highlightedCell[0]);
if (!didFlash) {
didFlash = true;
lcd.write(byte(0));
} else {
didFlash = false;
lcd.write(byte(1));
}
}
int getAxisMovement(int axisAnalogPin) {
int axisInt = analogRead(axisAnalogPin);
if (axisInt <= 300) {
return -1;
} else if (axisInt >= 700) {
return 1;
} else {
return 0;
}
}
int getXAxisMovement() { return getAxisMovement(JOYSTICK_X_ANALOG_PIN); }
int getYAxisMovement() { return getAxisMovement(JOYSTICK_Y_ANALOG_PIN); }
int clamp(int minVal, int maxVal, int val) {
if (val <= minVal) return minVal;
else if (val >= maxVal) return maxVal;
else return val;
}
void updateToNextHighlightedCellPos() {
highlightedCell[0] = nextHighlightedCell[0];
highlightedCell[1] = nextHighlightedCell[1];
}
void updateNextCursorPos(int xPos, int yPos) {
if (nextHighlightedCell[0] == highlightedCell[0]) {
nextHighlightedCell[0] = yPos;
}
if (nextHighlightedCell[1] == highlightedCell[1]) {
nextHighlightedCell[1] = xPos;
}
}
void animateRenderedFrames() {
for (int row=0; row<2; row++) {
for (int col=0; col<16; col++) {
int frameByte = gameBoard[row][col];
if (frameByte == 5) {
gameBoard[row][col] = 7;
if (col-1 >= 0) gameBoard[row][col-1] = 6;
}
else if (frameByte == 6) {
gameBoard[row][col] = 7;
if (col-1 >= 0) gameBoard[row][col-1] = 5;
}
}
}
}
bool didGenerateEnemyLastFrame = false;
bool shouldGenerateEnemy() {
if (didGenerateEnemyLastFrame) {
didGenerateEnemyLastFrame = false;
return didGenerateEnemyLastFrame;
}
else {
int randNumber = random(101);
if (randNumber >= 75) {
didGenerateEnemyLastFrame = true;
if (randNumber % 2 == 0) {
gameBoard[0][15] = 5;
} else {
gameBoard[1][15] = 5;
}
}
}
return didGenerateEnemyLastFrame;
}
void setup() {
Serial.begin(9600);
lastRefresh = millis();
setupLcd();
addGround(birdFlapUp);
addGround(birdFlapDown);
addGround(enemyBirdFlapUp);
addGround(enemyBirdFlapDown);
lcd.createChar(0, birdFlapUp);
lcd.createChar(1, birdFlapDown);
lcd.createChar(5, enemyBirdFlapUp);
lcd.createChar(6, enemyBirdFlapDown);
lcd.createChar(7, ground);
}
void loop() {
int xAxisMovement = getXAxisMovement();
int nextXAxisPos = highlightedCell[1] + xAxisMovement;
int yAxisMovement = getYAxisMovement();
int nextYAxisPos = highlightedCell[0] + yAxisMovement;
updateNextCursorPos(
clamp(0, LCD_SCREEN_CHAR_WIDTH-1, nextXAxisPos),
clamp(0, LCD_SCREEN_ROW_HEIGHT-1, nextYAxisPos)
);
if (millis() - lastRefresh < SCREEN_REFRESH_RATE_MS) {
return;
}
lcd.clear();
if (!didFlash) updateToNextHighlightedCellPos();
animateRenderedFrames();
shouldGenerateEnemy();
writeGameboardStateToLcd();
// game over
if (gameBoard[highlightedCell[0]][highlightedCell[1]] == 5 || gameBoard[highlightedCell[0]][highlightedCell[1]] == 6) {
lcd.clear();
lcd.write(" GAME OVER! ");
lcd.setCursor(0, 1);
lcd.write((String(" Score: ") + String(score)).c_str());
while(true) {};
}
int rowToCheck = highlightedCell[0] == 0 ? 1 : 0;
if (gameBoard[rowToCheck][highlightedCell[1] ] == 5 || gameBoard[rowToCheck][highlightedCell[1]] == 6) {
score += 100 * (FPS/15);
}
lastRefresh = millis();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment