A Demo Tap-Game for the 4D Systems GEN4-24PT touch display

Dependencies:   mbed uLCD_4D_Picaso

Revision:
0:2254d7e8172a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Nov 02 20:58:36 2016 +0000
@@ -0,0 +1,419 @@
+#include "mbed.h"
+#include "uLCD_4D_Picaso.h"
+#include <iostream>
+#include <queue>
+#include <math.h>
+
+/**
+* Demo program for uLCD_4D_Picaso resistance touchscreen 
+* Simple demo game-application on resistive touchscreen display displaying images and using printf.
+* @version 1.0
+* @author Christian Steinmetz, Sam Horwich
+*/
+// three pins are: TX   RX  RESET
+
+uLCD_4D_Picaso lcd(p9, p10, p11);
+uint16_t handle;    //for images (countdown 3,2,1)
+double level=3.0;   //current level indicator, level=((game_level+5)/2)
+int menu=0;         //menu==0 --> start menu
+unsigned long xpos[]={0,40,120,200,40,120,200,40,120,200};  //centers of the nine circular buttons
+unsigned long ypos[]={0,40,40,40,120,120,120,200,200,200};
+Timer t;            //used for timing later
+Timer randt;        //used as a random number generator
+int err=0;          //error indicator
+bool redo=0;        //true if same pattern shall be displayed after error
+int games=0;        //number of games started
+
+
+//creates a random integer out of the current time (random as human input is varying)
+int rnd()
+{
+    return (int) randt.read_us();
+}
+
+//displays the image with name "image.gci" (created from graphics composer) at position (x,y)
+void disp_im(int x, int y, char * name)
+{
+    handle=lcd.file_Open(name,'r');
+    lcd.file_Image(x,y,handle);
+}    
+
+//colors the button number 'pos' in color col
+void col(int pos, Picaso::Color col)
+{
+    int j=(pos-1)/3;
+    int i=pos-j*3-1;
+    lcd.gfx_CircleFilled(40+80*i,40+80*j,37, col);
+}
+
+//creates the setting of the beginning (white background, 9 black circular buttons)
+void black()
+{
+    lcd.gfx_RectangleFilled(0, 0, 240, 320, Picaso::WHITE);
+    for(int i=0;i<3;i++)
+    {
+        for(int j=0;j<3;j++)
+        {
+            lcd.gfx_CircleFilled(40+80*i,40+80*j,37, Picaso::BLACK);
+        }
+    }
+}
+
+int main() {
+    randt.start();//for random numbers!
+    wait(1);
+    // change the baudrate to improve or decrease the latency and plotting quality
+    lcd.setbaudWait(Picaso::BAUD_600000);
+    
+    //initialize SD card in LCD
+    lcd.file_Mount();
+    
+    //text color
+    lcd.txt_FGcolour(Picaso::BLUE);
+            
+    //qu: Queue in which the current pattern is stored
+    //qu2: when qu is dequeued bc of human input, qu2 stores the information from qu which may later be needed in case of an error, ...
+    queue <int> qu;
+    queue <int> qu2;
+    
+    lcd.touch_Set(0);   //initialize touch screen
+    int status = 0;     //stores information from touch screen: 0 if untouched, 1 if touched (2 if released, 3 if moved)
+    int oldst = 0;      //stores old status (so that only changes in status result in events) [may not be needed here...]
+    unsigned long x = 0;//position of finger contact (updated when screen is touched)
+    unsigned long y = 0;
+    while(1) {
+        if(menu==0)     //start menu
+        {
+            lcd.txt_BGcolour(Picaso::ORANGE);
+            lcd.txt_Height(4);
+            lcd.txt_Width(2);
+            lcd.txt_MoveCursor(1,2);
+            lcd.printf(" DEMO-GAME ");
+            
+            lcd.txt_Height(3);
+            lcd.txt_Width(1);
+            lcd.txt_MoveCursor(5,9);
+            lcd.printf(" Start Game ");
+            
+            lcd.txt_Height(3);
+            lcd.txt_MoveCursor(7,8);
+            lcd.printf(" Instructions ");
+            lcd.txt_Height(1);
+            
+            oldst=status;
+            status = lcd.touch_Get(0);  //get status from touch screen as explained above
+            if (status&&(oldst==0))     //change in status from 0 to 1
+            {
+                x = lcd.touch_Get(1);
+                y = lcd.touch_Get(2);
+                if(x>70&&x<170&&y>170&&y<230)   //start game button-->reset everything
+                {
+                    lcd.txt_BGcolour(Picaso::WHITE);    //white backgound for texts
+                    menu=1;                             //pattern show mode
+                    redo=0;                             //in the beginning, new pattern shall be created
+                    err=0;                              //no errors in the beginning
+                    level=3.0;                          //beginner's level is 3 (=(game_level+5)/2
+                    t.stop();                           //reset timer if used before
+                    t.reset();
+                    lcd.gfx_Cls();                      //clear
+                    lcd.gfx_RectangleFilled(0, 0, 240, 320, Picaso::WHITE);     //white background for screen
+                    if(games<5)
+                    {
+                        disp_im(68,110,"three.gci");         //countdown
+                        wait(1);
+                        disp_im(68,110,"two.gci");
+                        wait(1);
+                        disp_im(68,110,"one.gci");
+                        wait(1);
+                        games++;    //needs to be updated/needs to be updated now every time the images are displayed-->see comment below
+                    }
+                    //countdown without displaying images: memory space allows only to display ~16 of these images, so afterwards the screen would freeze
+                    //if we still try to display them-->alternative without images is a text countdown...
+                    else                    
+                    {
+                        lcd.txt_Height(5);
+                        lcd.txt_MoveCursor(2,14);
+                        lcd.txt_Width(3);
+                        lcd.printf("3");
+                        wait(1);
+                        lcd.txt_Width(1);
+                        lcd.txt_MoveCursor(2,14);
+                        lcd.txt_Width(3);
+                        lcd.printf("2");
+                        wait(1);
+                        lcd.txt_Width(1);
+                        lcd.txt_MoveCursor(2,14);
+                        lcd.txt_Width(3);
+                        lcd.printf("1");
+                        wait(1);
+                        lcd.txt_Height(1);
+                        lcd.txt_Width(1);
+                    }
+                    black();                            //game setting (9 circular black buttons)
+                }
+                else if(x>65&&x<175&&y>255&&y<290)      //show instructions as a text
+                {
+                    menu=4;                             //go to instructions menu
+                    lcd.gfx_Cls();
+                    lcd.txt_MoveCursor(1,1);
+                    lcd.printf("Tap the buttons in the shown\n\rorder. Do not tap the wrong \n\rbuttons, as you can only    \n\rmake a maximum of 3 mistakes\n\rbefore losing.\n\r");
+                    lcd.printf("\n\rThe longer you play, the \n\rharder the levels will be. \n\rThere are no time limits, so\n\rhave fun and try to remember\n\ran infinitely long \n\rcombination.\n\r");
+                    lcd.printf("\n\rTap anywhere to get back to \n\rthe menu and get going!");
+                }
+            }
+        }
+        if(menu==1)     //show pattern mode
+        {
+            lcd.txt_MoveCursor(22,13);  //write level
+            lcd.txt_Height(1);
+            lcd.printf("Level");
+            lcd.txt_MoveCursor(24,15);
+            lcd.txt_Height(2);
+            lcd.printf("%i",((int)ceil(level*2-5)));
+            
+            lcd.txt_Height(1);      //write errors
+            lcd.txt_MoveCursor(22,3);
+            lcd.printf("Lives");
+            lcd.txt_MoveCursor(24,4);
+            lcd.txt_Height(2);
+            lcd.printf("%i/3",3-err);
+            
+            lcd.txt_Height(1);      //MENU-button
+            lcd.txt_MoveCursor(23,22);
+            lcd.txt_Height(3);
+            lcd.printf("M E N U");
+            lcd.txt_Height(2);
+        
+            if(err==3)          //in that case, you lost--> menu=3
+            {
+                lcd.gfx_RectangleFilled(0, 240, 240, 320, Picaso::WHITE);
+                lcd.txt_MoveCursor(11,10);
+                lcd.printf("GAME  OVER");
+                
+                lcd.txt_Height(1);
+                lcd.txt_MoveCursor(25,11);
+                lcd.printf("Level %2i",(int)ceil(level*2-5));
+                
+                lcd.txt_Height(1);
+                lcd.txt_MoveCursor(23,22);
+                lcd.txt_Height(3);
+                lcd.printf("M E N U");
+                lcd.txt_Height(2);
+            
+                lcd.txt_Height(1);
+                lcd.txt_MoveCursor(23,2);
+                lcd.txt_Height(3);
+                lcd.printf("N E W");
+                lcd.txt_Height(2);
+                
+                menu=3;
+            }
+            else        //show pattern
+            {
+                double wt=0.1*log(level/1.4);   //speed of showed pattern decreases iwth length of pattern!--> makes rnd() more random!
+                wait(3*wt);
+                if(redo==0)     //new pattern wanted
+                {
+                    for(int i=0;i<level;i++)    //creates a new pattern (integer numbers of buttons) of length ceil(level) and writes it in the queue qu
+                    {
+                        int pos=rnd()%9+1;  
+                        qu.push(pos);
+                        col(pos,Picaso::ORANGE);    //shows pattern: button blinking in orange
+                        wait(wt*3);
+                        col(pos,Picaso::BLACK);     //back to black
+                        wait(wt);
+                        if(qu2.size()>0)            //if qu2 exists, we want to clear it as it is not needed (redo==1!)
+                        {
+                            qu2.pop();
+                        }
+                    }
+                }
+                else        //we want to show the old pattern again (there was an error...)
+                {
+                    redo=0;             //want to get a new pattern next time if no mistake is made!
+                    for(int i=0;i<level;i++)    //restore old pattern from qu2 in qu (afterwards, qu2 is not needed anymore-->cleared)
+                    {
+                        qu.push(qu2.front());
+                        col(qu2.front(),Picaso::ORANGE);    //buttons blink again
+                        wait(wt*3);
+                        col(qu2.front(),Picaso::BLACK);
+                        wait(wt);
+                        qu2.pop();
+                    }
+                }
+                menu=2;         //go to human input mode
+            }
+        }
+        if(menu==2)     //now we try to retap the buttons in the right order
+        {
+            t.start();  //timer needed later
+            oldst=status;
+            status = lcd.touch_Get(0);
+            if (status&&(oldst==0))
+            {
+                x = lcd.touch_Get(1);   //positions of finger contact
+                y = lcd.touch_Get(2);
+                
+                if((unsigned long)((x-xpos[qu.front()])*(x-xpos[qu.front()])+(y-ypos[qu.front()])*(y-ypos[qu.front()]))<=1444)  //checks if we are more or less inside the circle at the first position of qu
+                {
+                    col(qu.front(),Picaso::GREEN);  //green==good
+                    t.reset();
+                    //this loop is needed so that the green light is actually shown and it is not continuously trying to get green and black again at the same time
+                    //only if button is released for at least 0.15s (so not due to a too light pressure on the screen for a few milliseconds),
+                    //the button goes back to black
+                    while(1)                      
+                    {
+                        oldst=status;
+                        status = lcd.touch_Get(0);
+                        
+                        if(status!=0){t.reset();}
+                        if(status==0&&t.read()>0.15){break;}
+                    }
+                    col(qu.front(),Picaso::BLACK);
+                    if(qu.size()==1)        //after the last element , a longer green signal shows that the pattern is finished and correct
+                    {
+                        col(qu.front(),Picaso::BLACK);
+                        wait(.2);
+                        col(qu.front(),Picaso::GREEN);
+                        wait(.1);
+                        col(qu.front(),Picaso::BLACK);
+                        wait(.1);
+                        col(qu.front(),Picaso::GREEN);
+                        wait(1);
+                        col(qu.front(),Picaso::BLACK);
+                        wait(0.8);
+                        level+=0.5;         //now, as we were correct, we can also increase our level and go back to the new show pattern-phase (menu==1)
+                        menu=1;
+                    }
+                    qu2.push(qu.front());   //store qu in qu2 (as it may be needed above)
+                    qu.pop();               //clear first element from qu
+                }
+                else if(y<240)          //if we press the wrong button...
+                {
+                    unsigned long dist=100000;
+                    unsigned long min=0;    //the following writes the button in 'min' that is closest to the finger contact
+                    for(int i=1;i<10;i++)
+                    {
+                        if((x-xpos[i])*(x-xpos[i])+(y-ypos[i])*(y-ypos[i])<dist)
+                        {
+                            min=i;
+                            dist=(x-xpos[i])*(x-xpos[i])+(y-ypos[i])*(y-ypos[i]);
+                        }
+                    }
+                    if(min!=qu.front()) //if closest button is the right one, but we press outside of it, nothing happens. IF NOT: Error routine in following lines
+                    {
+                        col(min,Picaso::RED);   //red==bad
+                        wait(1.5);
+                        col(min,Picaso::BLACK);
+                        redo=1;     //now we want to show the right pattern again next time (good that we have qu2...)
+                        menu=1;     //new show pattern-phase
+                        err++;      //error number updated
+                    }
+                    while(qu.size()>0)  //shift remaining information from qu in qu2
+                    {
+                        qu2.push(qu.front());
+                        qu.pop();
+                    }
+                }
+                else if(x>170&&y>270)   //MENU-button
+                {
+                    menu=0;     //back to start menu
+                    lcd.gfx_Cls();  //reset lcd
+                    while(qu.size()>0)  //clear queues
+                    {
+                        qu.pop();
+                    }
+                    while(qu2.size()>0)
+                    {
+                        qu2.pop();
+                    }
+                }
+            }
+        }
+        if(menu==3)     //GAME OVER...
+        {
+            oldst=status;
+            status = lcd.touch_Get(0);
+            if (status&&(oldst==0))
+            {
+                x = lcd.touch_Get(1);
+                y = lcd.touch_Get(2);
+                if(x>170&&y>270)        //MENU-button as above
+                {
+                    menu=0;
+                    lcd.gfx_Cls();
+                    while(qu.size()>0)
+                    {
+                        qu.pop();
+                    }
+                    while(qu2.size()>0)
+                    {
+                        qu2.pop();
+                    }
+                }
+                else if(x<70&&y>270)    //NEW game-button
+                {
+                    while(qu.size()>0)  //clear queues
+                    {
+                        qu.pop();
+                    }
+                    while(qu2.size()>0)
+                    {
+                        qu2.pop();
+                    }
+                    menu=1;     //back to show-mode
+                    redo=0;     //new pattern required
+                    err=0;      //no errors in the beginning
+                    level=3.0;  //first level is that (you should know by now)
+                    t.stop();   //reset timer (as the other timer is used for random numbers, we don't reset it, an overflow would not matter at all!)
+                    t.reset();
+                    lcd.gfx_Cls();  //reset lcd
+                    lcd.gfx_RectangleFilled(0, 0, 240, 320, Picaso::WHITE); //countdown:
+                    if(games<5)
+                    {
+                        disp_im(68,110,"three.gci");         //countdown
+                        wait(1);
+                        disp_im(68,110,"two.gci");
+                        wait(1);
+                        disp_im(68,110,"one.gci");
+                        wait(1);
+                        games++;    //needs to be updated now every time the images are displayed-->see comment below
+                    }
+                    //countdown without displaying images: memory space allows only to display ~16 of these images, so afterwards the screen would freeze
+                    //if we still try to display them-->alternative without images is a text countdown...
+                    else
+                    {
+                        lcd.txt_Height(5);
+                        lcd.txt_MoveCursor(2,14);
+                        lcd.txt_Width(3);
+                        lcd.printf("3");
+                        wait(1);
+                        lcd.txt_Width(1);
+                        lcd.txt_MoveCursor(2,14);
+                        lcd.txt_Width(3);
+                        lcd.printf("2");
+                        wait(1);
+                        lcd.txt_Width(1);
+                        lcd.txt_MoveCursor(2,14);
+                        lcd.txt_Width(3);
+                        lcd.printf("1");
+                        wait(1);
+                        lcd.txt_Height(1);
+                        lcd.txt_Width(1);
+                    }
+                    black();        //first screen of game
+                }
+            }
+        }
+        if(menu==4)
+        {
+            oldst=status;
+            status = lcd.touch_Get(0);
+            if (status&&(oldst==0)) //tap ANYWHERE to get back to the menu screen
+            {
+                menu=0;
+                lcd.gfx_Cls();
+            }
+        }
+    }
+}