4180 Final Project

Dependencies:   mbed uLCD_4D_Picaso mbed-rtos PinDetect

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(); 
+    }
+}
+