[問題] 貪食蛇遇到阻斷式getch問題

看板C_and_CPP作者 (老柏~)時間10年前 (2015/08/13 21:40), 編輯推噓2(2046)
留言48則, 4人參與, 最新討論串1/2 (看更多)
開發平台(Platform): (Ex: VC++, GCC, Linux, ...) Mac OS (g++) 額外使用到的函數庫(Library Used): (Ex: OpenGL, ...) ncurses.h 問題(Question): 目前寫的貪食蛇練習題,可以分別讓蛇自動前進及按上下左右鍵盤前進 但嘗試想把兩個功能合併,卻疑似發生阻斷,已經爬文過但還不知道怎麼解 在下面的程式碼有highlight,求解 餵入的資料(Input): 預期的正確結果(Expected Output): 錯誤結果(Wrong Output): 程式碼(Code):(請善用置底文網頁, 記得排版) #include <iostream> #include <ncurses.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> #include <termios.h> #include <fcntl.h> using namespace std; //int move(int y , int x); #define Initial_Length 5 #define width 50 #define hieght 20 typedef struct snack{ int x; int y; }Snack; Snack Sbody[1000]; Snack Newbody; Snack Food; void Show_Bulletinboard() { mvaddstr(2,55,"Press Enter or Space to start."); mvaddstr(6,55,"Press P or p to pause."); mvaddstr(10,55,"Press to arrow key to move."); //加一個狀態欄位 //mvaddch(10,55,'\x18\x19\x1a\x1b'); mvaddstr(14,55,"Grade:"); mvaddstr(18,55,"Speed:"); } void Initial_Snack_Position() { for (int i = 0; i < Initial_Length ; i++) { Sbody[i].y = 2; Sbody[i].x = Initial_Length + 1 - (i); } } void Print_Snack(int *Length) { for (int i = 0; i < *Length ; i++) { if (i == 0) { //改變頭的顏色 mvaddstr(Sbody[i].y, Sbody[i].x , "@"); //printf("\033[33m@"); } else { mvaddstr(Sbody[i].y, Sbody[i].x , "@"); } } } void Print_Dot(int Pos_y, int Pos_x, char ch ) { mvaddch(Pos_y, Pos_x , ch); //printf("\033[33m@"); } void Initial_GameZone() { for (int j = 1 ; j <= hieght ; j++) { for (int i = 1; i <= width ; i++) { if (j == 1 || j == hieght) { Print_Dot(j,i,'#'); } else if(i == 1 || i == width) { Print_Dot(j,i,'#'); } } } } void Change_Snack_Position(int * Length) { for(int i = *Length -1 ; i >= 0 ; i--) { if (i == 0) //移動頭 { Sbody[i].x = Newbody.x; Sbody[i].y = Newbody.y; Print_Dot(Sbody[i].y , Sbody[i].x , '@'); } else if (i == *Length -1) //清除尾巴 { Print_Dot(Sbody[i].y, Sbody[i].x, ' '); Sbody[i].x = Sbody[i-1].x; Sbody[i].y = Sbody[i-1].y; } else{ //移動身體 Sbody[i].x = Sbody[i-1].x; Sbody[i].y = Sbody[i-1].y; Print_Dot(Sbody[i].y , Sbody[i].x, '@'); } } } void Create_Food(int * Length) //產生食物的位置 { int count = 0; srand(time(NULL)); do { Food.x = (rand() % (width-2) ) +2; Food.y = (rand() % (hieght-2)) +2; for (int i = 0; i < *Length ; i++) { if (Sbody[i].x != Food.x && Sbody[i].y != Food.y) { count = count + 1; } } }while ( count == *Length); Print_Dot(Food.y , Food.x , '*'); } void isEaten(int *Length) //判斷是否吃到食物 { if (Sbody[0].x == Food.x && Sbody[0].y == Food.y) { *Length = *Length + 1; //當吃到新的果實,長出一節,位置在食物的位置 Sbody[*Length].x = Food.x; Sbody[*Length].y = Food.y; Print_Dot(Sbody[*Length].y,Sbody[*Length].x , '@'); Create_Food( Length ); } } bool isGameOver(int * Length) { bool flag = true; for (int i = 1 ; i < *Length ; i++) //頭碰到身體 { if (Sbody[0].x == Sbody[i].x && Sbody[0].y == Sbody[i].y) { flag = false; } } for (int n = 1 ; n <= hieght ; n++) //頭碰到牆 { for (int m = 1; m <= width ; m++) { if (n == 1 || n == hieght) { if (Sbody[0].x == m && Sbody[0].y == n) { flag = false; } } else if(m == 1 || m == width) { if (Sbody[0].x == m && Sbody[0].y == n) { flag = false; } } } } return flag; } int kbhit(void) { struct termios oldt, newt; int ch; int oldf; tcgetattr(STDIN_FILENO, &oldt); newt = oldt; newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); oldf = fcntl(STDIN_FILENO, F_GETFL, 0); fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); ch = getchar(); tcsetattr(STDIN_FILENO, TCSANOW, &oldt); fcntl(STDIN_FILENO, F_SETFL, oldf); if(ch != EOF) { ungetc(ch, stdin); return 1; } return 0; } int main(void) { WINDOW * w; //產生視窗 w=initscr(); keypad(stdscr,TRUE); //啟動鍵盤特殊按鍵功能 int GameKey =1; noecho(); int Length = Initial_Length; Initial_GameZone(); //產生遊戲邊 筐 Show_Bulletinboard(); Initial_Snack_Position(); Print_Snack(&Length); Create_Food(&Length); while (GameKey != 27) { if ( kbhit() ) { GameKey = getch(); //根據鍵盤上下左右間決定移動方向 switch(GameKey) { case KEY_UP: Newbody.y = Sbody[0].y -1; Newbody.x = Sbody[0].x; setbuf(stdin,NULL); break; case KEY_DOWN: Newbody.y = Sbody[0].y +1; Newbody.x = Sbody[0].x; setbuf(stdin,NULL); break; case KEY_LEFT: Newbody.y = Sbody[0].y; Newbody.x = Sbody[0].x -1; setbuf(stdin,NULL); break; case KEY_RIGHT: Newbody.y = Sbody[0].y; Newbody.x = Sbody[0].x +1; setbuf(stdin,NULL); break; default:; } } else //依據原本的方向前進 { if((Sbody[0].x - Sbody[1].x) == -1) //自動想左移動 { Newbody.y = Sbody[0].y; Newbody.x = Sbody[0].x -1; } else if ((Sbody[0].x - Sbody[1].x) == 1) //自動向右移動 { Newbody.y = Sbody[0].y; Newbody.x = Sbody[0].x +1; } else if ((Sbody[0].y-Sbody[0].y)== -1) //自動向上移動 { Newbody.y = Sbody[0].y -1; Newbody.x = Sbody[0].x; } else if ((Sbody[0].y - Sbody[1].y) == 1) //自動向下移動 { Newbody.y = Sbody[0].y +1; Newbody.x = Sbody[0].x; } else { } sleep(1); refresh(); } Change_Snack_Position(&Length); isEaten (&Length); //GameKey = 1; if ( isGameOver(&Length) == false) //判斷是否GameOver 吃到自己或是撞牆 { break; } } //getchar(); delwin(w); return 0; } http://codepad.org/XP42S3rE 補充說明(Supplement): -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 59.115.156.66 ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1439473205.A.511.html

08/13 21:43, , 1F
你是不是連按會連走? 方向鍵控制方向, 他不會走.
08/13 21:43, 1F

08/13 21:57, , 2F
08/13 21:57, 2F

08/13 21:58, , 3F
兩個功能分開是OK的,但是合在一起就不行
08/13 21:58, 3F

08/13 22:05, , 4F
所以你是希望連按連走 ? 不懂. "方向鍵控制方向. 不要走"
08/13 22:05, 4F

08/13 22:06, , 5F
然後看你要 multithread 還是要把 sleep 換掉
08/13 22:06, 5F

08/13 22:08, , 6F
應該是我按一次上下左右 蛇就會轉向
08/13 22:08, 6F

08/13 22:09, , 7F
不按的時候就照著原方向移動
08/13 22:09, 7F

08/13 22:09, , 8F
正常的貪吃蛇在你按上下左右的時候是不會轉的
08/13 22:09, 8F

08/13 22:10, , 9F
他只是改變方向. 等下一個時間點移動你才看得到轉
08/13 22:10, 9F

08/13 22:10, , 10F
"方向鍵控制方向. 不要移動"
08/13 22:10, 10F

08/13 22:10, , 11F
剩下的問題就是你怎麼控制時間點. 你用 sleep 的話
08/13 22:10, 11F

08/13 22:11, , 12F
一旦睡了要怎麼收到鍵盤資料?
08/13 22:11, 12F

08/13 22:11, , 13F
是的,我現在可以單獨控制上下左右 讓他轉向
08/13 22:11, 13F

08/13 22:12, , 14F
也可以單獨施作,當沒有碰觸上下左右的時候
08/13 22:12, 14F

08/13 22:12, , 15F
蛇會往前跑
08/13 22:12, 15F

08/13 22:12, , 16F
但是這兩個功能和不起來
08/13 22:12, 16F

08/13 22:13, , 17F
好吧. 我放棄. 簡單講就是你上下左右實作是錯的
08/13 22:13, 17F

08/13 22:13, , 18F
你按方向就會動本身是不對的. 其他的我就不跳針了
08/13 22:13, 18F

08/13 22:15, , 19F
我原本是把sleep放在外面
08/13 22:15, 19F

08/13 22:15, , 20F

08/13 22:15, , 21F
謝謝你
08/13 22:15, 21F

08/13 22:16, , 22F
那有沒有想過是因為不能用 sleep ?
08/13 22:16, 22F

08/13 22:16, , 23F
你在睡的時候要怎麼收到鍵盤資料?
08/13 22:16, 23F

08/13 22:17, , 24F
我在思考看看 “方向鍵控制方向,不要移動”
08/13 22:17, 24F

08/13 22:42, , 25F
如果你把 sleep 放在後面, 邏輯上應該就是我要的
08/13 22:42, 25F

08/13 22:42, , 26F
但是我研究了一下你的 code. 感覺 stdin 被弄爛了
08/13 22:42, 26F

08/13 22:43, , 27F
看起來意圖比較像是用 stdin 的 buffer 來避免 multithread
08/13 22:43, 27F

08/13 23:10, , 28F
看看這樣有沒有比較好? http://codepad.org/gi1cVofH
08/13 23:10, 28F

08/13 23:16, , 29F
然後 refresh 跟 sleep 要對調. 不然會 lag..
08/13 23:16, 29F

08/13 23:22, , 30F
為什麼我覺得最大的問題是在 getch() @@
08/13 23:22, 30F

08/13 23:22, , 31F
不知道 linux 有沒有像 windows 類似 GetKeyState 的東西
08/13 23:22, 31F

08/13 23:25, , 32F
看你怎麼想. getch 感覺沒甚麼錯啊. 只是他亂用
08/13 23:25, 32F

08/13 23:26, , 33F
@Feis : 但 getch 不是 Blocking 嗎 ?
08/13 23:26, 33F

08/13 23:27, , 34F
nodelay
08/13 23:27, 34F

08/13 23:35, , 35F
試了一下, 原來是我誤會了 @@
08/13 23:35, 35F

08/13 23:41, , 36F
不過我發現原 po 沒加, 也許這才是他想問的.. Orz
08/13 23:41, 36F

08/13 23:43, , 37F
看看能不能讓kbhit改成直接返回按鍵訊息, 不要再去
08/13 23:43, 37F

08/13 23:44, , 38F
getch第2次.
08/13 23:44, 38F

08/14 08:34, , 39F
我自己覺得是和c大一樣,被kbhit卡住
08/14 08:34, 39F

08/14 08:37, , 40F
我給的版本沒有 kbhit 阿
08/14 08:37, 40F

08/14 08:42, , 41F
我發現因為中文所以貼爛了: http://codepad.org/lyZQziS9
08/14 08:42, 41F

08/14 13:30, , 42F
F大,不是指你的code(我還在研究),我原本已為我
08/14 13:30, 42F

08/14 13:31, , 43F
的code是在kbhit卡住
08/14 13:31, 43F

08/14 15:14, , 44F
你的 kbhit 是自己寫的嗎?
08/14 15:14, 44F

08/14 20:08, , 45F
不是,從網路上找到的,因為linux不支援
08/14 20:08, 45F

08/14 20:08, , 46F
我原本有想要直接用全域變數處理kbhit裡的問題
08/14 20:08, 46F

08/14 20:09, , 47F
但我也不確定我覺得的問題是不是真正的問題@@
08/14 20:09, 47F

08/14 20:09, , 48F
謝謝F大解惑
08/14 20:09, 48F
文章代碼(AID): #1Lp9urKH (C_and_CPP)
文章代碼(AID): #1Lp9urKH (C_and_CPP)