concept:
This entire journey starst with me almost losing an eyeI was in the hospital due to an eye operation procedure that presented personal, medical, and academic circumstances I did not expect. After healing and returning home, my family and I had to go to our Emirate, Fujairah. As a result, I found myself without the Arduino kit we were given in class, but instead, a smaller one that I previously had when teaching myself coding throughout the summer. The Arduino kit did not have all of the equipment that a complete kit would offer but only included a potentiometer, a couple of wires, two buttons, a couple of resistors, and a Liquid Display Screen. Yet, rather than being demotivated and giving up, I took this as an opportunity to explore coding on the LCD!
code:
(code that is altered and commented is due to an Arduino update that messed around with the code. Please reach out if you have any questions)
// HAMAD ALSHAMSI // RUN OR DONE: FINAL PROJECT // code // const int buttonPin = 2; // pin for the button // int buttonState = 0; // variable to store the button state // void setup() { // pinMode(buttonPin, INPUT); // set the button pin as an input // Serial.begin(9600); // start the serial connection // } // void loop() { // buttonState = digitalRead(buttonPin); // read the button state // // if the button is pressed, send a "1" over the serial connection // if (buttonState == HIGH) { // Serial.println("1"); // } // delay(100); // delay to prevent sending too many "1"s // } //allow the incorporation of the LCD for the project #include <LiquidCrystal.h> //indicate used pin variables #define pinPress 2 #define pinPlay 1 #define pinWriteNRead 10 #define pinBrightness 12 //indicate variables for game animations used #define runAnimation1 1 #define runAnimation2 2 #define jumpAnimation 3 #define topJumpAnimation '.' #define bottomJumpAnimation 4 #define noObstacleAnimation ' ' #define yesObstacleAnimation 5 #define yesRightObstacleAnimation 6 #define yesLeftObstacleAnimation 7 //fix position for character #define characterPosition 1 //define obstacle attributes #define obstacleYSize 16 #define obstacleBlank 0 #define obstacleBottom 1 #define obstacleTop 2 //define character running attributes and poses when on floor #define characterLocationNul 0 #define characterLocationBottom1 1 #define characterLocationBottom2 2 //define character hopping attributes and poses #define characterLocationHop1 3 #define characterLocationHop2 4 #define characterLocationHop3 5 #define characterLocationHop4 6 #define characterLocationHop5 7 #define characterLocationHop6 8 #define characterLocationHop7 9 #define characterLocationHop8 10 //define character running attributes and poses when on obstacle #define characterLocationRunTop1 11 #define characterLocationRunTop2 12 //LCD attributes and pixel arrangement inspired from Rees5286 on YouTube LiquidCrystal lcd(11, 9, 6, 5, 4, 3); static char obstaclePresentTop[obstacleYSize + 1]; static char obstaclePresentBottom[obstacleYSize + 1]; static bool pushButton = false; //assign specific pixels to light up for corresponding poses void drawCanvasNPixels(){ static byte canvasNPixels[] = { //first running pose B01100, B01100, B00000, B01110, B11100, B01100, B11010, B10011, //second running pose B01100, B01100, B00000, B01100, B01100, B01100, B01100, B01110, //high hop B01100, B01100, B00000, B11110, B01101, B11111, B10000, B00000, //low hop B11110, B01101, B11111, B10000, B00000, B00000, B00000, B00000, //on ground B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111, //right side on ground B00011, B00011, B00011, B00011, B00011, B00011, B00011, B00011, //left side on ground B11000, B11000, B11000, B11000, B11000, B11000, B11000, B11000, }; int i; //code, referenced from AymaanRahman on YouTube, to skip using '0' and allow rapid character alterations for (i = 0; i < 7; ++i) { lcd.createChar(i + 1, &canvasNPixels[i * 8]); } for (i = 0; i < obstacleYSize; ++i) { obstaclePresentTop[i] = noObstacleAnimation; obstaclePresentBottom[i] = noObstacleAnimation; } } //move obstacle void generateObstacles(char* obstacle, byte newObstacle){ for (int i = 0; i < obstacleYSize; ++i) { char current = obstacle[i]; char next = (i == obstacleYSize-1) ? newObstacle : obstacle[i+1]; switch (current){ case noObstacleAnimation: obstacle[i] = (next == yesObstacleAnimation) ? yesRightObstacleAnimation : noObstacleAnimation; break; case yesObstacleAnimation: obstacle[i] = (next == noObstacleAnimation) ? yesLeftObstacleAnimation : yesObstacleAnimation; break; case yesRightObstacleAnimation: obstacle[i] = yesObstacleAnimation; break; case yesLeftObstacleAnimation: obstacle[i] = noObstacleAnimation; break; } } } //move character bool characterDraw(byte position, char* obstaclePresentTop, char* obstaclePresentBottom, unsigned int score) { bool collision = false; char topStore = obstaclePresentTop[characterPosition]; char bottomStore = obstaclePresentBottom[characterPosition]; byte top, bottom; switch (position) { case characterLocationNul: top = bottom = noObstacleAnimation; break; case characterLocationBottom1: top = noObstacleAnimation; bottom = runAnimation1; break; case characterLocationBottom2: top = noObstacleAnimation; bottom = runAnimation2; break; case characterLocationHop1: case characterLocationHop8: top = noObstacleAnimation; bottom = jumpAnimation; break; case characterLocationHop2: case characterLocationHop7: top = topJumpAnimation; bottom = bottomJumpAnimation; break; case characterLocationHop3: case characterLocationHop4: case characterLocationHop5: case characterLocationHop6: top = jumpAnimation; bottom = noObstacleAnimation; break; case characterLocationRunTop1: top = runAnimation1; bottom = noObstacleAnimation; break; case characterLocationRunTop2: top = runAnimation2; bottom = noObstacleAnimation; break; } if (top != ' ') { obstaclePresentTop[characterPosition] = top; collision = (topStore == noObstacleAnimation) ? false : true; } if (bottom != ' ') { obstaclePresentBottom[characterPosition] = bottom; collision |= (bottomStore == noObstacleAnimation) ? false : true; } byte digits = (score > 9999) ? 5 : (score > 999) ? 4 : (score > 99) ? 3 : (score > 9) ? 2 : 1; //create canvas for game obstaclePresentTop[obstacleYSize] = '\0'; obstaclePresentBottom[obstacleYSize] = '\0'; char temp = obstaclePresentTop[16-digits]; obstaclePresentTop[16-digits] = '\0'; lcd.setCursor(0,0); lcd.print(obstaclePresentTop); obstaclePresentTop[16-digits] = temp; lcd.setCursor(0,1); lcd.print(obstaclePresentBottom); lcd.setCursor(16 - digits,0); lcd.print(score); obstaclePresentTop[characterPosition] = topStore; obstaclePresentBottom[characterPosition] = bottomStore; return collision; } //take in digital button signal void buttonPush() { pushButton = true; } //allow the button to act as an interruption to make the character hop void setup(){ pinMode(pinWriteNRead, OUTPUT); digitalWrite(pinWriteNRead, LOW); pinMode(pinBrightness, OUTPUT); digitalWrite(pinBrightness, LOW); pinMode(pinPress, INPUT); digitalWrite(pinPress, HIGH); pinMode(pinPlay, OUTPUT); digitalWrite(pinPlay, HIGH); attachInterrupt(0/*pinPress*/, buttonPush, FALLING); drawCanvasNPixels(); lcd.begin(16, 2); } //constantly check for new obstacle generation void loop(){ static byte characterLoc = characterLocationBottom1; static byte newObstacleType = obstacleBlank; static byte newObstacleDuration = 1; static bool playing = false; static bool blink = false; static unsigned int distance = 0; if (!playing) { characterDraw((blink) ? characterLocationNul : characterLoc, obstaclePresentTop, obstaclePresentBottom, distance >> 3); if (blink) { lcd.setCursor(0,0); lcd.print("Press Start"); } delay(250); blink = !blink; if (pushButton) { drawCanvasNPixels(); characterLoc = characterLocationBottom1; playing = true; pushButton = false; distance = 0; } return; } //constantly move obstacles towards the character generateObstacles(obstaclePresentBottom, newObstacleType == obstacleBottom ? yesObstacleAnimation : noObstacleAnimation); generateObstacles(obstaclePresentTop, newObstacleType == obstacleTop ? yesObstacleAnimation : noObstacleAnimation); //create obstacle repeatedly if (--newObstacleDuration == 0) { if (newObstacleType == obstacleBlank) { newObstacleType = (random(3) == 0) ? obstacleTop : obstacleBottom; newObstacleDuration = 2 + random(10); } else { newObstacleType = obstacleBlank; newObstacleDuration = 10 + random(10); } } //allow character to jump if interruption senses if (pushButton) { if (characterLoc <= characterLocationBottom2) characterLoc = characterLocationHop1; pushButton = false; } //constantly check if character is in collision, if so game ends if (characterDraw(characterLoc, obstaclePresentTop, obstaclePresentBottom, distance >> 3)) { playing = false; } else { if (characterLoc == characterLocationBottom2 || characterLoc == characterLocationHop8) { characterLoc = characterLocationBottom1; } else if ((characterLoc >= characterLocationHop3 && characterLoc <= characterLocationHop5) && obstaclePresentBottom[characterPosition] != noObstacleAnimation) { characterLoc = characterLocationRunTop1; } else if (characterLoc >= characterLocationRunTop1 && obstaclePresentBottom[characterPosition] == noObstacleAnimation) { characterLoc = characterLocationHop5; } else if (characterLoc == characterLocationRunTop2) { characterLoc = characterLocationRunTop1; } else { ++characterLoc; } ++distance; digitalWrite(pinPlay, obstaclePresentBottom[characterPosition + 2] == noObstacleAnimation ? HIGH : LOW); } delay(100); } // references //inspired projects //https://www.youtube.com/@rees5286 //https://rishanda.github.io/BSE_Template_Portfolio/ //learning how to incorporate LCD in project //https://www.youtube.com/watch?v=EAeuxjtkumM&ab_channel=AymaanRahman //https://www.youtube.com/watch?v=dZZynJLmTn8&ab_channel=HowToMechatronics