Components and supplies
Switch Actuator, APEM A01 series Illuminated Push-Button Switches
Plastic Enclosure, Precision Miniature Case
Arduino UNO
Jumper wires (generic)
Tools and machines
Solder Wire, Lead Free
Soldering iron (generic)
Apps and platforms
Arduino IDE
Project description
Code
endless_run_game
c_cpp
written in arduino programming language and c++
1 2// Arduino Endless Run game with Lcd i2c Screen 3 4#include <LiquidCrystal_I2C.h> 5#include <Wire.h> 6 7#define PIN_BUTTON 2 8#define PIN_AUTOPLAY 1 9#define PIN_READWRITE 10 10#define PIN_CONTRAST 7 11 12 #define SPRITE_RUN1 1 13#define SPRITE_RUN2 2 14#define SPRITE_JUMP 3 15#define SPRITE_JUMP_UPPER '.' 16#define SPRITE_JUMP_LOWER 4 17#define SPRITE_TERRAIN_EMPTY ' ' 18#define SPRITE_TERRAIN_SOLID 5 19#define SPRITE_TERRAIN_SOLID_RIGHT 6 20#define SPRITE_TERRAIN_SOLID_LEFT 7 21 22#define HERO_HORIZONTAL_POSITION 1 // Horizontal position of hero on screen 23 24#define TERRAIN_WIDTH 16 25#define TERRAIN_EMPTY 0 26#define TERRAIN_LOWER_BLOCK 1 27#define TERRAIN_UPPER_BLOCK 2 28 29#define HERO_POSITION_OFF 0 // Hero is invisible 30#define HERO_POSITION_RUN_LOWER_1 1 31#define HERO_POSITION_RUN_LOWER_2 2 32 33#define HERO_POSITION_JUMP_1 3 // Starting a jump 34#define HERO_POSITION_JUMP_2 4 // Half-way up 35#define HERO_POSITION_JUMP_3 5 // Jump is on upper row 36#define HERO_POSITION_JUMP_4 6 // Jump is on upper row 37#define HERO_POSITION_JUMP_5 7 // Jump is on upper row 38#define HERO_POSITION_JUMP_6 8 // Jump is on upper row 39#define HERO_POSITION_JUMP_7 9 // Half-way down 40#define HERO_POSITION_JUMP_8 10 // About to land 41 42#define HERO_POSITION_RUN_UPPER_1 11 // Hero is running on upper row (pose 1) 43#define HERO_POSITION_RUN_UPPER_2 12 // (pose 2) 44 45LiquidCrystal_I2C lcd(0x27,16,2); 46 47static char terrainUpper[TERRAIN_WIDTH + 1]; 48static char terrainLower[TERRAIN_WIDTH + 1]; 49static bool buttonPushed = false; 50 51void initializeGraphics(){ 52 static byte graphics[] = { 53 // Run position 1 54 B01100, 55 B01100, 56 B00000, 57 B01110, 58 B11100, 59 B01100, 60 B11010, 61 B10011, 62 // Run position 2 63 B01100, 64 B01100, 65 B00000, 66 B01100, 67 B01100, 68 B01100, 69 B01100, 70 B01110, 71 // Jump 72 B01100, 73 B01100, 74 B00000, 75 B11110, 76 B01101, 77 B11111, 78 B10000, 79 B00000, 80 // Jump lower 81 B11110, 82 B01101, 83 B11111, 84 B10000, 85 B00000, 86 B00000, 87 B00000, 88 B00000, 89 // Ground 90 B11111, 91 B11111, 92 B11111, 93 B11111, 94 B11111, 95 B11111, 96 B11111, 97 B11111, 98 // Ground right 99 B00011, 100 B00011, 101 B00011, 102 B00011, 103 B00011, 104 B00011, 105 B00011, 106 B00011, 107 // Ground left 108 B11000, 109 B11000, 110 B11000, 111 B11000, 112 B11000, 113 B11000, 114 B11000, 115 B11000, 116 }; 117 int i; 118 // Skip using character 0, this allows lcd.print() to be used to 119 // quickly draw multiple characters 120 for (i = 0; i < 7; ++i) { 121 lcd.createChar(i + 1, &graphics[i * 8]); 122 } 123 for (i = 0; i < TERRAIN_WIDTH; ++i) { 124 terrainUpper[i] = SPRITE_TERRAIN_EMPTY; 125 terrainLower[i] = SPRITE_TERRAIN_EMPTY; 126 } 127} 128 129// Slide the terrain to the left in half-character increments 130// 131void advanceTerrain(char* terrain, byte newTerrain){ 132 for (int i = 0; i < TERRAIN_WIDTH; ++i) { 133 char current = terrain[i]; 134 char next = (i == TERRAIN_WIDTH-1) ? newTerrain : terrain[i+1]; 135 switch (current){ 136 case SPRITE_TERRAIN_EMPTY: 137 terrain[i] = (next == SPRITE_TERRAIN_SOLID) ? SPRITE_TERRAIN_SOLID_RIGHT : SPRITE_TERRAIN_EMPTY; 138 break; 139 case SPRITE_TERRAIN_SOLID: 140 terrain[i] = (next == SPRITE_TERRAIN_EMPTY) ? SPRITE_TERRAIN_SOLID_LEFT : SPRITE_TERRAIN_SOLID; 141 break; 142 case SPRITE_TERRAIN_SOLID_RIGHT: 143 terrain[i] = SPRITE_TERRAIN_SOLID; 144 break; 145 case SPRITE_TERRAIN_SOLID_LEFT: 146 terrain[i] = SPRITE_TERRAIN_EMPTY; 147 break; 148 } 149 } 150} 151 152bool drawHero(byte position, char* terrainUpper, char* terrainLower, unsigned int score) { 153 bool collide = false; 154 char upperSave = terrainUpper[HERO_HORIZONTAL_POSITION]; 155 char lowerSave = terrainLower[HERO_HORIZONTAL_POSITION]; 156 byte upper, lower; 157 switch (position) { 158 case HERO_POSITION_OFF: 159 upper = lower = SPRITE_TERRAIN_EMPTY; 160 break; 161 case HERO_POSITION_RUN_LOWER_1: 162 upper = SPRITE_TERRAIN_EMPTY; 163 lower = SPRITE_RUN1; 164 break; 165 case HERO_POSITION_RUN_LOWER_2: 166 upper = SPRITE_TERRAIN_EMPTY; 167 lower = SPRITE_RUN2; 168 break; 169 case HERO_POSITION_JUMP_1: 170 case HERO_POSITION_JUMP_8: 171 upper = SPRITE_TERRAIN_EMPTY; 172 lower = SPRITE_JUMP; 173 break; 174 case HERO_POSITION_JUMP_2: 175 case HERO_POSITION_JUMP_7: 176 upper = SPRITE_JUMP_UPPER; 177 lower = SPRITE_JUMP_LOWER; 178 break; 179 case HERO_POSITION_JUMP_3: 180 case HERO_POSITION_JUMP_4: 181 case HERO_POSITION_JUMP_5: 182 case HERO_POSITION_JUMP_6: 183 upper = SPRITE_JUMP; 184 lower = SPRITE_TERRAIN_EMPTY; 185 break; 186 case HERO_POSITION_RUN_UPPER_1: 187 upper = SPRITE_RUN1; 188 lower = SPRITE_TERRAIN_EMPTY; 189 break; 190 case HERO_POSITION_RUN_UPPER_2: 191 upper = SPRITE_RUN2; 192 lower = SPRITE_TERRAIN_EMPTY; 193 break; 194 } 195 if (upper != ' ') { 196 terrainUpper[HERO_HORIZONTAL_POSITION] = upper; 197 collide = (upperSave == SPRITE_TERRAIN_EMPTY) ? false : true; 198 } 199 if (lower != ' ') { 200 terrainLower[HERO_HORIZONTAL_POSITION] = lower; 201 collide |= (lowerSave == SPRITE_TERRAIN_EMPTY) ? false : true; 202 } 203 204 byte digits = (score > 9999) ? 5 : (score > 999) ? 4 : (score > 99) ? 3 : (score > 9) ? 2 : 1; 205 206 // Draw the scene 207 terrainUpper[TERRAIN_WIDTH] = '\\0'; 208 terrainLower[TERRAIN_WIDTH] = '\\0'; 209 char temp = terrainUpper[16-digits]; 210 terrainUpper[16-digits] = '\\0'; 211 lcd.setCursor(0,0); 212 lcd.print(terrainUpper); 213 terrainUpper[16-digits] = temp; 214 lcd.setCursor(0,1); 215 lcd.print(terrainLower); 216 217 lcd.setCursor(16 - digits,0); 218 lcd.print(score); 219 220 terrainUpper[HERO_HORIZONTAL_POSITION] = upperSave; 221 terrainLower[HERO_HORIZONTAL_POSITION] = lowerSave; 222 return collide; 223} 224 225// Handle the button push as an interrupt 226void buttonPush() { 227 buttonPushed = true; 228} 229 230void setup(){ 231 pinMode(PIN_READWRITE, OUTPUT); 232 digitalWrite(PIN_READWRITE, LOW); 233 pinMode(PIN_CONTRAST, OUTPUT); 234 digitalWrite(PIN_CONTRAST, LOW); 235 pinMode(PIN_BUTTON, INPUT); 236 digitalWrite(PIN_BUTTON, HIGH); 237 pinMode(PIN_AUTOPLAY, OUTPUT); 238 digitalWrite(PIN_AUTOPLAY, HIGH); 239 lcd.backlight(); 240 241 lcd.begin(16,2); 242 243 // Digital pin 2 maps to interrupt 0 244 attachInterrupt(0/*PIN_BUTTON*/, buttonPush, FALLING); 245 246 initializeGraphics(); 247 248// lcd.begin(); 249} 250 251void loop(){ 252 static byte heroPos = HERO_POSITION_RUN_LOWER_1; 253 static byte newTerrainType = TERRAIN_EMPTY; 254 static byte newTerrainDuration = 1; 255 static bool playing = false; 256 static bool blink = false; 257 static unsigned int distance = 0; 258 259 if (!playing) { 260 drawHero((blink) ? HERO_POSITION_OFF : heroPos, terrainUpper, terrainLower, distance >> 3); 261 if (blink) { 262 lcd.setCursor(0,0); 263 lcd.print("Press Start"); 264 } 265 delay(250); 266 blink = !blink; 267 if (buttonPushed) { 268 initializeGraphics(); 269 heroPos = HERO_POSITION_RUN_LOWER_1; 270 playing = true; 271 buttonPushed = false; 272 distance = 0; 273 } 274 return; 275 } 276 277 // Shift the terrain to the left 278 advanceTerrain(terrainLower, newTerrainType == TERRAIN_LOWER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY); 279 advanceTerrain(terrainUpper, newTerrainType == TERRAIN_UPPER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY); 280 281 // Make new terrain to enter on the right 282 if (--newTerrainDuration == 0) { 283 if (newTerrainType == TERRAIN_EMPTY) { 284 newTerrainType = (random(3) == 0) ? TERRAIN_UPPER_BLOCK : TERRAIN_LOWER_BLOCK; 285 newTerrainDuration = 2 + random(10); 286 } else { 287 newTerrainType = TERRAIN_EMPTY; 288 newTerrainDuration = 10 + random(10); 289 } 290 } 291 292 if (buttonPushed) { 293 if (heroPos <= HERO_POSITION_RUN_LOWER_2) heroPos = HERO_POSITION_JUMP_1; 294 buttonPushed = false; 295 } 296 297 if (drawHero(heroPos, terrainUpper, terrainLower, distance >> 3)) { 298 playing = false; // The hero collided with something. Too bad. 299 } else { 300 if (heroPos == HERO_POSITION_RUN_LOWER_2 || heroPos == HERO_POSITION_JUMP_8) { 301 heroPos = HERO_POSITION_RUN_LOWER_1; 302 } else if ((heroPos >= HERO_POSITION_JUMP_3 && heroPos <= HERO_POSITION_JUMP_5) && terrainLower[HERO_HORIZONTAL_POSITION] != SPRITE_TERRAIN_EMPTY) { 303 heroPos = HERO_POSITION_RUN_UPPER_1; 304 } else if (heroPos >= HERO_POSITION_RUN_UPPER_1 && terrainLower[HERO_HORIZONTAL_POSITION] == SPRITE_TERRAIN_EMPTY) { 305 heroPos = HERO_POSITION_JUMP_5; 306 } else if (heroPos == HERO_POSITION_RUN_UPPER_2) { 307 heroPos = HERO_POSITION_RUN_UPPER_1; 308 } else { 309 ++heroPos; 310 } 311 ++distance; 312 313 digitalWrite(PIN_AUTOPLAY, terrainLower[HERO_HORIZONTAL_POSITION + 2] == SPRITE_TERRAIN_EMPTY ? HIGH : LOW); 314 } 315 delay(100); 316} 317 318// Thank you................................... 319
Downloadable files
schematic diagram
Its all detailed in the diagram
schematic diagram
Comments
Only logged in users can leave comments