Components and supplies
Tactile Switch, Top Actuated
Male/Female Jumper Wires
I2C 16x2 Arduino LCD Display Module
USB-A to B Cable
Arduino UNO
Jumper wires (generic)
Tools and machines
Breadboard, 170 Pin
Apps and platforms
Arduino IDE
Project description
Code
Code
csharp
The code
1#include <LiquidCrystal_I2C.h> 2#include <Wire.h> 3#define PIN_BUTTON 2 4#define PIN_AUTOPLAY 1 5#define SPRITE_RUN1 1 //Car sprite 6#define SPRITE_RUN2 2 7#define SPRITE_JUMP 3 8#define SPRITE_JUMP_UPPER '.' 9#define SPRITE_JUMP_LOWER 4 10#define SPRITE_TERRAIN_EMPTY ' ' 11#define SPRITE_TERRAIN_SOLID 5 12#define SPRITE_TERRAIN_SOLID_RIGHT 6 13#define SPRITE_TERRAIN_SOLID_LEFT 7 14#define CAR_HORIZONTAL_POSITION 1 // Horizontal position of CAR on screen 15#define TERRAIN_WIDTH 16 16#define TERRAIN_EMPTY 0 17#define TERRAIN_LOWER_BLOCK 1 18#define TERRAIN_UPPER_BLOCK 2 19#define CAR_POSITION_OFF 0 // CAR is invisible 20#define CAR_POSITION_RUN_LOWER_1 1 // CAR is running on lower row (pose 1) 21#define CAR_POSITION_RUN_LOWER_2 2 // (pose 2) 22#define CAR_POSITION_JUMP_1 3 // Starting a jump 23#define CAR_POSITION_JUMP_2 4 // Half-way up 24#define CAR_POSITION_JUMP_3 5 // Jump is on upper row 25#define CAR_POSITION_JUMP_4 6 // Jump is on upper row 26#define CAR_POSITION_JUMP_5 7 // Jump is on upper row 27#define CAR_POSITION_JUMP_6 8 // Jump is on upper row 28#define CAR_POSITION_JUMP_7 9 // Half-way down 29#define CAR_POSITION_JUMP_8 10 // About to land 30#define CAR_POSITION_RUN_UPPER_1 11 // CAR is running on upper row (pose 1) 31#define CAR_POSITION_RUN_UPPER_2 12 // (pose 2) 32LiquidCrystal_I2C lcd(0x27, 16, 2); 33static char terrainUpper[TERRAIN_WIDTH + 1]; 34static char terrainLower[TERRAIN_WIDTH + 1]; 35static bool buttonPushed = false; 36void initializeGraphics() { 37 static byte graphics[] = { 38 // Run position 1 39 0b00000, 40 0b00000, 41 0b00100, 42 0b11110, 43 0b11110, 44 0b11111, 45 0b01001, 46 0b00000, 47 // Run position 2 48 0b00000, 49 0b00000, 50 0b00100, 51 0b11110, 52 0b11110, 53 0b11111, 54 0b01001, 55 0b00000, 56 // Jump 57 0b00000, 58 0b00000, 59 0b00100, 60 0b11110, 61 0b11110, 62 0b11111, 63 0b01001, 64 0b00000, 65 // Jump lower 66 0b00000, 67 0b00000, 68 0b00100, 69 0b11110, 70 0b11110, 71 0b11111, 72 0b01001, 73 0b00000, 74 // Ground 75 0b01110, 76 0b11111, 77 0b10101, 78 0b11111, 79 0b10101, 80 0b11111, 81 0b10101, 82 0b11111, 83 // Ground right 84 0b01110, 85 0b11111, 86 0b10101, 87 0b11111, 88 0b10101, 89 0b11111, 90 0b10101, 91 0b11111, 92 // Ground left 93 0b01110, 94 0b11111, 95 0b10101, 96 0b11111, 97 0b10101, 98 0b11111, 99 0b10101, 100 0b11111, 101 }; 102 int i; 103 // Skip using character 0, this allows lcd.print() to be used to 104 // quickly draw multiple characters 105 for (i = 0; i < 7; ++i) { 106 lcd.createChar(i + 1, &graphics[i * 8]); 107 } 108 for (i = 0; i < TERRAIN_WIDTH; ++i) { 109 terrainUpper[i] = SPRITE_TERRAIN_EMPTY; 110 terrainLower[i] = SPRITE_TERRAIN_EMPTY; 111 } 112} 113 114// Slide the terrain to the left in half-character increments 115// 116void advanceTerrain(char* terrain, byte newTerrain) { 117 for (int i = 0; i < TERRAIN_WIDTH; ++i) { 118 char current = terrain[i]; 119 char next = (i == TERRAIN_WIDTH - 1) ? newTerrain : terrain[i + 1]; 120 switch (current) { 121 case SPRITE_TERRAIN_EMPTY: 122 terrain[i] = (next == SPRITE_TERRAIN_SOLID) ? SPRITE_TERRAIN_SOLID_RIGHT : SPRITE_TERRAIN_EMPTY; 123 break; 124 case SPRITE_TERRAIN_SOLID: 125 terrain[i] = (next == SPRITE_TERRAIN_EMPTY) ? SPRITE_TERRAIN_SOLID_LEFT : SPRITE_TERRAIN_SOLID; 126 break; 127 case SPRITE_TERRAIN_SOLID_RIGHT: 128 terrain[i] = SPRITE_TERRAIN_SOLID; 129 break; 130 case SPRITE_TERRAIN_SOLID_LEFT: 131 terrain[i] = SPRITE_TERRAIN_EMPTY; 132 break; 133 } 134 } 135} 136 137bool drawCAR(byte position, char* terrainUpper, char* terrainLower, unsigned int score) { 138 bool collide = false; 139 char upperSave = terrainUpper[CAR_HORIZONTAL_POSITION]; 140 char lowerSave = terrainLower[CAR_HORIZONTAL_POSITION]; 141 byte upper, lower; 142 switch (position) { 143 case CAR_POSITION_OFF: 144 upper = lower = SPRITE_TERRAIN_EMPTY; 145 break; 146 case CAR_POSITION_RUN_LOWER_1: 147 upper = SPRITE_TERRAIN_EMPTY; 148 lower = SPRITE_RUN1; 149 break; 150 case CAR_POSITION_RUN_LOWER_2: 151 upper = SPRITE_TERRAIN_EMPTY; 152 lower = SPRITE_RUN2; 153 break; 154 case CAR_POSITION_JUMP_1: 155 case CAR_POSITION_JUMP_8: 156 upper = SPRITE_TERRAIN_EMPTY; 157 lower = SPRITE_JUMP; 158 break; 159 case CAR_POSITION_JUMP_2: 160 case CAR_POSITION_JUMP_7: 161 upper = SPRITE_JUMP_UPPER; 162 lower = SPRITE_JUMP_LOWER; 163 break; 164 case CAR_POSITION_JUMP_3: 165 case CAR_POSITION_JUMP_4: 166 case CAR_POSITION_JUMP_5: 167 case CAR_POSITION_JUMP_6: 168 upper = SPRITE_JUMP; 169 lower = SPRITE_TERRAIN_EMPTY; 170 break; 171 case CAR_POSITION_RUN_UPPER_1: 172 upper = SPRITE_RUN1; 173 lower = SPRITE_TERRAIN_EMPTY; 174 break; 175 case CAR_POSITION_RUN_UPPER_2: 176 upper = SPRITE_RUN2; 177 lower = SPRITE_TERRAIN_EMPTY; 178 break; 179 } 180 if (upper != ' ') { 181 terrainUpper[CAR_HORIZONTAL_POSITION] = upper; 182 collide = (upperSave == SPRITE_TERRAIN_EMPTY) ? false : true; 183 } 184 if (lower != ' ') { 185 terrainLower[CAR_HORIZONTAL_POSITION] = lower; 186 collide |= (lowerSave == SPRITE_TERRAIN_EMPTY) ? false : true; 187 } 188 189 byte digits = (score > 9999) ? 5 : (score > 999) ? 4 : (score > 99) ? 3 : (score > 9) ? 2 : 1; 190 191 // Draw the scene 192 terrainUpper[TERRAIN_WIDTH] = '\\0'; 193 terrainLower[TERRAIN_WIDTH] = '\\0'; 194 char temp = terrainUpper[16 - digits]; 195 terrainUpper[16 - digits] = '\\0'; 196 lcd.setCursor(0, 0); 197 lcd.print(terrainUpper); 198 terrainUpper[16 - digits] = temp; 199 lcd.setCursor(0, 1); 200 lcd.print(terrainLower); 201 202 lcd.setCursor(16 - digits, 0); 203 lcd.print(score); 204 205 terrainUpper[CAR_HORIZONTAL_POSITION] = upperSave; 206 terrainLower[CAR_HORIZONTAL_POSITION] = lowerSave; 207 return collide; 208} 209 210// Handle the button push as an interrupt 211void buttonPush() { 212 buttonPushed = true; 213} 214 215void setup() { 216 pinMode(PIN_BUTTON, INPUT); 217 digitalWrite(PIN_BUTTON, HIGH); 218 pinMode(PIN_AUTOPLAY, OUTPUT); 219 digitalWrite(PIN_AUTOPLAY, HIGH); 220 221 // Digital pin 2 maps to interrupt 0 222 attachInterrupt(0/*PIN_BUTTON*/, buttonPush, FALLING); 223 224 initializeGraphics(); 225 226 lcd.init(); 227 lcd.backlight(); 228} 229 230void loop() { 231 static byte CARPos = CAR_POSITION_RUN_LOWER_1; 232 static byte newTerrainType = TERRAIN_EMPTY; 233 static byte newTerrainDuration = 1; 234 static bool playing = false; 235 static bool blink = false; 236 static unsigned int distance = 0; 237 238 if (!playing) { 239 drawCAR((blink) ? CAR_POSITION_OFF : CARPos, terrainUpper, terrainLower, distance >> 3); 240 if (blink) { 241 lcd.setCursor(0, 0); 242 lcd.print("Press Start"); 243 } 244 delay(100); 245 blink = !blink; 246 if (buttonPushed) { 247 initializeGraphics(); 248 CARPos = CAR_POSITION_RUN_LOWER_1; 249 playing = true; 250 buttonPushed = false; 251 distance = 0; 252 } 253 return; 254 } 255 256 // Shift the terrain to the left 257 advanceTerrain(terrainLower, newTerrainType == TERRAIN_LOWER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY); 258 advanceTerrain(terrainUpper, newTerrainType == TERRAIN_UPPER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY); 259 260 // Make new terrain to enter on the right 261 if (--newTerrainDuration == 0) { 262 if (newTerrainType == TERRAIN_EMPTY) { 263 newTerrainType = (random(3) == 0) ? TERRAIN_UPPER_BLOCK : TERRAIN_LOWER_BLOCK; 264 newTerrainDuration = 10 + random(6); 265 } else { 266 newTerrainType = TERRAIN_EMPTY; 267 newTerrainDuration = 10 + random(6); 268 } 269 } 270 271 if (buttonPushed) { 272 if (CARPos <= CAR_POSITION_RUN_LOWER_2) CARPos = CAR_POSITION_JUMP_1; 273 buttonPushed = false; 274 } 275 276 if (drawCAR(CARPos, terrainUpper, terrainLower, distance >> 3)) { 277 playing = false; // The CAR collided with something. Too bad. 278 for (int i = 0; i <= 2; i++) { 279 } 280 } else { 281 if (CARPos == CAR_POSITION_RUN_LOWER_2 || CARPos == CAR_POSITION_JUMP_8) { 282 CARPos = CAR_POSITION_RUN_LOWER_1; 283 } else if ((CARPos >= CAR_POSITION_JUMP_3 && CARPos <= CAR_POSITION_JUMP_5) && terrainLower[CAR_HORIZONTAL_POSITION] != SPRITE_TERRAIN_EMPTY) { 284 CARPos = CAR_POSITION_RUN_UPPER_1; 285 } else if (CARPos >= CAR_POSITION_RUN_UPPER_1 && terrainLower[CAR_HORIZONTAL_POSITION] == SPRITE_TERRAIN_EMPTY) { 286 CARPos = CAR_POSITION_JUMP_5; 287 } else if (CARPos == CAR_POSITION_RUN_UPPER_2) { 288 CARPos = CAR_POSITION_RUN_UPPER_1; 289 } else { 290 ++CARPos; 291 } 292 ++distance; 293 294 digitalWrite(PIN_AUTOPLAY, terrainLower[CAR_HORIZONTAL_POSITION + 2] == SPRITE_TERRAIN_EMPTY ? HIGH : LOW); 295 } 296}
Code
csharp
The code
1#include <LiquidCrystal_I2C.h> 2#include <Wire.h> 3#define PIN_BUTTON 2 4#define PIN_AUTOPLAY 1 5#define SPRITE_RUN1 1 //Car sprite 6#define SPRITE_RUN2 2 7#define SPRITE_JUMP 3 8#define SPRITE_JUMP_UPPER '.' 9#define SPRITE_JUMP_LOWER 4 10#define SPRITE_TERRAIN_EMPTY ' ' 11#define SPRITE_TERRAIN_SOLID 5 12#define SPRITE_TERRAIN_SOLID_RIGHT 6 13#define SPRITE_TERRAIN_SOLID_LEFT 7 14#define CAR_HORIZONTAL_POSITION 1 // Horizontal position of CAR on screen 15#define TERRAIN_WIDTH 16 16#define TERRAIN_EMPTY 0 17#define TERRAIN_LOWER_BLOCK 1 18#define TERRAIN_UPPER_BLOCK 2 19#define CAR_POSITION_OFF 0 // CAR is invisible 20#define CAR_POSITION_RUN_LOWER_1 1 // CAR is running on lower row (pose 1) 21#define CAR_POSITION_RUN_LOWER_2 2 // (pose 2) 22#define CAR_POSITION_JUMP_1 3 // Starting a jump 23#define CAR_POSITION_JUMP_2 4 // Half-way up 24#define CAR_POSITION_JUMP_3 5 // Jump is on upper row 25#define CAR_POSITION_JUMP_4 6 // Jump is on upper row 26#define CAR_POSITION_JUMP_5 7 // Jump is on upper row 27#define CAR_POSITION_JUMP_6 8 // Jump is on upper row 28#define CAR_POSITION_JUMP_7 9 // Half-way down 29#define CAR_POSITION_JUMP_8 10 // About to land 30#define CAR_POSITION_RUN_UPPER_1 11 // CAR is running on upper row (pose 1) 31#define CAR_POSITION_RUN_UPPER_2 12 // (pose 2) 32LiquidCrystal_I2C lcd(0x27, 16, 2); 33static char terrainUpper[TERRAIN_WIDTH + 1]; 34static char terrainLower[TERRAIN_WIDTH + 1]; 35static bool buttonPushed = false; 36void initializeGraphics() { 37 static byte graphics[] = { 38 // Run position 1 39 0b00000, 40 0b00000, 41 0b00100, 42 0b11110, 43 0b11110, 44 0b11111, 45 0b01001, 46 0b00000, 47 // Run position 2 48 0b00000, 49 0b00000, 50 0b00100, 51 0b11110, 52 0b11110, 53 0b11111, 54 0b01001, 55 0b00000, 56 // Jump 57 0b00000, 58 0b00000, 59 0b00100, 60 0b11110, 61 0b11110, 62 0b11111, 63 0b01001, 64 0b00000, 65 // Jump lower 66 0b00000, 67 0b00000, 68 0b00100, 69 0b11110, 70 0b11110, 71 0b11111, 72 0b01001, 73 0b00000, 74 // Ground 75 0b01110, 76 0b11111, 77 0b10101, 78 0b11111, 79 0b10101, 80 0b11111, 81 0b10101, 82 0b11111, 83 // Ground right 84 0b01110, 85 0b11111, 86 0b10101, 87 0b11111, 88 0b10101, 89 0b11111, 90 0b10101, 91 0b11111, 92 // Ground left 93 0b01110, 94 0b11111, 95 0b10101, 96 0b11111, 97 0b10101, 98 0b11111, 99 0b10101, 100 0b11111, 101 }; 102 int i; 103 // Skip using character 0, this allows lcd.print() to be used to 104 // quickly draw multiple characters 105 for (i = 0; i < 7; ++i) { 106 lcd.createChar(i + 1, &graphics[i * 8]); 107 } 108 for (i = 0; i < TERRAIN_WIDTH; ++i) { 109 terrainUpper[i] = SPRITE_TERRAIN_EMPTY; 110 terrainLower[i] = SPRITE_TERRAIN_EMPTY; 111 } 112} 113 114// Slide the terrain to the left in half-character increments 115// 116void advanceTerrain(char* terrain, byte newTerrain) { 117 for (int i = 0; i < TERRAIN_WIDTH; ++i) { 118 char current = terrain[i]; 119 char next = (i == TERRAIN_WIDTH - 1) ? newTerrain : terrain[i + 1]; 120 switch (current) { 121 case SPRITE_TERRAIN_EMPTY: 122 terrain[i] = (next == SPRITE_TERRAIN_SOLID) ? SPRITE_TERRAIN_SOLID_RIGHT : SPRITE_TERRAIN_EMPTY; 123 break; 124 case SPRITE_TERRAIN_SOLID: 125 terrain[i] = (next == SPRITE_TERRAIN_EMPTY) ? SPRITE_TERRAIN_SOLID_LEFT : SPRITE_TERRAIN_SOLID; 126 break; 127 case SPRITE_TERRAIN_SOLID_RIGHT: 128 terrain[i] = SPRITE_TERRAIN_SOLID; 129 break; 130 case SPRITE_TERRAIN_SOLID_LEFT: 131 terrain[i] = SPRITE_TERRAIN_EMPTY; 132 break; 133 } 134 } 135} 136 137bool drawCAR(byte position, char* terrainUpper, char* terrainLower, unsigned int score) { 138 bool collide = false; 139 char upperSave = terrainUpper[CAR_HORIZONTAL_POSITION]; 140 char lowerSave = terrainLower[CAR_HORIZONTAL_POSITION]; 141 byte upper, lower; 142 switch (position) { 143 case CAR_POSITION_OFF: 144 upper = lower = SPRITE_TERRAIN_EMPTY; 145 break; 146 case CAR_POSITION_RUN_LOWER_1: 147 upper = SPRITE_TERRAIN_EMPTY; 148 lower = SPRITE_RUN1; 149 break; 150 case CAR_POSITION_RUN_LOWER_2: 151 upper = SPRITE_TERRAIN_EMPTY; 152 lower = SPRITE_RUN2; 153 break; 154 case CAR_POSITION_JUMP_1: 155 case CAR_POSITION_JUMP_8: 156 upper = SPRITE_TERRAIN_EMPTY; 157 lower = SPRITE_JUMP; 158 break; 159 case CAR_POSITION_JUMP_2: 160 case CAR_POSITION_JUMP_7: 161 upper = SPRITE_JUMP_UPPER; 162 lower = SPRITE_JUMP_LOWER; 163 break; 164 case CAR_POSITION_JUMP_3: 165 case CAR_POSITION_JUMP_4: 166 case CAR_POSITION_JUMP_5: 167 case CAR_POSITION_JUMP_6: 168 upper = SPRITE_JUMP; 169 lower = SPRITE_TERRAIN_EMPTY; 170 break; 171 case CAR_POSITION_RUN_UPPER_1: 172 upper = SPRITE_RUN1; 173 lower = SPRITE_TERRAIN_EMPTY; 174 break; 175 case CAR_POSITION_RUN_UPPER_2: 176 upper = SPRITE_RUN2; 177 lower = SPRITE_TERRAIN_EMPTY; 178 break; 179 } 180 if (upper != ' ') { 181 terrainUpper[CAR_HORIZONTAL_POSITION] = upper; 182 collide = (upperSave == SPRITE_TERRAIN_EMPTY) ? false : true; 183 } 184 if (lower != ' ') { 185 terrainLower[CAR_HORIZONTAL_POSITION] = lower; 186 collide |= (lowerSave == SPRITE_TERRAIN_EMPTY) ? false : true; 187 } 188 189 byte digits = (score > 9999) ? 5 : (score > 999) ? 4 : (score > 99) ? 3 : (score > 9) ? 2 : 1; 190 191 // Draw the scene 192 terrainUpper[TERRAIN_WIDTH] = '\\0'; 193 terrainLower[TERRAIN_WIDTH] = '\\0'; 194 char temp = terrainUpper[16 - digits]; 195 terrainUpper[16 - digits] = '\\0'; 196 lcd.setCursor(0, 0); 197 lcd.print(terrainUpper); 198 terrainUpper[16 - digits] = temp; 199 lcd.setCursor(0, 1); 200 lcd.print(terrainLower); 201 202 lcd.setCursor(16 - digits, 0); 203 lcd.print(score); 204 205 terrainUpper[CAR_HORIZONTAL_POSITION] = upperSave; 206 terrainLower[CAR_HORIZONTAL_POSITION] = lowerSave; 207 return collide; 208} 209 210// Handle the button push as an interrupt 211void buttonPush() { 212 buttonPushed = true; 213} 214 215void setup() { 216 pinMode(PIN_BUTTON, INPUT); 217 digitalWrite(PIN_BUTTON, HIGH); 218 pinMode(PIN_AUTOPLAY, OUTPUT); 219 digitalWrite(PIN_AUTOPLAY, HIGH); 220 221 // Digital pin 2 maps to interrupt 0 222 attachInterrupt(0/*PIN_BUTTON*/, buttonPush, FALLING); 223 224 initializeGraphics(); 225 226 lcd.init(); 227 lcd.backlight(); 228} 229 230void loop() { 231 static byte CARPos = CAR_POSITION_RUN_LOWER_1; 232 static byte newTerrainType = TERRAIN_EMPTY; 233 static byte newTerrainDuration = 1; 234 static bool playing = false; 235 static bool blink = false; 236 static unsigned int distance = 0; 237 238 if (!playing) { 239 drawCAR((blink) ? CAR_POSITION_OFF : CARPos, terrainUpper, terrainLower, distance >> 3); 240 if (blink) { 241 lcd.setCursor(0, 0); 242 lcd.print("Press Start"); 243 } 244 delay(100); 245 blink = !blink; 246 if (buttonPushed) { 247 initializeGraphics(); 248 CARPos = CAR_POSITION_RUN_LOWER_1; 249 playing = true; 250 buttonPushed = false; 251 distance = 0; 252 } 253 return; 254 } 255 256 // Shift the terrain to the left 257 advanceTerrain(terrainLower, newTerrainType == TERRAIN_LOWER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY); 258 advanceTerrain(terrainUpper, newTerrainType == TERRAIN_UPPER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY); 259 260 // Make new terrain to enter on the right 261 if (--newTerrainDuration == 0) { 262 if (newTerrainType == TERRAIN_EMPTY) { 263 newTerrainType = (random(3) == 0) ? TERRAIN_UPPER_BLOCK : TERRAIN_LOWER_BLOCK; 264 newTerrainDuration = 10 + random(6); 265 } else { 266 newTerrainType = TERRAIN_EMPTY; 267 newTerrainDuration = 10 + random(6); 268 } 269 } 270 271 if (buttonPushed) { 272 if (CARPos <= CAR_POSITION_RUN_LOWER_2) CARPos = CAR_POSITION_JUMP_1; 273 buttonPushed = false; 274 } 275 276 if (drawCAR(CARPos, terrainUpper, terrainLower, distance >> 3)) { 277 playing = false; // The CAR collided with something. Too bad. 278 for (int i = 0; i <= 2; i++) { 279 } 280 } else { 281 if (CARPos == CAR_POSITION_RUN_LOWER_2 || CARPos == CAR_POSITION_JUMP_8) { 282 CARPos = CAR_POSITION_RUN_LOWER_1; 283 } else if ((CARPos >= CAR_POSITION_JUMP_3 && CARPos <= CAR_POSITION_JUMP_5) && terrainLower[CAR_HORIZONTAL_POSITION] != SPRITE_TERRAIN_EMPTY) { 284 CARPos = CAR_POSITION_RUN_UPPER_1; 285 } else if (CARPos >= CAR_POSITION_RUN_UPPER_1 && terrainLower[CAR_HORIZONTAL_POSITION] == SPRITE_TERRAIN_EMPTY) { 286 CARPos = CAR_POSITION_JUMP_5; 287 } else if (CARPos == CAR_POSITION_RUN_UPPER_2) { 288 CARPos = CAR_POSITION_RUN_UPPER_1; 289 } else { 290 ++CARPos; 291 } 292 ++distance; 293 294 digitalWrite(PIN_AUTOPLAY, terrainLower[CAR_HORIZONTAL_POSITION + 2] == SPRITE_TERRAIN_EMPTY ? HIGH : LOW); 295 } 296}
Downloadable files
Schematics
Schematics (Edited)
Schematics
Comments
Only logged in users can leave comments