在写完超Low的windows上的贪吃蛇后
被人吐槽了几个方面:
1.界面真的Low,开始,结束,游戏中,都太简陋了...
2.每次都清屏在输出字符矩阵的解决方案...太晃眼了
3.一个BUG,为了解决贪吃蛇隔固定时间time移动一个单位的问题
我们写的是while(距上次移动时间 < time && 没有键盘方向键的读入);
于是我们惊喜的发现,只要一直摁方向键,就不必等待固定时间time
而是会直接下一步移动...手动加快贪吃蛇移动速度...
但是我们暂时并不想改进这个程序...毕竟怎么说还是能玩一玩的
于是ytz决定在自己的deepin系统上写一个能运行的贪吃蛇...
先想到的方法当然是直接把windows上的代码拿来改一改啊...
但很快我们就遇到了重重障碍!
1.我们使用了conio.h中的_kbhit函数来判断是否有键盘读入
然而linux系统下是没有conio.h这个库的...
百度了一下linux下也没有自带库函数有相同功能
于是我们就百度了一个手动实现_kbhit函数加进去
(这个实现原理不是很懂我是照抄的...)
2.conio.h中的getch函数同样需要替代品
这时候就有人指出明路,curses.h库里有啊
然后我们需要先安装这个库,在终端输入
sudo apt-get install libncurses5-dev
回车即可开始安装
然后编译时需要加入 -lncurses 命令
比如 g++ -o Snake -lncurses Snake.cpp
否则编译无法通过
3.啊,编译通过了!
我们愉快的运行一下吧!
运行出了一坨屎!
我们百度一下curses.h 这个库
发现是一个图形库,类似于大一学习C和C++的时候
老师提供的windows上的的第三方库ege.h
只不过curses的评价似乎比ege好一点2333
然后ege那个你懂的吧,开始运行进入图形界面后
各种函数失效,printf,scanf...
以及输出基本靠定位定点输出, , 什么的都会gg你懂的吧
4.于是我们把pritf全都换成printw
抛弃 改用move来移动光标位置
然后只在游戏开始时使用一次清屏system("clear")
其它都使用 curses.h 里的 refresh 函数即可
(我们的 printw 是输出在逻辑屏幕上的,
refresh 函数用来同步逻辑屏幕和物理屏幕
物理屏幕即我们看到的屏幕
然后refresh函数表现的十分流畅的
最重要的是,它不晃眼!!!)
然后终于能正确打印出字符矩阵了!
5.汉字在终端显示成乱码
百度了很久没有找到解决方案
我们就退让一步,把汉字都改成了英文
然后调用noecho函数,让我们的按键不显示在屏幕上
尝试运行......发现我们造出来了一条瘫痪蛇...
不贪吃了瘫痪了...我们不摁下方向键它自己不动...
先把瘫痪蛇代码放上来吧
1 #include <ctime> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <stdio.h> 5 #include <termios.h> 6 #include <unistd.h> 7 #include <fcntl.h> 8 #include <curses.h> 9 10 #define map(pos) map[pos.x][pos.y] 11 12 char map[30][30]; 13 14 struct point { 15 int x, y; 16 17 void _rand() { 18 x = rand() % 20 + 1; 19 y = rand() % 20 + 1; 20 } 21 22 bool operator == (const point &a) const { 23 return x == a.x && y == a.y; 24 } 25 26 }; 27 28 int head, tail; 29 point snake[500], food, next; 30 int dir, grade, length, uptime; 31 32 inline int _kbhit() { 33 termios oldt, newt; 34 int ch, oldf; 35 tcgetattr(STDIN_FILENO, &oldt); 36 newt = oldt; 37 newt.c_lflag &= -(ICANON | ECHO); 38 tcsetattr(STDIN_FILENO, TCSANOW, &newt); 39 oldf = fcntl(STDIN_FILENO, F_GETFL, 0); 40 fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); 41 ch = getchar(); 42 tcsetattr(STDIN_FILENO, TCSANOW, &oldt); 43 fcntl(STDIN_FILENO, F_SETFL, oldf); 44 if(ch != EOF) { 45 ungetc(ch, stdin); 46 return 1; 47 } 48 return 0; 49 } 50 51 inline void find_food() { 52 do { 53 food._rand(); 54 }while(map(food) != ' '); 55 map(food) = '*'; 56 } 57 58 inline void update() { 59 //system("clear"); 60 for(int i = 0;i < 22;i ++) { 61 for(int j = 0;j < 22;j ++) 62 move(i + 3, 3 + j * 2), printw("%c ", map[i][j]); 63 if(i == 0) move(i + 3, 60), printw("Level:%d", grade); 64 if(i == 2) move(i + 3, 60), printw("length:%d", length); 65 if(i == 6) move(i + 3, 60), printw("Time_interval:"); 66 if(i == 8) move(i + 3, 60), printw("%d ms", uptime); 67 } 68 refresh(); 69 } 70 71 inline bool GO() { 72 bool timeover = 1; 73 double start = (double) clock() / CLOCKS_PER_SEC; 74 while((timeover = (double) clock() / CLOCKS_PER_SEC <= start + uptime / 1000.0) && !(_kbhit())); 75 if(timeover) dir = getch(); 76 next = snake[head]; 77 switch (dir) { 78 case 'w':next.x -= 1;break; 79 case 's':next.x += 1;break; 80 case 'a':next.y -= 1;break; 81 case 'd':next.y += 1;break; 82 default: 83 move(30, 10); 84 printw("Game Over!"); 85 return 0; 86 } 87 if(!next.x || next.x == 21 || !next.y || next.y == 21) { 88 move(30, 10); 89 printw("Game Over!"); 90 return 0; 91 } 92 if(map(next) != ' ' && !(next == food)) { 93 move(30, 10); 94 printw("Game Over!"); 95 return 0; 96 } 97 if(length == 400) { 98 move(30, 10); 99 printw("Game Over!"); 100 return 0; 101 } 102 return 1; 103 } 104 105 int main() { 106 initscr(); 107 clear(); 108 curs_set(0); 109 srand(19980320); 110 for(int i = 1;i <= 20;i ++) 111 for(int j = 1;j <= 20;j ++) 112 map[i][j] = ' '; 113 for(int i = 0;i < 22;i ++) 114 map[i][0] = map[21][i] = map[0][i] = map[i][21] = '!'; 115 map[1][1] = map[1][2] = 'o', map[1][3] = '@'; 116 snake[0] = (point){1, 1}; 117 snake[1] = (point){1, 2}; 118 snake[2] = (point){1, 3}; 119 head = 2, tail = 0, grade = 1, length = 3, uptime = 500; 120 find_food(), dir = 'd'; 121 122 move(3, 10); 123 printw("Let's play snake!"); 124 refresh(); 125 double start; 126 for(int i = 3;i >= 0;i --) { 127 start = (double)clock() / CLOCKS_PER_SEC; 128 while((double)clock() / CLOCKS_PER_SEC <= start + 1); 129 if(i > 0) { 130 //system("clear"); 131 move(3, 10); 132 printw("Enter the countdown:%d", i); 133 refresh(); 134 } 135 else { 136 update(); 137 } 138 } 139 140 while(1) { 141 int tmp = GO(); 142 refresh(); 143 if(tmp) { 144 if(next == food) { 145 length ++; 146 if(length % 10 == 0) { 147 grade ++; 148 if(uptime >= 100) uptime -= 50; 149 } 150 map(next) = '@'; 151 map(snake[head]) = 'o'; 152 head = (head + 1) % 500; 153 snake[head] = next; 154 find_food(), update(); 155 } 156 else { 157 map(snake[tail]) = ' '; 158 tail = (tail + 1) % 500; 159 map(next) = '@'; 160 map(snake[head]) = 'o'; 161 head = (head + 1) % 500; 162 snake[head] = next; 163 update(); 164 } 165 } 166 else break; 167 } 168 getch(); 169 endwin(); 170 return 0; 171 }
6.我们经过实验发现
问题出在getch和kbhit函数的配合上面
于是我们修改一下kbhit函数
在获得键盘读入之后不再把字符存入缓冲区
而是直接return回去就好了!
然后为了解决开始提到的第三个问题
我们在获得键盘读入之后
依旧等待固定时间间隔结束再让蛇移动即可!
然后为了解决第一个问题...
我加了个最终分数提示...
重新开始游戏我懒就没加...
至此我们的windows贪吃蛇魔改之linux版基本就完成了!
(我太懒了所以依然没有加注释
1 #include <ctime> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <stdio.h> 5 #include <termios.h> 6 #include <unistd.h> 7 #include <fcntl.h> 8 #include <curses.h> 9 10 #define map(pos) map[pos.x][pos.y] 11 12 char map[30][30]; 13 14 struct point { 15 int x, y; 16 17 void _rand() { 18 x = rand() % 20 + 1; 19 y = rand() % 20 + 1; 20 } 21 22 bool operator == (const point &a) const { 23 return x == a.x && y == a.y; 24 } 25 26 }; 27 28 int head, tail; 29 point snake[500], food, next; 30 int dir, grade, length, uptime; 31 32 inline int _kbhit() { 33 termios oldt, newt; 34 int ch, oldf; 35 tcgetattr(STDIN_FILENO, &oldt); 36 newt = oldt; 37 newt.c_lflag &= -(ICANON | ECHO); 38 tcsetattr(STDIN_FILENO, TCSANOW, &newt); 39 oldf = fcntl(STDIN_FILENO, F_GETFL, 0); 40 fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); 41 ch = getchar(); 42 tcsetattr(STDIN_FILENO, TCSANOW, &oldt); 43 fcntl(STDIN_FILENO, F_SETFL, oldf); 44 if(ch != EOF) return ch; 45 return 0; 46 } 47 48 inline void find_food() { 49 do { 50 food._rand(); 51 }while(map(food) != ' '); 52 map(food) = '*'; 53 } 54 55 inline void update() { 56 //system("clear"); 57 for(int i = 0;i < 22;i ++) { 58 for(int j = 0;j < 22;j ++) 59 move(i + 3, 3 + j * 2), printw("%c ", map[i][j]); 60 if(i == 0) move(i + 3, 60), printw("Level:%d", grade); 61 if(i == 2) move(i + 3, 60), printw("length:%d", length); 62 if(i == 6) move(i + 3, 60), printw("Time_interval:"); 63 if(i == 8) move(i + 3, 60), printw("%d ms", uptime); 64 } 65 refresh(); 66 } 67 68 inline bool GO() { 69 int ch; 70 bool timeover = 1; 71 double start = (double) clock() / CLOCKS_PER_SEC; 72 while((timeover = (double) clock() / CLOCKS_PER_SEC <= start + uptime / 1000.0) && !(ch = _kbhit())); 73 if(timeover) { 74 while(timeover = (double) clock() / CLOCKS_PER_SEC <= start + uptime / 1000.0); 75 dir = ch; 76 } 77 next = snake[head]; 78 switch (dir) { 79 case 'w':next.x -= 1;break; 80 case 's':next.x += 1;break; 81 case 'a':next.y -= 1;break; 82 case 'd':next.y += 1;break; 83 default:return 0; 84 } 85 if(!next.x || next.x == 21 || !next.y || next.y == 21) return 0; 86 if(map(next) != ' ' && !(next == food)) return 0; 87 if(length == 400) return 0; 88 return 1; 89 } 90 91 int main() { 92 initscr(); 93 noecho(); 94 clear(); 95 curs_set(0); 96 srand(19980320); 97 for(int i = 1;i <= 20;i ++) 98 for(int j = 1;j <= 20;j ++) 99 map[i][j] = ' '; 100 for(int i = 0;i < 22;i ++) 101 map[i][0] = map[21][i] = map[0][i] = map[i][21] = '!'; 102 map[1][1] = map[1][2] = 'o', map[1][3] = '@'; 103 snake[0] = (point){1, 1}; 104 snake[1] = (point){1, 2}; 105 snake[2] = (point){1, 3}; 106 head = 2, tail = 0, grade = 1, length = 3, uptime = 500; 107 find_food(), dir = 'd'; 108 109 move(3, 10); 110 printw("Let's play snake!"); 111 refresh(); 112 double start; 113 for(int i = 3;i >= 0;i --) { 114 start = (double)clock() / CLOCKS_PER_SEC; 115 while((double)clock() / CLOCKS_PER_SEC <= start + 1); 116 if(i > 0) { 117 //system("clear"); 118 move(3, 10); 119 printw("Enter the countdown:%d", i); 120 refresh(); 121 } 122 else { 123 update(); 124 } 125 } 126 127 while(1) { 128 int tmp = GO(); 129 refresh(); 130 if(tmp) { 131 if(next == food) { 132 length ++; 133 if(length % 10 == 0) { 134 grade ++; 135 if(uptime >= 100) uptime -= 50; 136 } 137 map(next) = '@'; 138 map(snake[head]) = 'o'; 139 head = (head + 1) % 500; 140 snake[head] = next; 141 find_food(), update(); 142 } 143 else { 144 map(snake[tail]) = ' '; 145 tail = (tail + 1) % 500; 146 map(next) = '@'; 147 map(snake[head]) = 'o'; 148 head = (head + 1) % 500; 149 snake[head] = next; 150 update(); 151 } 152 } 153 else { 154 move(30, 20); 155 printw("Game Over!"); 156 move(32, 14); 157 printw("Your Final Score : %d", length); 158 break; 159 } 160 } 161 getch(); 162 endwin(); 163 return 0; 164 }
感谢参考资料