第一次作业

(一)项目来源

       项目为贪吃蛇小游戏,来源于网上

(二)项目分析

  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

  • 游戏截图

 

(四)心得体会

       这次的改动虽然只是很小的改动,但即便如此,仍然花费了不少时间,还是在源代码有详细注释的情况下,可想而知要是没有这些注释,修改起来该有多费劲。

我以后也要养成写注释的好习惯,这不仅仅会给他人理解自己代码提供便利,更是为了自己。自己写的代码,过了一段时间也可能会看不懂。

posted @ 2022-02-28 16:29  Aimerry  阅读(45)  评论(0)    收藏  举报