4180 Final Project
Dependencies: mbed uLCD_4D_Picaso mbed-rtos PinDetect
Diff: main.cpp
- Revision:
- 0:a45fd26bd87b
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Thu Dec 05 17:27:10 2019 +0000 @@ -0,0 +1,520 @@ +#include "mbed.h" +#include "rtos.h" +#include "SongPlayer.h" +#include "Picaso_enums.h" +#include "uLCD_4D_Picaso.h" + + + +//////////////////////////////////////////////////////////////////////////////// +// Variables +//////////////////////////////////////////////////////////////////////////////// + +// Hardware variables +//---------------------------------------------------------------------- +uLCD_4D_Picaso uLCD(p9, p10, p7); // serial tx, serial rx, reset pin; +Mutex lcdMtx; // Mutex for the uLCD +SongPlayer mySpeaker(p21); +DigitalOut led(LED4); +Ticker motion, scoreTicker; + +// Screen and map dimensions +//---------------------------------------------------------------------- +// Dimensions +const int sHeight = 240; +const int sWidth = 320; +const int marginH = 40; +const int marginW = 40; +const float mapHeight = 5.0; +const float mapWidth = mapHeight*float(sWidth-marginW)/float(sHeight-2*marginH); +// Sand +volatile unsigned int sandY[80]= {203,205,205,203,205,203,205,203,203,205,203,205 + ,205,203,205,203,205,203,203,205,203,205,205,203, + 205,203,205,203,203,205,203,205,205,203,205,203, + 205,203,203,205,203,205,205,203,205,203,205,203, + 203,205,203,205,205,203,205,203,205,203,203,205, + 203,205,205,203,205,203,205,203,203,205,203,205, + 205,203,205,203,203,205,203,205}; +volatile unsigned int sandX[80]= {1,2,3,4,5,16,17,18,19,20,46,47,48,49,50,61,62, + 63,64,65,76,92,93,94,95,106,107,108,109,110,111, + 127,128,129,130,141,142,143,144,145,156,173,174, + 175,186,187,188,189,190,201,202,218,219,220,231, + 232,233,234,235,246,247,263,264,265,276,277,278, + 279,280,291,292,293,308,309,310,311,312,313,314,315}; + + +// Game variables +//---------------------------------------------------------------------- +// Trex +volatile float zTrex; +volatile float old_zTrex; +float vzTrex; +float massTrex = 1.5; +float rTrex = 0.5; +float maxHeightTrex = mapHeight-2.5*rTrex; +// Obstacle +volatile float xObstacle; +volatile float old_xObstacle; +float vxObstacle; +// Motion +float gravity = 20.0; +float dt_move = 0.01; // in seconds +float dt_disp = 0.1; // in seconds +volatile bool collision; +// Score +volatile int score; +// Sounds +float trexJump[2] = {515.0, 0.0}; +float trexJump_duration[2] = {0.07, 0.0}; +float trexLvlPass[3] = {515.0, 780, 0.0}; +float trexLvlPass_duration[3] = {0.075, 0.175, 0.0}; +float trexGameOver[4] = {63.0, 0.0, 63.0, 0.0}; +float trexGameOver_duration[4] = {0.07, 0.01, 0.07, 0.0}; +// Cloud +volatile float xCloud; +volatile float old_xCloud; +volatile float zCloud = 4.0; +// Restart +volatile bool restart = true; + + +//////////////////////////////////////////////////////////////////////////////// +// GRAPHICS FOR THE T-REX AND THE CACTUS +//////////////////////////////////////////////////////////////////////////////// + +//--------------------------------------------------------------------------------------------------------------- +enum Status { COLLISION = 0, JUMP = 1, RUNNING_FRONT = 2, RUNNING_BACK = 3 }; + +// T-rex body offsets +uint16_t dino_x_base_offset[28] = {0, 0, 10, 22, 22, 26, 28, 28, 30, + 30, 32, 32, 28, 28, 36, 36, 30, 30, 40, 40, 38, 38, 22, 20, 20, 10, 6, 2}; +uint16_t dino_y_base_offset[28] = {14, 24, 32, 32, 30, 26, 26, 20, + 20, 22, 22, 18, 18, 14, 14, 12, 12, 10, 10, 2, 2, 0, 0, 2, 14, 22, 22, 14}; +// T-rex eyes offsets +uint16_t dino_x_eye_1_offset[4] = {24, 24, 26, 26}; +uint16_t dino_y_eye_1_offset[4] = {4, 6, 6, 4}; +uint16_t dino_x_eye_2_offset[4] = {23, 23, 27, 27}; +uint16_t dino_y_eye_2_offset[4] = {3, 7, 7, 3}; + +// Running animation coordinates offset +uint16_t dino_x_leg_offset[15] = {10, 10, 14, 14, 12, 12, 16, 18, 20, 20, 24, 24, 22, 22, 10}; +uint16_t dino_y_leg_offset[15] = {32, 40, 40, 38, 38, 36, 32, 32, 34, 40, 40, 38, 38, 30, 32}; + +uint16_t dino_x_leg_front_offset[14] = {10, 10, 14, 14, 12, 12, 16, 18, 20, 20, 26, 26, 22, 22}; +uint16_t dino_y_leg_front_offset[14] = {32, 40, 40, 38, 38, 36, 32, 32, 34, 36, 36, 34, 34, 32}; + +uint16_t dino_x_leg_back_offset[15] = {10, 10, 12, 12, 16, 16, 14, 14, 18, 20, 20, 24, 24, 22, 22}; +uint16_t dino_y_leg_back_offset[15] = {32, 34, 34, 36, 36, 34, 34, 32, 32, 34, 40, 40, 38, 38, 30}; + +// Cactus offsets +uint16_t cactus_x_offset[28] = {0, 0, 10, 10, 18, 18, 28, 28, 26, 26, 24, 24, 22, 22, + 18, 18, 16, 16, 12, 12, 10, 10, 6, 6, 4, 4, 2, 2}; +uint16_t cactus_y_offset[28] = {10, 30, 30 ,40, 40, 30, 30, 10, 10, 8, 8, 10, 10, 24, + 24, 2, 2, 0, 0, 2, 2, 24, 24, 10, 10, 8, 8, 10}; +//--------------------------------------------------------------------------------------------------------------- + +// status can be 0 (jumps or collisions), 1 (running animation 1), or 2 (running animation 2) +void drawDino(uint16_t x, uint16_t y, Picaso::Color Color, Status status) +{ + uint16_t dino_x[28]; + uint16_t dino_y[28]; + uint16_t eye_1_x[4]; + uint16_t eye_1_y[4]; + uint16_t eye_2_x[4]; + uint16_t eye_2_y[4]; + + // draw the base t-rex sprite + for (int i = 0; i < 28; i++) + { + dino_x[i] = x + dino_x_base_offset[i]; + dino_y[i] = y + dino_y_base_offset[i]; + if (i < 4) + { + eye_1_x[i] = x + dino_x_eye_1_offset[i]; + eye_1_y[i] = y + dino_y_eye_1_offset[i]; + eye_2_x[i] = x + dino_x_eye_2_offset[i]; + eye_2_y[i] = y + dino_y_eye_2_offset[i]; + } + } + + uLCD.gfx_PolygonFilled(28, dino_x, dino_y, Color); + + if (status == COLLISION) + { + uLCD.gfx_PolygonFilled(4, eye_2_x, eye_2_y, Picaso::WHITE); + uLCD.gfx_PolygonFilled(4, eye_1_x, eye_1_y, Picaso::BLACK); + } + else + { + uLCD.gfx_PolygonFilled(4, eye_1_x, eye_1_y, Picaso::WHITE); + } + // if t-rex is jumping + if (status == JUMP || status == COLLISION) + { + uint16_t dino_leg_base_x[14]; + uint16_t dino_leg_base_y[14]; + + for (int i = 0; i < 14; i++) + { + dino_leg_base_x[i] = x + dino_x_leg_offset[i]; + dino_leg_base_y[i] = y + dino_y_leg_offset[i]; + } + + uLCD.gfx_PolygonFilled(14, dino_leg_base_x, dino_leg_base_y, Color); + } + // if t-rex is in running animation 1 + else if (status == RUNNING_FRONT) + { + uint16_t dino_leg_front_x[14]; + uint16_t dino_leg_front_y[14]; + + for (int i = 0; i < 14; i++) + { + dino_leg_front_x[i] = x + dino_x_leg_front_offset[i]; + dino_leg_front_y[i] = y + dino_y_leg_front_offset[i]; + } + + uLCD.gfx_PolygonFilled(14, dino_leg_front_x, dino_leg_front_y, Color); + } + // if t-rex is in running animation 2 + else if (status == RUNNING_BACK) + { + uint16_t dino_leg_back_x[15]; + uint16_t dino_leg_back_y[15]; + + for (int i = 0; i < 15; i++) + { + dino_leg_back_x[i] = x + dino_x_leg_back_offset[i]; + dino_leg_back_y[i] = y + dino_y_leg_back_offset[i]; + } + + uLCD.gfx_PolygonFilled(15, dino_leg_back_x, dino_leg_back_y, Color); + } +} + +void drawCactus(uint16_t x, uint16_t y, Picaso::Color color) +{ + uint16_t cactus_x[28]; + uint16_t cactus_y[28]; + + for (int i = 0; i < 28; i++) + { + cactus_x[i] = x + cactus_x_offset[i]; + cactus_y[i] = y + cactus_y_offset[i]; + } + + uLCD.gfx_PolygonFilled(28, cactus_x, cactus_y, color); +} + +//////////////////////////////////////////////////////////////////////////////// +// Functions +//////////////////////////////////////////////////////////////////////////////// + +// These functions convert physical distances to pixel +//---------------------------------------------------------------------- +int toPixz(float z) +{ + return sHeight - int(float(marginH) + z*float(sHeight - 2*marginH)/float(mapHeight)); +} +int toPixx(float x) +{ + return marginW + int(x*float(sWidth - marginW)/float(mapWidth)); +} +int toPix(float r) +{ + return int(r*float(sHeight-2*marginH)/mapHeight); +} + +// This function initializes all the variables when game starts +//---------------------------------------------------------------------- +void init() +{ + // Trex + zTrex = 0; + old_zTrex = -1.5; + vzTrex = 0; + // Obstacle + xObstacle = mapWidth; + old_xObstacle = xObstacle+1; + vxObstacle = 5; + // Motion + collision = false; + // Score + score = 0; + // Cloud + xCloud = 7.5; + old_xCloud = 7.0; + // Restart + restart = false; + + // Screen + //--------------------------- + lcdMtx.lock(); + uLCD.setbaudWait(Picaso::BAUD_600000); + uLCD.gfx_ScreenMode(Picaso::LANDSCAPE); + uLCD.touch_Set(0); + uLCD.txt_BGcolour(Picaso::WHITE); + uLCD.txt_FGcolour(Picaso::BLACK); + uLCD.gfx_RectangleFilled(0, 0, sWidth, sHeight, Picaso::WHITE); + uLCD.gfx_Line(0, toPixz(0)+1, sWidth, toPixz(0)+1, Picaso::BLACK); + uLCD.gfx_Line(0, toPixz(mapHeight)-1, sWidth, toPixz(mapHeight)-1, Picaso::BLACK); + lcdMtx.unlock(); +} + +// This function displays the score +//---------------------------------------------------------------------- +void updateScore() +{ + score++; + if (score%10 == 0) + { + vxObstacle += 0.5; + mySpeaker.PlaySong(trexLvlPass, trexLvlPass_duration); + } +} + +// This function updates the location of Trex and Obstacle every dt_move +//----------------------------------------------------------------------- +void updateLocations() +{ + // Trex + zTrex += vzTrex*dt_move - 0.5*gravity*dt_move*dt_move ; + vzTrex += -gravity*dt_move; + if (zTrex<0) + { + zTrex = 0; + vzTrex = 0; + } + if (zTrex>maxHeightTrex) zTrex = maxHeightTrex; + + // Obstacle + xObstacle -= vxObstacle*dt_move; + if (xObstacle < -2) xObstacle = mapWidth; + + // Check collision + if (((zTrex)*(zTrex) + (xObstacle)*(xObstacle)) < 1) collision = true; + + // Clouds + xCloud -= 0.2*vxObstacle*dt_move; + if (xCloud < 2.5) xCloud = 7.5; +} + +// This function is called when jump button is hit +//---------------------------------------------------------------------- +void jump() +{ + while(1) + { + lcdMtx.lock(); + if (uLCD.touch_Get(0) & vzTrex>=0 & zTrex != maxHeightTrex) + { + vzTrex = 8.0; + mySpeaker.PlaySong(trexJump, trexJump_duration); + } + lcdMtx.unlock(); + } +} + +// This function displays the Trex +//---------------------------------------------------------------------- +void displayTrex() +{ + float tmp; + Status status = RUNNING_BACK; + int countStatus = 0; + while(1) + { + countStatus = (++countStatus)%2; + tmp = old_zTrex; + old_zTrex = zTrex; + if (collision) status = COLLISION; + else + { + if (old_zTrex > 0) status = JUMP; + else if (countStatus == 0) status = RUNNING_BACK; + else status = RUNNING_FRONT; + } + + // Mask the Trex at old position + lcdMtx.lock(); + uLCD.gfx_RectangleFilled(marginW, toPixz(tmp) - marginH, 2*marginW, toPixz(0), Picaso::WHITE); + // lcdMtx.unlock(); + + // Draw the Trex at new position + // lcdMtx.lock(); + drawDino(marginW, toPixz(old_zTrex) - marginH, Picaso::BLACK, status); + lcdMtx.unlock(); + + // Wait a little + Thread::wait(dt_disp*1000); + } +} + +// This function displays the Obstacle +//---------------------------------------------------------------------- +void displayObstacle() +{ + float tmp; + while(1) + { + tmp = old_xObstacle; + old_xObstacle = xObstacle; + + // Mask the Obstacle at old position + lcdMtx.lock(); + drawCactus(toPixx(tmp), sHeight-2*marginH, Picaso::WHITE); + //lcdMtx.unlock(); + + // Draw the Obstacle at new position + //lcdMtx.lock(); + drawCactus(toPixx(old_xObstacle), sHeight-2*marginH, Picaso::BLACK); + lcdMtx.unlock(); + + // Wait a little + Thread::wait(dt_disp*1000); + } +} + +// This function displays the sand +//---------------------------------------------------------------------- +void displayGround() +{ + while(1) + { + // Delete the sand + lcdMtx.lock(); + uLCD.gfx_RectangleFilled(0, 202, sWidth, 210, Picaso::WHITE); + lcdMtx.unlock(); + + // Update positions + int dx = toPix(vxObstacle*dt_disp); + for (int i = 0; i < 80; ++i) + { + sandX[i] = (sandX[i] - dx)%320 + 1; + lcdMtx.lock(); + uLCD.gfx_PutPixel(sandX[i], sandY[i], Picaso::BLACK); + lcdMtx.unlock(); + } + + // Wait a little + Thread::wait(dt_disp*1000); + } +} + +// This function displays the clouds +//---------------------------------------------------------------------- +void displayCloud() +{ + float tmp; + while(1) + { + tmp = old_xCloud; + old_xCloud = xCloud; + // Mask previous clouds + lcdMtx.lock(); + uLCD.gfx_CircleFilled(toPixx(tmp), toPixz(zCloud), 10, Picaso::WHITE); + uLCD.gfx_CircleFilled(toPixx(tmp+0.5), toPixz(zCloud+0.3), 15, Picaso::WHITE); + //lcdMtx.unlock(); + + // Draw current clouds + //lcdMtx.lock(); + uLCD.gfx_CircleFilled(toPixx(old_xCloud), toPixz(zCloud), 10, Picaso::LIGHTGREY); + uLCD.gfx_CircleFilled(toPixx(old_xCloud+0.5), toPixz(zCloud+0.3), 15, Picaso::LIGHTGREY); + lcdMtx.unlock(); + + // Wait a little + Thread::wait(dt_disp*1000); + } +} + +// This function displays score +//---------------------------------------------------------------------- +void displayScore() +{ + char str[50]; + while(1) + { + sprintf(str, "\n\n Score: %d\0", score); + lcdMtx.lock(); + uLCD.txt_MoveCursor(0, 0); + uLCD.putStr(str); + lcdMtx.unlock(); + Thread::wait(1000); + } +} + +// This function tells the player that he lost +//---------------------------------------------------------------------- +void gameOver() +{ + mySpeaker.PlaySong(trexGameOver, trexGameOver_duration); + lcdMtx.lock(); + uLCD.gfx_Cls(); + uLCD.gfx_RectangleFilled(0, 0, sWidth, sHeight, Picaso::BLACK); + uLCD.txt_BGcolour(Picaso::BLACK); + uLCD.txt_FGcolour(Picaso::WHITE); + uLCD.putStr("\n\n\0"); + uLCD.txt_Height(3.25); + uLCD.txt_Width(3.25); + uLCD.putStr("\n\n GAME OVER\0"); + uLCD.txt_Height(1); + uLCD.txt_Width(1); + uLCD.putStr("\n\n\n Touch the screen to restart ! \0"); + lcdMtx.unlock(); + while (1) + { + if (uLCD.touch_Get(0)) + { + restart = true; + break; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// MAIN FUNCTION +//////////////////////////////////////////////////////////////////////////////// + +int main() { + + while(restart) + { + // Initializations + //----------------------------------------- + init(); + //----------------------------------------- + + // Laucnch thread and tickers + //----------------------------------------- + motion.attach(&updateLocations, dt_move); + scoreTicker.attach(&updateScore, 1); + Thread Jump(jump); + Thread Trex(displayTrex); + Thread Obstacle(displayObstacle); + Thread Sand(displayGround); + Thread Cloud(displayCloud); + Thread Score(displayScore); + + // Main loop + //----------------------------------------- + while(!collision) {} + + // Terminate threads and detach tickers + //----------------------------------------- + motion.detach(); + scoreTicker.detach(); + Jump.terminate(); + wait(0.5); // Wait for the right positions to appear on screen + Trex.terminate(); + Obstacle.terminate(); + Sand.terminate(); + Cloud.terminate(); + Score.terminate(); + wait(0.5); // Wait for the uLCD to be free ? + + // Game Over + //----------------------------------------- + gameOver(); + } +} +