A Demo Tap-Game for the 4D Systems GEN4-24PT touch display
Dependencies: mbed uLCD_4D_Picaso
main.cpp
- Committer:
- csteinmetz
- Date:
- 2016-11-02
- Revision:
- 0:2254d7e8172a
File content as of revision 0:2254d7e8172a:
#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(); } } } }