4180 Final Project

Dependencies:   mbed uLCD_4D_Picaso mbed-rtos PinDetect

Files at this revision

API Documentation at this revision

Comitter:
NotAnExpert
Date:
Thu Dec 05 17:27:10 2019 +0000
Commit message:
Published

Changed in this revision

PinDetect.lib Show annotated file Show diff for this revision Revisions of this file
SongPlayer.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-rtos.lib Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
uLCD_4D_Picaso.lib Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r a45fd26bd87b PinDetect.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PinDetect.lib	Thu Dec 05 17:27:10 2019 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/AjK/code/PinDetect/#cb3afc45028b
diff -r 000000000000 -r a45fd26bd87b SongPlayer.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SongPlayer.h	Thu Dec 05 17:27:10 2019 +0000
@@ -0,0 +1,41 @@
+#include "mbed.h"
+// new class to play a note on Speaker based on PwmOut class
+class SongPlayer
+{
+public:
+    SongPlayer(PinName pin) : _pin(pin) {
+// _pin(pin) means pass pin to the constructor
+    }
+// class method to play a note based on PwmOut class
+    void PlaySong(float frequency[], float duration[], float volume=1.0) {
+        vol = volume;
+        notecount = 0;
+        _pin.period(1.0/frequency[notecount]);
+        _pin = volume/2.0;
+        noteduration.attach(this,&SongPlayer::nextnote, duration[notecount]);
+        // setup timer to interrupt for next note to play
+        frequencyptr = frequency;
+        durationptr = duration;
+        //returns after first note starts to play
+    }
+    void nextnote();
+private:
+    Timeout noteduration;
+    PwmOut _pin;
+    int notecount;
+    float vol;
+    float * frequencyptr;
+    float * durationptr;
+};
+//Interrupt Routine to play next note
+void SongPlayer::nextnote()
+{
+    _pin = 0.0;
+    notecount++; //setup next note in song
+    if (durationptr[notecount]!=0.0) {
+        _pin.period(1.0/frequencyptr[notecount]);
+        noteduration.attach(this,&SongPlayer::nextnote, durationptr[notecount]);
+        _pin = vol/2.0;
+    } else
+        _pin = 0.0; //turn off on last note
+}
\ No newline at end of file
diff -r 000000000000 -r a45fd26bd87b main.cpp
--- /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(); 
+    }
+}
+
diff -r 000000000000 -r a45fd26bd87b mbed-rtos.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-rtos.lib	Thu Dec 05 17:27:10 2019 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/mbed_official/code/mbed-rtos/#5713cbbdb706
diff -r 000000000000 -r a45fd26bd87b mbed.bld
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Thu Dec 05 17:27:10 2019 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/mbed_official/code/mbed/builds/65be27845400
\ No newline at end of file
diff -r 000000000000 -r a45fd26bd87b uLCD_4D_Picaso.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/uLCD_4D_Picaso.lib	Thu Dec 05 17:27:10 2019 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/shorwich/code/uLCD_4D_Picaso/#72f00cef468b