中國礦業大學信控學院
/*參考*/
https://blog.csdn.net/Fdog_/article/details/102625969
https://blog.csdn.net/DY_1024/article/details/78841757
一、問題描述
以資料結構思想設計實作貪吃蛇小游戲,
二、需求分析
首先需要考慮如何設計一個win運行視窗來實時顯示結果
然后考慮到蛇的身子是一節一節的,此時最容易聯想到的資料結構就是順序表,鏈表,如果把蛇比做順序表或者鏈表,在之后吃到食物的時候,身子肯定會變長,這就涉及到插入的操作,所以為了更高的效率,我們用鏈表實作我們的蛇的部分,最初我們把蛇身子按照四個結點列印在螢屏,
對于蛇的移動,在螢屏上面蛇的移動看起來是整個身子向前方平移一個單位,但是其原理是我們在螢屏的另一個地方把蛇從新列印一遍,又把之前的蛇身子去除掉,
對于食物的產生,隨機的在地圖中產生一個節點,在蛇的頭坐標和食物的坐標重復的時候,食物消失,蛇的身子加長,也就是蛇的節點數增加一個,
蛇在其中的幾種狀態,正常狀態:蛇頭節點的坐標沒有和墻的坐標以及自己身子的坐標重合,
被自己殺死:蛇頭的坐標和蛇身子的坐標重合,
撞墻:蛇頭的坐標和墻的坐標重合,
三、演算法設計
1.相關變數,
1 1.相關變數,
2 int JudgeSum = 0; //判斷是否加快
3 int Pause = 200000000; //暫停速度(移動速度)
4 int * PJ = &JudgeDirection; //用指標傳值判斷移動方向
5 nakebody *end = NULL; //尾節點
2.創建鏈表
貪吃蛇的身體如何保存是游戲的核心,所以我們需要用到鏈表來保存蛇的身體,這樣就可以隨時知道蛇身資料,
1 typedef struct Snakebody
2 {
3 int x, y; //蛇身的坐標
4 struct Snakebody *next;//保存下一個蛇身的地址
5 }Snakebody; //通過typedef將 Snakebody 替代 struct Snakebody
3.記錄食物出現的坐標,
1 typedef struct Snakexy
2 {
3 int x;
4 int y;
5 }Snakexy; //記錄食物坐標
4.繪制初始界面和游戲地圖,
1 #include<Windows.h>
2 #define HEIGHT 20 //設定地圖高度
3 #define WIDTH 40 //設定地圖寬度
4 #define PRINTF printf("■");
5 #define LINE printf("\n");
6 #define EMPTY printf(" "); //因為這三個陳述句經常用,所以我就定義成了宏
7 void Front(); //繪制初始界面
8 void DeawMap(); //繪制地圖
9
10 void Front()
11 {
12 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);//設定紅色和藍色相加
13 MoveCursor(18, 15);
14 printf("請等待......");
15 for (int i = 0; i <= 3000000000; i++) {}
16 system("cls");//清屏處理
17 }
18 void DeawMap()
19 {
20 for (int i = 0; i < WIDTH; i++)PRINTF LINE //列印上邊框
21 for (int i = 1; i < HEIGHT - 1; i++) //列印左右邊框
22 {
23 for (int j = 0; j < WIDTH; j++)
24 {
25 if (j == 0 || j == WIDTH - 1 || j == WIDTH - 10)
26 {
27 PRINTF
28 if (j == WIDTH - 1)LINE
29 }
30 else EMPTY
31 }
32 }
33 for (int i = 0; i < WIDTH; i++)PRINTF LINE //列印下邊框
34 }
SetConsoleTextAttribute()函式是一個API設定字體顏色和背景色的函式,引數表中使用兩個屬性(屬性之間用,隔開),不同于system(),SetConsoleTextAttribute()可以改變界面多種顏色,而system()只能修改為一種!,
5. 初始化蛇身,剛開始蛇不應該只要一個頭,所以我們必須創建幾個身體,
1 Snakebody *Phead = NULL; //存盤著整個蛇身 不可更改
2 Snakebody *Phead_1 = NULL; //指向蛇身
3 Snakebody *Pbady = NULL; //創建節點
4 void ISnake(); //初始化蛇身
5 void ISnake()
6 {
7 for (int i = 0; i < 5; i++)//初始化蛇身擁有五個長度
8 {
9 Pbady = (Snakebody*)malloc(sizeof(Snakebody));//創建節點
10 Pbady->x = 5 - i;
11 Pbady->y = 5;
12 if (Phead == NULL)
13 {
14 Phead = Pbady;
15 }
16 else
17 {
18 end->next = Pbady;
19 }
20 Pbady->next = NULL;
21 end = Pbady;
22 }
23 Phead_1 = Phead;
24 while (Phead_1->next != NULL)//列印蛇身
25 {
26 MoveCursor(Phead_1->x, Phead_1->y);
27 PRINTF
28 Phead_1 = Phead_1->next;
29 }
30 }
6.產生食物,隨機產生食物,如果和蛇身體重合則再次隨機產生食物,
1 #include<time.h>
2 int sum = 0; //計算得分
3 Snakexy * Food = NULL; //保存食物位置
4 void FoodRand(); //生成食物
5 void FoodRand()
6 {
7 srand((int)time(0));
8 int x = rand() % 27 + 2;//生成亂數
9 int y = rand() % 17 + 2;
10 Phead_1 = Phead;
11 for (int i = 0; i <= 200; i++)
12 {
13 if (Phead_1->x == x && Phead_1->y == y)
14 {
15 x = rand() % 27 + 2;
16 y = rand() % 17 + 2;
17 }
18 else
19 {
20 Phead_1 = Phead_1->next;
21 }
22 if (Phead_1->next == NULL)
23 {
24 break;
25 }
26 }
27 MoveCursor(x, y);
28 PRINTF
29 Food = (Snakexy*)malloc(sizeof(Snakexy));
30 Food->x = x;
31 Food->y = y;
32 MoveCursor(33, 5);
33 printf(" ");
34 Showf();
35 sum++;
36 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);// 藍
37 }
rand函式功能為獲取一個偽亂數,如要產生[m,n]范圍內的亂數num,可用int num=rand()%(n-m+1)+m;
7.游戲重繪和暫停 ,按回車可暫停游戲,
1 int JudgeDirection = 4; //判斷方向
2 void ControlMove(); //控制移動和暫停
3 void ControlMove()
4 {
5 if (GetAsyncKeyState(VK_UP) && 0x8000)
6 {
7 if (JudgeDirection == 2)
8 {
9 }
10 else
11 {
12 JudgeDirection = 1;
13 }
14 }
15 if (GetAsyncKeyState(VK_DOWN) && 0x8000)
16 {
17 if (JudgeDirection == 1)
18 {
19 }
20 else
21 {
22 JudgeDirection = 2;
23 }
24 }
25 if (GetAsyncKeyState(VK_RIGHT) && 0x8000)
26 {
27 if (JudgeDirection == 3)
28 {
29 }
30 else
31 {
32 JudgeDirection = 4;
33 }
34 }
35 if (GetAsyncKeyState(VK_LEFT) && 0x8000)
36 {
37 if (JudgeDirection == 4)
38 {
39 }
40 else
41 {
42 JudgeDirection = 3;
43 }
44 }
45 if (GetAsyncKeyState(VK_RETURN) && 0x0D)//判斷回車
46 {
47 while (1)
48 {
49 if (GetAsyncKeyState(VK_RETURN) && 0x0D)//再次回車退出死回圈
50 {
51 break;
52 }
53 }
54 }
55 }
GetAsyncKeyState()確定用戶當前是否按下了鍵盤上的一個鍵
8.顯示分數和難度,更新分數和難度,
1 int sum = 0; //計算得分
2 int Hard = 0; //計算難度
3 void Showf(); //顯分數以及難度
4 void Showf()
5 {
6 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE);// 藍
7 MoveCursor(33, 5);
8 printf("得分:%d", sum);
9 MoveCursor(33, 6);
10 printf("難度:%d", Hard);
11 }
9.移動游標 ,游戲不閃的原因就是我們只繪制一次地圖 然后用游標定點重繪目標點,
1 void MoveCursor(int x, int y); //移動游標
2 void MoveCursor(int x, int y)//設定游標位置(就是輸出顯示的開始位置)
3 {
4 COORD pos = { x * 2,y };
5 HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE);//獲得標準輸出的句柄
6 SetConsoleCursorPosition(output, pos); //設定游標位置
7 }
COORD是Windows API中定義的一種結構體
10.檢測,檢測是否吃到食物,是否撞墻,是否撞到自己,
1 void Jfood(); //檢測是否吃到食物
2 void Jwall(); //檢測蛇頭是否撞墻
3 void Jsnake(); //檢測蛇頭是否撞到蛇身
4 void Jfood()
5 {
6 Phead_1 = Phead;
7 if (Phead_1->x == Food->x&&Phead_1->y == Food->y)
8 {
9 FoodRand();
10 JudgeSum += 1;
11 if (JudgeSum == 5)
12 {
13 JudgeSum = 0;//如果JudgeSum等于5則從新判斷
14 Hard += 1;
15 Pause -= 20000000;//每成立一次回圈減少20000000
16 }
17 while (Phead_1->next != NULL)
18 {
19 Phead_1 = Phead_1->next;
20 }
21 Snakebody *S = (Snakebody*)malloc(sizeof(Snakebody));
22 S->x = Food->x;
23 S->y = Food->y;
24 S->next = NULL;
25 Phead_1->next = S;
26 ControlMove();
27 MoveCursor(Phead_1->x, Phead_1->y);
28 PRINTF
29 }
30 //獲取食物的坐標和蛇頭做對比
31 }
32 void Jwall()
33 {
34 if (Phead->x == 0 || Phead->x == 29 || Phead->y == 0 || Phead->y == 19)
35 {
36 MoveCursor(10, 20);
37 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//設定紅色
38 printf("抱歉,你撞到了自己,游戲結束! ");
39 system("pause>nul");
40 exit(0);
41 }
42 }
43 void Jsnake()
44 {
45 Phead_1 = Phead->next;
46 while (Phead_1->next != NULL)
47 {
48 if ((Phead->x == Phead_1->x) && (Phead->y == Phead_1->y))
49 {
50 MoveCursor(10, 20);
51 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//設定紅色
52 printf("抱歉,你撞到了自己,游戲結束! ");
53 system("pause>nul");
54 exit(0);
55 }
56 Phead_1 = Phead_1->next;
57 }
58 }
11.游戲回圈
1 void Move(); //游戲運行
2 void Move()
3 {
4 while (1)
5 {
6 Phead_1 = Phead;
7 while (Phead_1->next->next != NULL)
8 {
9 Phead_1 = Phead_1->next;
10 }
11 Phead_1->next = NULL;
12 for (int i = 0; i < Pause; i++) {}
13 ControlMove();
14 MoveCursor(Phead_1->x, Phead_1->y);
15 EMPTY
16 //上面為消除尾部
17 Snakebody *Phead_2 = (Snakebody*)malloc(sizeof(Snakebody));
18 if (*PJ == 1)
19 {
20 Phead_2->x = Phead->x;
21 Phead_2->y = Phead->y - 1;
22 }
23 if (*PJ == 2)
24 {
25 Phead_2->x = Phead->x;
26 Phead_2->y = Phead->y + 1;
27 }
28 if (*PJ == 3)
29 {
30 Phead_2->x = Phead->x - 1;
31 Phead_2->y = Phead->y;
32 }
33 if (*PJ == 4)
34 {
35 Phead_2->x = Phead->x + 1;
36 Phead_2->y = Phead->y;
37 }
38 Phead_2->next = Phead;
39 Phead = Phead_2;
40 MoveCursor(Phead_2->x, Phead_2->y);
41 PRINTF
42 Jfood();
43 Jwall();
44 Jsnake();
45 MoveCursor(40, 20);
46 }
47 }
12.釋放記憶體
1 void Free(); //釋放記憶體
2 void Free()
3 {
4 while (Phead->next != NULL)
5 {
6 Phead = Phead->next;
7 free(Phead);
8 }
9 free(Phead);
10 }
附錄:完整代碼
1 #include<stdio.h>
2 #include<time.h>
3 #include<Windows.h>
4 #define HEIGHT 20 //設定地圖高度
5 #define WIDTH 40 //設定地圖寬度
6 #define PRINTF printf("■");
7 #define LINE printf("\n");
8 #define EMPTY printf(" ");
9 typedef struct Snakebody
10 {
11 int x, y;//身體的坐標
12 struct Snakebody *next;//結構指標
13 }Snakebody;//先來創建保持身體的鏈表,貪吃蛇的核心代碼就是該如何保存蛇的身體
14 typedef struct Snakexy
15 {
16 int x;
17 int y;
18 }Snakexy; //記錄食物坐標
19 int sum = 0; //計算得分
20 int JudgeSum = 0; //判斷是否加快
21 int Hard = 0; //計算難度
22 int Pause = 200000000; //暫停速度(移動速度)
23 int JudgeDirection = 4; //判斷方向
24 int * PJ = &JudgeDirection; //用指標傳值判斷移動方向
25 Snakebody *Phead = NULL; //存盤著整個蛇身 不可更改
26 Snakebody *Phead_1 = NULL; //指向蛇身
27 Snakebody *Pbady = NULL; //創建節點
28 Snakebody *end = NULL; //尾節點
29 Snakexy * Food = NULL; //保存食物位置
30 void Front(); //游戲開始頁面1
31 void Jfood(); //檢測是否吃到食物1
32 void Jwall(); //檢測蛇頭是否撞墻1
33 void Jsnake(); //檢測蛇頭是否撞到蛇身1
34 void ISnake(); //初始化蛇身1
35 void DeawMap(); //繪制地圖1
36 void FoodRand(); //生成食物1
37 void ControlMove(); //控制移動和暫停1
38 void MoveCursor(int x, int y); //移動游標1
39 void Move(); //游戲運行1
40 void Showf(); //顯分數以及難度1
41 void Free(); //釋放記憶體
42 int main()
43 {
44 Front();
45 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN);//綠
46 DeawMap();
47 Showf();
48 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);// 暗白
49 MoveCursor(34, 10);
50 printf("↑");
51 MoveCursor(31, 11);
52 printf("使用←↓→來控制");
53 MoveCursor(31, 12);
54 printf("蛇的移動,撞墻游");
55 MoveCursor(31, 13);
56 printf("戲結束,每5分增 ");
57 MoveCursor(31, 14);
58 printf("一個難度(速度)");
59 ISnake();
60 FoodRand();
61 MoveCursor(40, 20);
62 Move();
63 return 0;
64 }
65 void Front()
66 {
67 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);//設定紅色和藍色相加
68 MoveCursor(18, 15);
69 printf("請等待......");
70 for (int i = 0; i <= 3000000000; i++) {}
71 system("cls");
72 }
73 void DeawMap()
74 {
75 for (int i = 0; i < WIDTH; i++)PRINTF LINE //上邊框
76 for (int i = 1; i < HEIGHT - 1; i++) //列印左右邊框
77 {
78 for (int j = 0; j < WIDTH; j++)
79 {
80 if (j == 0 || j == WIDTH - 1 || j == WIDTH - 10)
81 {
82 PRINTF
83 if (j == WIDTH - 1)LINE
84 }
85 else EMPTY
86 }
87 }
88 for (int i = 0; i < WIDTH; i++)PRINTF LINE //下邊框
89 }
90 void MoveCursor(int x, int y)//設定游標位置(就是輸出顯示的開始位置)
91 {
92 /* COORD是Windows API中定義的一種結構體
93 * typedef struct _COORD
94 * {
95 * SHORT X;
96 * SHORT Y;
97 * } COORD;
98 * */
99 COORD pos = { x * 2,y };
100 HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE);//獲得 標準輸出的句柄
101 SetConsoleCursorPosition(output, pos); //設定控制臺游標位置
102 }
103 void FoodRand()
104 {
105 srand((int)time(0));
106 int x = rand() % 27 + 2;
107 int y = rand() % 17 + 2;
108 Phead_1 = Phead;
109 for (int i = 0; i <= 200; i++)
110 {
111 if (Phead_1->x == x && Phead_1->y == y)
112 {
113 x = rand() % 27 + 2;
114 y = rand() % 17 + 2;
115 }
116 else
117 {
118 Phead_1 = Phead_1->next;
119 }
120 if (Phead_1->next == NULL)
121 {
122 break;
123 }
124 }
125 MoveCursor(x, y);
126 PRINTF
127 Food = (Snakexy*)malloc(sizeof(Snakexy));
128 Food->x = x;
129 Food->y = y;
130 MoveCursor(33, 5);
131 printf(" ");
132 Showf();
133 sum++;
134 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);// 藍
135 }
136 void ControlMove()
137 {
138 if (GetAsyncKeyState(VK_UP) && 0x8000)
139 {
140 if (JudgeDirection == 2)
141 {
142 }
143 else
144 {
145 JudgeDirection = 1;
146 }
147 }
148 if (GetAsyncKeyState(VK_DOWN) && 0x8000)
149 {
150 if (JudgeDirection == 1)
151 {
152 }
153 else
154 {
155 JudgeDirection = 2;
156 }
157 }
158 if (GetAsyncKeyState(VK_RIGHT) && 0x8000)
159 {
160 if (JudgeDirection == 3)
161 {
162 }
163 else
164 {
165 JudgeDirection = 4;
166 }
167 }
168 if (GetAsyncKeyState(VK_LEFT) && 0x8000)
169 {
170 if (JudgeDirection == 4)
171 {
172 }
173 else
174 {
175 JudgeDirection = 3;
176 }
177 }
178 if (GetAsyncKeyState(VK_RETURN) && 0x0D)
179 {
180 while (1)
181 {
182 if (GetAsyncKeyState(VK_RETURN) && 0x0D)
183 {
184 break;
185 }
186 }
187 }
188 }
189 void ISnake()
190 {
191 for (int i = 0; i < 5; i++)
192 {
193 Pbady = (Snakebody*)malloc(sizeof(Snakebody));
194 Pbady->x = 5 - i;
195 Pbady->y = 5;
196 if (Phead == NULL)
197 {
198 Phead = Pbady;
199 }
200 else
201 {
202 end->next = Pbady;
203 }
204 Pbady->next = NULL;
205 end = Pbady;
206 }
207 Phead_1 = Phead;
208 while (Phead_1->next != NULL)
209 {
210 MoveCursor(Phead_1->x, Phead_1->y);
211 PRINTF
212 Phead_1 = Phead_1->next;
213 }
214 }
215 void Move()
216 {
217 while (1)
218 {
219 Phead_1 = Phead;
220 while (Phead_1->next->next != NULL)
221 {
222 Phead_1 = Phead_1->next;
223 }
224 Phead_1->next = NULL;
225 for (int i = 0; i < Pause; i++) {}
226 ControlMove();
227 MoveCursor(Phead_1->x, Phead_1->y);
228 EMPTY
229 //上面為消除尾部
230 Snakebody *Phead_2 = (Snakebody*)malloc(sizeof(Snakebody));
231 if (*PJ == 1)
232 {
233 Phead_2->x = Phead->x;
234 Phead_2->y = Phead->y - 1;
235 }
236 if (*PJ == 2)
237 {
238 Phead_2->x = Phead->x;
239 Phead_2->y = Phead->y + 1;
240 }
241 if (*PJ == 3)
242 {
243 Phead_2->x = Phead->x - 1;
244 Phead_2->y = Phead->y;
245 }
246 if (*PJ == 4)
247 {
248 Phead_2->x = Phead->x + 1;
249 Phead_2->y = Phead->y;
250 }
251 Phead_2->next = Phead;
252 Phead = Phead_2;
253 MoveCursor(Phead_2->x, Phead_2->y);
254 PRINTF
255 Jfood();
256 Jwall();
257 Jsnake();
258 MoveCursor(40, 20);
259 }
260 }
261 void Jfood()
262 {
263 Phead_1 = Phead;
264 if (Phead_1->x == Food->x&&Phead_1->y == Food->y)
265 {
266 FoodRand();
267 JudgeSum += 1;
268 if (JudgeSum == 5)
269 {
270 JudgeSum = 0;
271 Hard += 1;
272 Pause -= 20000000;
273 }
274 while (Phead_1->next != NULL)
275 {
276 Phead_1 = Phead_1->next;
277 }
278 Snakebody *S = (Snakebody*)malloc(sizeof(Snakebody));
279 S->x = Food->x;
280 S->y = Food->y;
281 S->next = NULL;
282 Phead_1->next = S;
283 ControlMove();
284 MoveCursor(Phead_1->x, Phead_1->y);
285 PRINTF
286 }
287 //獲取食物的坐標和蛇頭做對比
288 }
289 void Jwall()
290 {
291 if (Phead->x == 0 || Phead->x == 29 || Phead->y == 0 || Phead->y == 19)
292 {
293 MoveCursor(10, 20);
294 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//設定紅色
295 printf("抱歉,你撞到了自己,游戲結束! ");
296 system("pause>nul");
297 exit(0);
298 }
299 }
300 void Jsnake()
301 {
302 Phead_1 = Phead->next;
303 while (Phead_1->next != NULL)
304 {
305 if ((Phead->x == Phead_1->x) && (Phead->y == Phead_1->y))
306 {
307 MoveCursor(10, 20);
308 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//設定紅色
309 printf("抱歉,你撞到了自己,游戲結束! ");
310 system("pause>nul");
311 exit(0);
312 }
313 Phead_1 = Phead_1->next;
314 }
315 }
316 void Showf()
317 {
318 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE);// 藍
319 MoveCursor(33, 5);
320 printf("得分:%d", sum);
321 MoveCursor(33, 6);
322 printf("難度:%d", Hard);
323 }
324 void Free()
325 {
326 while (Phead->next != NULL)
327 {
328 Phead = Phead->next;
329 free(Phead);
330 }
331 free(Phead);
332 }
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/52975.html
標籤:C
下一篇:重新認識C語言的指標(上)
