NIKOLA ANICIC 2019 0099 Arcade game: Rendezvous SW1: Clockwise rotation SW2: Counter-clockwise rotation POT1: Main engine POT2: Retrorockets
Dependencies: mbed Adafruit_GFX
NIKOLA ANICIC 2019 0099
Arcade game: Rendezvous Controls: - SW1 (PC_9) : Clockwise rotation - SW2 (PC_8) : Counter-clockwise rotation - POT1 (PA_0) : Main engine - POT2 (PA_1) : Retrorockets
main.cpp
- Committer:
- an_thanik
- Date:
- 2021-11-21
- Revision:
- 2:d015424ca47e
- Parent:
- 0:63446647fa0e
File content as of revision 2:d015424ca47e:
/* * Nikola Aničić 2019/0099 * ETF Beograd * napisano 20/11/2021 * * Zadatak 3.4 - Napraviti arkadnu igricu po zelji * Kao komande je moguće koristiti POT1, POT2, SW1, SW2. * Pozavršetku zadatka isti objaviti (Publish) i obavestiti predmetnog nastavnika. * * - Ideja: Brod i letelica za sletanje treba da poklope vektore brzine * i prispoje se. Letelica za sletanje na raspolaganju ima: * -> kontrolu usmerenja (Rotacija: POT1 ccw, POT2 clockwise) * -> kontrolu pogona (Brzina X, Y: SW1 retrograde, SW2 prograde) */ #include "mbed.h" // Potrebne biblioteke za Adafruit #include "Adafruit_GFX.h" #include "Adafruit_GFX_Config.h" /* Sve potrebne funkcionalnosti su otkomentarisane */ #include "Adafruit_SSD1306.h" // Custom headers za bitmape #include "bitmaps.h" // Program options - uncomment to enable #define SHOW_SPLASH // #define DEBUG_SPRITES // I2C bus #define SCL PB_13 #define SDA PB_14 // I2C address #define I2C_ADDRESS 0x3C #define I2C_ADDRESS_MBED I2C_ADDRESS << 1 // I2C frequency #define FREQ 400000 // 400 kHz // OLED dimensions #define OLED_HEIGHT 64 #define OLED_WIDTH 128 // Time Intervals /* milliseconds */ #define RFR_MS 5 #define SHR_MS 2 /* seconds */ #define SPLASH_TIME 2 // Scalers #define WIDTH_SCALE 128 #define HEIGHT_SCALE 64 #define SENSITIVITY 0.35f #define MAXVEL 10 #define RESET_PIN PB_5 /* Neaktivno, ali potrebno za inicijalizaciju displeja */ I2C i2c_obj(SDA, SCL); Adafruit_SSD1306_I2c myOLED( /* Pogledaj Adafruit_SSD1306.cpp dokumentaciju */ i2c_obj, RESET_PIN, I2C_ADDRESS_MBED, OLED_HEIGHT, OLED_WIDTH ); // Kontrolni signali DigitalIn SW1(PC_9); DigitalIn SW2(PC_8); AnalogIn POT1(PA_0); AnalogIn POT2(PA_1); Ticker tick; Timer timeScore; static int x0, y0 = 0; static int x, y, vx, vy = 0; static int orientation = 0; static bool SW1_released = 0; static bool SW2_released = 0; static int victory = 0; static int y_target = 30, x_target = 30, vx_target = 6, vy_target = 2; // General functions void displayImage(int, int, const unsigned char *, int, int); void checkBounds(void); void updatePos(void); void updateState(void); /* Originalno je trebalo da bude jedna genericna funkcija, */ // RIP onPress(DigitalIn) /* ali potreba da se razlikuje ponasanje to cini teskim */ void victoryCheck(void); void mainBurn(void); void retroBurn(void); // Ticker functions void ping (void); void debugSprites(void); int main () { // Inicijalizacija programa -- Uspostavljanje komunikacije sa displejom, clear ekrana, i postavljanje brzine komunikacije. i2c_obj.frequency(FREQ); myOLED.begin(); #ifdef SHOW_SPLASH displayImage(0, 0, spriteSheet[20], OLED_WIDTH, OLED_HEIGHT); wait(SPLASH_TIME); // Total: 2.005 sec myOLED.clearDisplay(); #endif #ifdef DEBUG_SPRITES printf("Debugging!\n\r"); while(1) { tick.attach(&debugSprites, 3.0); for(int i = 0; i < 24; i++) { displayImage(64, 32, spriteSheet[i], OLED_WIDTH, OLED_HEIGHT); wait(SPLASH_TIME); } } #endif displayImage(0, 0, spriteSheet[21], OLED_WIDTH, OLED_HEIGHT); printf("\rMaking final approach... \n\r"); timeScore.start(); while(1) { victoryCheck(); if(!victory) { updateState(); checkBounds(); updatePos(); printf("\rAscent Module (%3d, %3d) | Distance (%3d, %3d)", vx, vy, x - x0, y - y0); myOLED.clearDisplay(); } else { x = 61; y = 31; x_target = 70; y_target = 32; vx = 0; vy = 0; vx_target = 0; vy_target = 0; wait(SPLASH_TIME); for (int i = 0; i < 4; i++) { wait(1); myOLED.clearDisplay(); x++; displayImage(x_target, y_target, spriteSheet[22], SHIP_OFF_WIDTH, SHIP_HEIGHT); displayImage(x, y, spriteSheet[18], SHIP_OFF_WIDTH, SHIP_HEIGHT); } while(x_target > -150) { myOLED.clearDisplay(); vx_target -= 1; vx -= 1; x += vx; x_target += vx_target; displayImage(x_target, y_target, spriteSheet[23], SHIP_ON_WIDTH, SHIP_HEIGHT); displayImage(x, y, spriteSheet[18], SHIP_OFF_WIDTH, SHIP_HEIGHT); wait_ms(RFR_MS); } } } } void displayImage(int x, int y, const unsigned char *bmp, int w, int h) { myOLED.drawBitmap(x, y, bmp, w, h, WHITE); /* To sto je bitmap staticka mi nista ne daje osim komplikacija. */ myOLED.display(); } void ping () { printf("\r\nPing!\r\n"); wait_ms(RFR_MS); } void debugSprites() { printf("\n\rDebug ping!\r\n"); } void updateState () { if (!SW1) // Stanje: Dugme je pritisnuto { SW1_released = 1; displayImage(x_target, y_target, spriteSheet[22], SHIP_OFF_WIDTH, SHIP_HEIGHT); /* Action while button pressed */ switch(orientation) { case 0: displayImage(x, y, spriteSheet[16], OLED_WIDTH, OLED_HEIGHT); break; case 1: displayImage(x, y, spriteSheet[17], OLED_WIDTH, OLED_HEIGHT); break; case 2: displayImage(x, y, spriteSheet[18], OLED_WIDTH, OLED_HEIGHT); break; case 3: displayImage(x, y, spriteSheet[19], OLED_WIDTH, OLED_HEIGHT); break; default: printf("\r\n Error: Invalid orientation.\r\n"); } } else // Stanje: Dugme nije pritisnuto { if(SW1_released) // Stanje: Dugme je neposredno bilo pritisnuto -> Aktiviraj { SW1_released = 0; myOLED.clearDisplay(); displayImage(x_target, y_target, spriteSheet[22], SHIP_OFF_WIDTH, SHIP_HEIGHT); /* Action */ switch(orientation) { case 0: displayImage(x, y, spriteSheet[12], OLED_WIDTH, OLED_HEIGHT); orientation = 1; break; case 1: displayImage(x, y, spriteSheet[13], OLED_WIDTH, OLED_HEIGHT); orientation = 2; break; case 2: displayImage(x, y, spriteSheet[14], OLED_WIDTH, OLED_HEIGHT); orientation = 3; break; case 3: displayImage(x, y, spriteSheet[15], OLED_WIDTH, OLED_HEIGHT); orientation = 0; break; default: printf("\r\n Error: Invalid orientation.\r\n"); } } else // Stanje: Nista se nije promenilo { SW1_released = 0; displayImage(x_target, y_target, spriteSheet[22], SHIP_OFF_WIDTH, SHIP_HEIGHT); /* Action */ switch(orientation) { case 0: displayImage(x, y, spriteSheet[16], OLED_WIDTH, OLED_HEIGHT); break; case 1: displayImage(x, y, spriteSheet[17], OLED_WIDTH, OLED_HEIGHT); break; case 2: displayImage(x, y, spriteSheet[18], OLED_WIDTH, OLED_HEIGHT); break; case 3: displayImage(x, y, spriteSheet[19], OLED_WIDTH, OLED_HEIGHT); break; default: printf("\r\n Error: Invalid orientation.\r\n"); } } } if (!SW2) // Stanje: Dugme je pritisnuto { SW2_released = 1; displayImage(x_target, y_target, spriteSheet[22], SHIP_OFF_WIDTH, SHIP_HEIGHT); /* Action while button pressed */ switch(orientation) { case 0: displayImage(x, y, spriteSheet[16], OLED_WIDTH, OLED_HEIGHT); break; case 1: displayImage(x, y, spriteSheet[17], OLED_WIDTH, OLED_HEIGHT); break; case 2: displayImage(x, y, spriteSheet[18], OLED_WIDTH, OLED_HEIGHT); break; case 3: displayImage(x, y, spriteSheet[19], OLED_WIDTH, OLED_HEIGHT); break; default: printf("\r\n Error: Invalid orientation.\r\n"); } } else // Stanje: Dugme nije pritisnuto { if(SW2_released) // Stanje: Dugme je neposredno bilo pritisnuto -> Aktiviraj { SW2_released = 0; myOLED.clearDisplay(); displayImage(x_target, y_target, spriteSheet[22], SHIP_OFF_WIDTH, SHIP_HEIGHT); /* Action */ switch(orientation) { case 0: displayImage(x, y, spriteSheet[8], OLED_WIDTH, OLED_HEIGHT); orientation = 3; break; case 1: displayImage(x, y, spriteSheet[9], OLED_WIDTH, OLED_HEIGHT); orientation = 0; break; case 2: displayImage(x, y, spriteSheet[10], OLED_WIDTH, OLED_HEIGHT); orientation = 1; break; case 3: displayImage(x, y, spriteSheet[11], OLED_WIDTH, OLED_HEIGHT); orientation = 2; break; default: printf("\r\n Error: Invalid orientation.\r\n"); } } else // Stanje: Nista se nije promenilo { SW2_released = 0; displayImage(x_target, y_target, spriteSheet[22], SHIP_OFF_WIDTH, SHIP_HEIGHT); /* Action */ switch(orientation) { case 0: displayImage(x, y, spriteSheet[16], OLED_WIDTH, OLED_HEIGHT); break; case 1: displayImage(x, y, spriteSheet[17], OLED_WIDTH, OLED_HEIGHT); break; case 2: displayImage(x, y, spriteSheet[18], OLED_WIDTH, OLED_HEIGHT); break; case 3: displayImage(x, y, spriteSheet[19], OLED_WIDTH, OLED_HEIGHT); break; default: printf("\r\n Error: Invalid orientation.\r\n"); } } } if(POT1 > 0.9f) { mainBurn(); } if(POT2 > 0.9f) { retroBurn(); } } void retroBurn() { myOLED.clearDisplay(); displayImage(x_target, y_target, spriteSheet[22], SHIP_OFF_WIDTH, SHIP_HEIGHT); switch(orientation) { case 0: displayImage(x, y, spriteSheet[6], OLED_WIDTH, OLED_HEIGHT); vx > -MAXVEL ? vx-- : vx; break; case 1: displayImage(x, y, spriteSheet[5], OLED_WIDTH, OLED_HEIGHT); vy > -MAXVEL ? vy-- : vy; break; case 2: displayImage(x, y, spriteSheet[4], OLED_WIDTH, OLED_HEIGHT); vx < MAXVEL ? vx++ : vx; break; case 3: displayImage(x, y, spriteSheet[7], OLED_WIDTH, OLED_HEIGHT); vy < MAXVEL ? vy++ : vy; break; } } void mainBurn() { myOLED.clearDisplay(); displayImage(x_target, y_target, spriteSheet[22], SHIP_OFF_WIDTH, SHIP_HEIGHT); switch(orientation) { case 0: displayImage(x, y, spriteSheet[0], OLED_WIDTH, OLED_HEIGHT); vx < MAXVEL ? vx++ : vx; break; case 1: displayImage(x, y, spriteSheet[1], OLED_WIDTH, OLED_HEIGHT); vy < MAXVEL ? vy++ : vy; break; case 2: displayImage(x, y, spriteSheet[2], OLED_WIDTH, OLED_HEIGHT); vx > -MAXVEL ? vx-- : vx; break; case 3: displayImage(x, y, spriteSheet[3], OLED_WIDTH, OLED_HEIGHT); vy > -MAXVEL ? vy-- : vy; break; } } void checkBounds() { // Osigurava da je x within bounds if (x + vx > OLED_WIDTH) { x -= OLED_WIDTH; } if (x + vx < 0) { x += OLED_WIDTH; } // Osigurava da je y within bounds if ( y + vy > OLED_HEIGHT) { y -= OLED_HEIGHT; } if (y + vy < 0) { y += OLED_HEIGHT; } // Isto i za metu if (x_target + vx_target > OLED_WIDTH) { x_target -= OLED_WIDTH; } if (x_target + vx_target < 0) { x_target += OLED_WIDTH; } // Osigurava da je y within bounds if (y_target + vy_target > OLED_HEIGHT) { y_target -= OLED_HEIGHT; } if (y_target + vy_target < 0) { y_target += OLED_HEIGHT; } } void updatePos() { x += vx; y += vy; x_target += vx_target; y_target += vy_target; x0 = x_target - 13; y0 = y_target; myOLED.drawCircle(x0, y0, 4, WHITE); myOLED.display(); } void victoryCheck() { if ((x - x0) <= 2 && (y - y0) <= 2 && (vx - vx_target) == 0 && (vy - vy_target) == 0 && orientation == 0 && !victory) { victory = 1; timeScore.stop(); printf("\n\rWith their vessels docked, the crew get ready for the journey home. The time taken was %f.2 seconds!\n", timeScore.read()); } } /* Dodatni resursi */ // https://javl.github.io/image2cpp/ --> za bitmape // Code Output Format -> Adafruit GFX Bitmap format; Vertical 1-bit-per-pixel // Ovaj konvertor je veoma los; // -- Ako ima vise slika razlicitih dimenzija, koristi prvu na koju naidje i ostale konvertuje kao da su iste bez obzira na prave dimenzije. // -- Kada brises, brisi od prve inace ubaguje. // -- Ne moze da pravi slike koje su manje siroke nego sto su dugacke, sto je autisticno. // https://ezgif.com/ --> za dekonstrukciju gif-a // https://www.mischianti.org/images-to-byte-array-online-converter-cpp-arduino/#Byte_array_generator --> Za properly separated 2D byte array