A Demo Tap-Game for the 4D Systems GEN4-24PT touch display
Dependencies: mbed uLCD_4D_Picaso
Diff: main.cpp
- 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(); + } + } + } +}