-
Notifications
You must be signed in to change notification settings - Fork 0
/
SnakeGame-V8.0.c
271 lines (250 loc) · 7.2 KB
/
SnakeGame-V8.0.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
#include<stdio.h>
#include<conio.h>
#include<windows.h> //光标定位函数的头文件
#include<time.h> // 日期和时间头文件
const int SNAKE_LENGTH = 3;//设置一个常量
//定义全局变量
char Dire=77;//方向
int WaitTime=1000;//等待时间
void Hide_Cursor(HANDLE hConsole) // 隐藏光标 固定函数
{
CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
SetConsoleCursorInfo(hConsole, &cursor_info);
}
void Goto(int x, int y)//定义光标函数 输入要显示得到x坐标,y坐标,坐标原点为程序窗口左上角,向下为y轴,向右为x轴
{
short a=(short)x*2; //*2保证xy是1:1的转换,把x的扩大二倍,因为用的打印的方块都是占一个字符
COORD pos = {a, (short)y }; // 定义一个字符在控制台屏幕上的坐标POS
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); // 获取标准输出的句柄
SetConsoleCursorPosition(hConsole, pos); // 定位光标位置的函数<windows.h>
Hide_Cursor(hConsole);
}
void SetColor(int color) // 设置颜色,打印的颜色,前景,就近原则
{
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hConsole, color); // 是API设置字体颜色和背景色的函数
// 格式:SetConsoleTextAttribute(句柄,颜色);
}
void PBox(int x,int y)//在x,y处打印方块
{
Goto(x,y);
printf("■");
}
void PInterface()//打印界面
{
int i=0;
SetColor(15);//设置颜色
for(;i<50;i++)
{
if(i<=39)
{
PBox(0,i);//打印竖线
PBox(39,i);
PBox(49,i);
PBox(i,0);//打印横线
PBox(i,39);
}
else
{
PBox(i,0);//打印竖线
PBox(i,39);
}
}
Goto(42,4);//打印信息
printf("贪吃蛇游戏");
Goto(42,33);
printf("上下左右");
Goto(42,35);
printf("控制移动");
}
void Init()//初始化界面
{
system("title 贪吃蛇");//设置窗口的标题
system("mode con: cols=100 lines=40");//调整控制台的宽高各//可以把游戏窗口看做是40*40的网格,坐标从0-39
PInterface();
}
void InitSnake(int (*Snake)[2],int SnakeLength)//初始化蛇身
{
int i;
for(i=SnakeLength-1;i>=0;i--)//遍历蛇身
{
*(Snake[i]+0) = 12-i;//设置初始值
*(Snake[i]+1) = 4;
//设置颜色
SetColor(10);
PBox(*(Snake[i]+0),*(Snake[i]+1));//打印身体
}
}
void PCircle(int x,int y)//在x,y处打印圆
{
Goto(x,y);
printf("●");
}
//产生食物
void CreateFood(int *Food,int Snake[400][2],int SnakeLength)
{
int Cfood[2];
srand((unsigned)time(NULL)); //设置随机数种子
for(;;)
{
int flag=1;
int i;
Cfood[0] = rand()%38+1;//生成位置
Cfood[1] = rand()%38+1;
for(i=0;i<SnakeLength;i++)//判断获取的位置是否在蛇身体上
{
if(Cfood[0]==Snake[i][0]&&Cfood[1]==Snake[i][1])//如果条件成立
{flag=0;break;}//跳出循环从新计算
}
if(flag==1)//生成的食物是正确的
{
SetColor(12);
PCircle(Cfood[0],Cfood[1]);//打印食物
*Food = Cfood[0];//为食物赋值
*(Food+1) = Cfood[1];
break;
}
}
}
void Clear(int x,int y)//清除x,y处的字符
{
Goto(x,y);
putchar('\0');
}
void AddSpeed(int SnakeLength)//根据蛇身长度增加速度
{
if(WaitTime==0)//当WaitTime被改变时,进行还原
{
int NewTime;
NewTime=500-(SnakeLength-SNAKE_LENGTH)*10;//获取新的等待时间
if(NewTime>=50)//如果新的等待时间>=50
{
WaitTime=NewTime;//将新的等待时间赋值给WaitTime
}
else//新的等待时间<=50
{
WaitTime=50;
}
}
}
void NextStape(int *Sx,int *Sy)//根据方向确定下一步的x,y
{
switch (Dire)
{
case 72: (*Sy)--;break; //上
case 80: (*Sy)++;break; //下
case 75: (*Sx)--;break; //左
case 77: (*Sx)++;break; //右
}
}
PScore(int SnakeLength)//打印分数
{
int score = (SnakeLength-SNAKE_LENGTH)*10;//当前蛇身长度减去初始值再*10
Goto(43,15);
printf("得分");
Goto(43,17);
printf("%d",score);
}
void GameOver(int (*Snake)[2],int *SnakeLength,int *Food)
{
Goto(10,20);
printf("游戏结束!游戏将在三秒后重新开始!");
Sleep(3000);//暂停三秒
system("cls");//清空屏幕
*SnakeLength = SNAKE_LENGTH; //重置蛇身长度
Init(); //初始化界面
InitSnake(Snake,*SnakeLength);//初始化蛇身
CreateFood(Food,Snake,*SnakeLength);//产生果实
Dire = 77;//重置方向
PScore(*SnakeLength);//打印分数
}
int Judge(int Sx,int Sy,int (*Snake)[2],int *SnakeLength,int *Food)
{
int i;
if(Sx<=0||Sx>=39||Sy>=39||Sy<=0)//判断是否撞墙
{
GameOver(Snake,SnakeLength,Food);//结束游戏
return 0;
}
for(i=3;i<*SnakeLength;i++)//遍历蛇身
{
if(Sx==Snake[i][0]&&Sy==Snake[i][1])//判断是否撞到自己
{
GameOver(Snake,SnakeLength,Food);//结束游戏
return 0;//表示游戏结束
}
}
if(Sx==*Food&&Sy==*(Food+1))//判断是否是食物
{
CreateFood(Food,Snake,*SnakeLength);//创造食物
(*SnakeLength)++;//增加长度
PScore(*SnakeLength);//更新分数
}
}
void MoveSnake(int Sx,int Sy,int (*Snake)[2],int SnakeLength) //移动蛇到指定的点
{
int j;
Clear(*(Snake[SnakeLength-1]+0),*(Snake[SnakeLength-1]+1));//清除蛇尾
for(j=SnakeLength;j>=2;j--)//遍历蛇身
{
*(Snake[j-1]+0) = *(Snake[j-2]+0); //把蛇身坐标依次从后往前传递
*(Snake[j-1]+1) = *(Snake[j-2]+1);
}
*(Snake[0]+0)=Sx;//给蛇头赋值
*(Snake[0]+1)=Sy;
SetColor(10);
PBox(*(Snake[0]+0),*(Snake[0]+1)); //打印蛇头
}
//线程监听键盘输入
DWORD WINAPI ListenKey( LPVOID lpParam ) //检测键盘的敲击
{
for(;;)
{
char flag = getch();//获取键盘信息
switch(flag)
{
case 72: if(Dire!=80)//判读输入的值,是否与当前值方向相反
{Dire=flag;WaitTime=0;break;}//如果不相反,改变方向,重置等待时间,跳出switch
else
{break;}
case 80: if(Dire!=72)
{Dire=flag;WaitTime=0;break;}
else
{break;}
case 75: if(Dire!=77)
{Dire=flag;WaitTime=0;break;}
else
{break;}
case 77: if(Dire!=75)
{Dire=flag;WaitTime=0;break;}
else
{break;}
}
}
return 0;
}
int main(void){
int Snake[400][2];
int SnakeLength = SNAKE_LENGTH;//初始化蛇身长度
int i;
int Food[2];
clock_t NowTime; //声明时间变量
CreateThread( NULL, 0,ListenKey, 0, 0, 0 ); //使用CreateThread方法开启线程,让开启的线程执行ListenKey方法
Init(); //初始化界面
InitSnake(Snake,SnakeLength);//Snake是按地址调用,初始化蛇身
CreateFood(Food,Snake,SnakeLength);//产生果实
PScore(SnakeLength);//打印分数
for(i=0;;i++) //主循环
{
int Sx = Snake[0][0];//取出蛇头的坐标
int Sy = Snake[0][1];
AddSpeed(SnakeLength);//根据蛇身体长度改变速度
NextStape(&Sx,&Sy);//根据方向,计算下步蛇头位置
if(Judge(Sx,Sy,Snake,&SnakeLength,Food)!=0)//判断下步的情况
{MoveSnake(Sx,Sy,Snake,SnakeLength);}//根据判断的情况,移动蛇身体,如果不是死亡就移动蛇身
NowTime=clock();//获取当前时间
for(;clock()-NowTime<WaitTime;){} //计时器,用当前时间减去上面获取的时间与等待时间比较
}
getch();
return 0;
}