第一次作业
(一)项目来源
项目为贪吃蛇小游戏,来源于网上
(二)项目分析
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <Windows.h>//windows编程头文件 4 #include <time.h> 5 #include <conio.h>//控制台输入输出头文件 6 7 #ifndef __cplusplus 8 9 typedef char bool; 10 #define false 0 11 #define true 1 12 13 #endif 14 15 //将光标移动到控制台的(x,y)坐标点处 16 void gotoxy(int x, int y) 17 { 18 COORD coord; 19 coord.X = x; 20 coord.Y = y; 21 SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); 22 } 23 24 25 26 #define SNAKESIZE 100//蛇的身体最大节数 27 #define MAPWIDTH 78//宽度 28 #define MAPHEIGHT 24//高度 29 30 //食物的坐标 31 struct { 32 int x; 33 int y; 34 }food; 35 36 //蛇的相关属性 37 struct { 38 int speed;//蛇移动的速度 39 int len;//蛇的长度 40 int x[SNAKESIZE];//组成蛇身的每一个小方块中x的坐标 41 int y[SNAKESIZE];//组成蛇身的每一个小方块中y的坐标 42 43 }snake; 44 45 //绘制游戏边框 46 void drawMap(); 47 //随机生成食物 48 void createFood(); 49 //按键操作 50 void keyDown(); 51 //蛇的状态 52 bool snakeStatus(); 53 //从控制台移动光标 54 void gotoxy(int x, int y); 55 56 int key = 72;//表示蛇移动的方向,72为按下“↑”所代表的数字 57 58 //用来判断蛇是否吃掉了食物,这一步很重要,涉及到是否会有蛇身移动的效果以及蛇身增长的效果 59 int changeFlag = 0; 60 61 int sorce = 0;//记录玩家的得分 62 63 int i; 64 void drawMap() 65 { 66 //打印上下边框 67 for (i = 0; i <= MAPWIDTH; i += 2)//i+=2是因为横向占用的是两个位置 68 { 69 //将光标移动依次到(i,0)处打印上边框 70 gotoxy(i, 0); 71 printf("■"); 72 //将光标移动依次到(i,MAPHEIGHT)处打印下边框 73 gotoxy(i, MAPHEIGHT); 74 printf("■"); 75 } 76 77 //打印左右边框 78 for (i = 1; i < MAPHEIGHT; i++) 79 { 80 //将光标移动依次到(0,i)处打印左边框 81 gotoxy(0, i); 82 printf("■"); 83 //将光标移动依次到(MAPWIDTH, i)处打印左边框 84 gotoxy(MAPWIDTH, i); 85 printf("■"); 86 } 87 88 //随机生成初试食物 89 while (1) 90 { 91 srand((unsigned int)time(NULL)); 92 food.x = rand() % (MAPWIDTH - 4) + 2; 93 food.y = rand() % (MAPHEIGHT - 2) + 1; 94 //生成的食物横坐标的奇偶必须和初试时蛇头所在坐标的奇偶一致,因为一个字符占两个字节位置,若不一致 95 //会导致吃食物的时候只吃到一半 96 if (food.x % 2 == 0) 97 break; 98 } 99 //将光标移到食物的坐标处打印食物 100 gotoxy(food.x, food.y); 101 printf("*"); 102 103 //初始化蛇的属性 104 snake.len = 3; 105 snake.speed = 200; 106 107 //在屏幕中间生成蛇头 108 snake.x[0] = MAPWIDTH / 2 + 1;//x坐标为偶数 109 snake.y[0] = MAPHEIGHT / 2; 110 //打印蛇头 111 gotoxy(snake.x[0], snake.y[0]); 112 printf("■"); 113 114 //生成初试的蛇身 115 for (i = 1; i < snake.len; i++) 116 { 117 //蛇身的打印,纵坐标不变,横坐标为上一节蛇身的坐标值+2 118 snake.x[i] = snake.x[i - 1] + 2; 119 snake.y[i] = snake.y[i - 1]; 120 gotoxy(snake.x[i], snake.y[i]); 121 printf("■"); 122 } 123 //打印完蛇身后将光标移到屏幕最上方,避免光标在蛇身处一直闪烁 124 gotoxy(MAPWIDTH - 2, 0); 125 return; 126 } 127 128 void keyDown() 129 { 130 int pre_key = key;//记录前一个按键的方向 131 if (_kbhit())//如果用户按下了键盘中的某个键 132 { 133 fflush(stdin);//清空缓冲区的字符 134 135 //getch()读取方向键的时候,会返回两次,第一次调用返回0或者224,第二次调用返回的才是实际值 136 key = _getch();//第一次调用返回的不是实际值 137 key = _getch();//第二次调用返回实际值 138 } 139 140 /* 141 *蛇移动时候先擦去蛇尾的一节 142 *changeFlag为0表明此时没有吃到食物,因此每走一步就要擦除掉蛇尾,以此营造一个移动的效果 143 *为1表明吃到了食物,就不需要擦除蛇尾,以此营造一个蛇身增长的效果 144 */ 145 if (changeFlag == 0) 146 { 147 gotoxy(snake.x[snake.len - 1], snake.y[snake.len - 1]); 148 printf(" ");//在蛇尾处输出空格即擦去蛇尾 149 } 150 151 //将蛇的每一节依次向前移动一节(蛇头除外) 152 for (i = snake.len - 1; i > 0; i--) 153 { 154 snake.x[i] = snake.x[i - 1]; 155 snake.y[i] = snake.y[i - 1]; 156 } 157 158 //蛇当前移动的方向不能和前一次的方向相反,比如蛇往左走的时候不能直接按右键往右走 159 //如果当前移动方向和前一次方向相反的话,把当前移动的方向改为前一次的方向 160 if (pre_key == 72 && key == 80) 161 key = 72; 162 if (pre_key == 80 && key == 72) 163 key = 80; 164 if (pre_key == 75 && key == 77) 165 key = 75; 166 if (pre_key == 77 && key == 75) 167 key = 77; 168 169 /** 170 *控制台按键所代表的数字 171 *“↑”:72 172 *“↓”:80 173 *“←”:75 174 *“→”:77 175 */ 176 177 //判断蛇头应该往哪个方向移动 178 switch (key) 179 { 180 case 75: 181 snake.x[0] -= 2;//往左 182 break; 183 case 77: 184 snake.x[0] += 2;//往右 185 break; 186 case 72: 187 snake.y[0]--;//往上 188 break; 189 case 80: 190 snake.y[0]++;//往下 191 break; 192 } 193 //打印出蛇头 194 gotoxy(snake.x[0], snake.y[0]); 195 printf("■"); 196 gotoxy(MAPWIDTH - 2, 0); 197 //由于目前没有吃到食物,changFlag值为0 198 changeFlag = 0; 199 return; 200 } 201 202 void createFood() 203 { 204 if (snake.x[0] == food.x && snake.y[0] == food.y)//蛇头碰到食物 205 { 206 //蛇头碰到食物即为要吃掉这个食物了,因此需要再次生成一个食物 207 while (1) 208 { 209 int flag = 1; 210 srand((unsigned int)time(NULL)); 211 food.x = rand() % (MAPWIDTH - 4) + 2; 212 food.y = rand() % (MAPHEIGHT - 2) + 1; 213 214 } 215 216 //绘制食物 217 gotoxy(food.x, food.y); 218 printf("*"); 219 220 snake.len++;//吃到食物,蛇身长度加1 221 sorce += 10;//每个食物得10分 222 changeFlag = 1;//很重要,因为吃到了食物,就不用再擦除蛇尾的那一节,以此来造成蛇身体增长的效果 223 } 224 return; 225 } 226 227 bool snakeStatus() 228 { 229 //蛇头碰到上下边界,游戏结束 230 if (snake.y[0] == 0 || snake.y[0] == MAPHEIGHT) 231 return false; 232 //蛇头碰到左右边界,游戏结束 233 if (snake.x[0] == 0 || snake.x[0] == MAPWIDTH) 234 return false; 235 } 236 237 int main() 238 { 239 drawMap(); 240 while (1) 241 { 242 keyDown(); 243 if (!snakeStatus()) 244 break; 245 createFood(); 246 Sleep(snake.speed); 247 } 248 249 gotoxy(MAPWIDTH / 2, MAPHEIGHT / 2); 250 printf("Game Over!\n"); 251 gotoxy(MAPWIDTH / 2, MAPHEIGHT / 2 + 1); 252 printf("本次游戏得分为:%d\n", sorce); 253 Sleep(5000); 254 return 0; 255 }
(三)代码改进
- 缺少蛇头撞到蛇身导致游戏失败的代码

- 可以根据蛇身的长度来增加速度以提高难度

- 食物产生的位置没有作出限制会有bug

- 游戏截图

(四)心得体会
这次的改动虽然只是很小的改动,但即便如此,仍然花费了不少时间,还是在源代码有详细注释的情况下,可想而知要是没有这些注释,修改起来该有多费劲。
我以后也要养成写注释的好习惯,这不仅仅会给他人理解自己代码提供便利,更是为了自己。自己写的代码,过了一段时间也可能会看不懂。

浙公网安备 33010602011771号