Last active
July 18, 2021 14:15
-
-
Save JenkinsDev/7c4cf7863e6c638968add751ac5ace53 to your computer and use it in GitHub Desktop.
LCD Game
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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