play snake on linux
在写完超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...
以及输出基本靠定位定点输出,\n,\t 什么的都会gg你懂的吧
4.于是我们把pritf全都换成printw
抛弃 \n \t 改用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 }
感谢参考资料