设计内容及要求:
贪吃蛇游戏是一个深受人们喜欢的游戏,一条蛇在密闭的围墙内,在围墙内随机出现一个食物,通过按键盘上的四个光标键控制蛇的上下左右四个方向移动,蛇头撞到食物,则表示食物被蛇吃掉,这时蛇的身体长一节,同时记10分,接着又出现食物,等待被蛇吃掉,如果蛇在移动过程中,撞到墙壁或身体交叉蛇头撞到自己的身体则游戏结束。
设计要求:
1、游戏界面:如边框表示围墙,红色矩形框表示蛇,绿色小方块表示食物。
2、设计思路:蛇的图形和蛇的移动。
3、用2个结构体表示食物和蛇的矩形块。
设计流程:
课程设计任务实现:
优化:
1.对蛇碰撞自身的优化
经过分析蛇头与前四个节点不会相撞所以直接与第五个节点进行比较
2.对蛇的移动的优化
优化前需要释放动态空间和重新申请新的动态空间,还需要增加一个指针变量。
优化后只需要更改链表内的存储数据即可实现。
优化前代码:
struct Snake//声明蛇的一个节点 (双向链表)
{
int x;
int y;
struct Snake *last;
struct Snake *next;
} ;
struct Snake *head,*p1,*p2; //动态链表创建三变量
struct Snake lasthead; //指向头节点前加入的一个节点
//释放尾节点,给现任尾节点赋空指向
p2=p1->last;
free(p1);
p1=p2;
p1->next=NULL;
//增加头节点
lasthead=(struct Snake)malloc(sizeof(struct Snake));
head->last=lasthead;
lasthead->next=head;
head=lasthead;
优化后:
struct Snake//声明蛇的一个节点 (双向链表)
{
int x;
int y;
struct Snake *last;
struct Snake *next;
} ;
struct Snake *head,*p1,*p2; //动态链表创建三变量
//使原尾节点上一节指向为NULL
p1->last->next=NULL;
p2=p1->last;
//使原尾节点连接到原头节点前
p1->next=head;
head->last=p1;
head=p1;
源码:
#include
#include//调用暂停函数
#include//调用光标结构体
#include//调用time函数
#include//VC6.0下调用kbhit函数
struct Snake//声明蛇的一个节点 (双向链表)
{
int x;
int y;
struct Snake *last;
struct Snake *next;
} ;
struct Food//声明食物位置
{
int x;
int y;
} ;
int i;//for循环计数
int k;//for循环计数
int keycode;//蛇移动的方向
struct Snake *head,*p1,*p2; //标准动态链表三变量
struct Food food;//食物节点
int score;//记录成绩
char want='r';//用户意愿
int antikeycode;//当前方向的反方向的值
int judge;//当前键入值
int Pos(int x, int y)//光标位置函数
{
COORD Pos;
HANDLE hOutput;
Pos.X = x*2;
Pos.Y = y;
hOutput = GetStdHandle(STD_OUTPUT_HANDLE); // 获取标准输出设备句柄
SetConsoleCursorPosition(hOutput, Pos);//定位光标位置的函数,两个参数分别是指定哪个窗体,具体位置
return 0;
}
int color(short x) //自定义函数根据参数改变颜色
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x); //参数x改变字体颜色 2为绿色 4为红色 7为白色
return 0;
}
int Welcome()//欢迎界面
{
//输出边框及欢迎文字
for(i=10;i<33;i++)
{
Pos(i,10);//上边框
printf("█");
Pos(i,32);//下边框
printf("█");
Pos(10,i);//左边框
printf("█");
Pos(32,i);//右边框
printf("█");
}
Pos(17,20);
printf("欢迎进入贪吃蛇游戏");
Pos(17,22);
printf("请按任意键开始游戏");
//等待按键,开始游戏
while(!kbhit()) ;//kbhit函数检测键入值,即时返回,非阻塞函数,无键入返回0。
while(kbhit()==1)
getch();// 清空键盘输入缓冲区
return 0;
}
int Startgame()//开始游戏
{
//清空提示语
Pos(17,20);
printf(" ");
Pos(17,22);
printf(" ");
//输出提示语
score=0;
Pos(18,8);
printf("当前得分:%d",score);
//初始化5个节点的蛇(动态链表)
//赋地址值使链表连接
//struct Snake *head,*p1,*p2;
p1=(struct Snake*)malloc(sizeof(struct Snake));
head=p1;
for(i=1;i<5;i++)
{
p2=p1;
p1=(struct Snake*)malloc(sizeof(struct Snake));
p1->last=p2;
p2->next=p1;
}
p1->next=NULL;
//赋坐标并输出
p1=head;
p1->x=27;
p1->y=21;
while(!(p1->next==NULL))
{
//给下一个节点赋坐标
(p1->next)->x=p1->x+1;
(p1->next)->y=p1->y;
//输出值
Pos(p1->x,p1->y);
color(4);
printf("□");
p1=p1->next;//指向下一个节点的地址
}
//输出蛇尾节点
Pos(p1->x,p1->y);
printf("□");
//初始化食物位置
srand(time(NULL));
//struct Food food;//食物节点
food.x=rand()%32;
while(food.x<11)
food.x=rand()%32;
food.y=rand()%32;
while(food.y<11)
food.y=rand()%32;
Pos(food.x,food.y);
color(2);
printf("█");
//int keycode=75 左光标
//保持每次重开keycode=左光标;
keycode=75;
while(i) //设置一个变量 将变量值传入switch
{
//保证当食物刷出位置在蛇身时,不被蛇身经过时食物被意外清除
Pos(food.x,food.y);
color(2);
printf("█");
color(7);
//控制蛇的移动
//覆盖尾节点的输出值使其为空
Pos(p1->x,p1->y);
printf(" ");
//使原尾节点上一节指向为NULL
p1->last->next=NULL;
p2=p1->last;
//使原尾节点连接到原头节点前
p1->next=head;
head->last=p1;
head=p1;
//给蛇的头节点坐标赋值
switch(keycode)
{
case 72:
head->x=(head->next)->x;
head->y=(head->next)->y-1;
break;
case 80:
head->x=(head->next)->x;
head->y=(head->next)->y+1;
break;
case 75:
head->x=(head->next)->x-1;
head->y=(head->next)->y;
break;
case 77:
head->x=(head->next)->x+1;
head->y=(head->next)->y;
}
Pos(head->x,head->y);
color(4);
printf("□");
//规则判断
switch(1)
{
case 1:
//头撞边框游戏结束
if(head->x==10 || head->x==32)
{
i=0;
break;
}
if(head->y==10 || head->y==32)
{
i=0;
break;
}
//头撞身体游戏结束
//蛇在5个节点内不会撞到身体
for(i=1;i<5;i++)
p1=p1->next;
//判断是否会相撞
while(p1->next!=NULL)//判断到尾节点的前一个节点
{
if(head->x==p1->x && head->y==p1->y)
{
i=0;
break;
}
p1=p1->next;
}
/*
两个情况
1.当while语句里未执行break,则下一个if语句为补充判断蛇头与蛇尾是否相撞。
2.当while语句里执行了break, 则下一个if语句始终为真值实际用处为跳出while循环,游戏结束。
*/
if(head->x==p1->x && head->y==p1->y)
{
i=0;
}
}
//头吃食物 蛇尾加一节
if(head->x==food.x && head->y==food.y)
{
//计分并输出值
score=score+10;
Pos(23,8);
color(7);
printf("%d",score);
//增加尾节点
p1=(struct Snake*)malloc(sizeof(struct Snake));
p2->next=p1;
p1->last=p2;
switch(p2->last->x-p2->x)
{
case -1:
p1->x=p2->x+1;
break;
case 1:
p1->x=p2->x-1;
case 0: p1->x=p2->x;
}
switch(p2->last->y-p2->y)
{
case 1:
p1->y=p2->y-1;
break;
case -1:
p1->y=p2->y+1;
case 0: p1->y=p2->y;
}
//输出蛇尾
color(4);
Pos(p1->x,p1->y);
printf("□");
//当蛇新长的尾节点在墙里时将其释放
if(p1->x==10 ||p1->x==32)
{
Pos(p1->x,p1->y);
printf("█");
}
if(p1->y==10 ||p1->y==32)
{
Pos(p1->x,p1->y);
printf("█");
}
//创建新的食物
food.x=rand()%32;
while(food.x<11)
food.x=rand()%32;
food.y=rand()%32;
while(food.y<11)
food.y=rand()%32;
Pos(food.x,food.y);
color(2);
printf("█");
}
/*若传入新值 判断传入的值
若与原来的方向相反 则无效 即值不做改变 ************
*/
//int antikeycode
//给anticode赋值
switch(keycode)
{
case 72:
antikeycode=80;
break;
case 80:
antikeycode=72;
break;
case 75:
antikeycode=77;
break;
case 77:
antikeycode=75;
}
judge=500;
//int k;
for(k=1;k<20000;k++)
{
if(kbhit()==1)
{
judge=getch();
}
switch(judge)
{
case 224 :
judge=getch();
switch(judge)
{
case 72:
case 80:
case 75:
case 77:
if(judge!=antikeycode)
{
k=20000;
break;
}
}
default :
if(k!=20000)
judge=500;
}
}
if(judge!=500)
keycode=judge;
}
return 0;
}
int gameover()
{
//提示游戏结束输出最终成绩
color(7);
Pos(15,8);
printf("游戏结束,最终分数:%d",score);
Pos(14,9);
printf("按q退出游戏,按r重新开始游戏");
//游戏结束,释放所有节点
p1=head->next;
while(p1->next!=NULL)
{
free(p1->last);
p1=p1->next;
}
free(p1);
//按q退出,按r重新开始
while(1)
{
want=getch();
if(want=='q' || want=='r')
break;
}
switch(want)
{
case 'q':
break;
case 'r':
system("cls");
}
return 0;
}
int main()
{
while(want=='r')
{
Welcome();//欢迎界面
Startgame();//开始游戏
gameover();//游戏结束
}
system("pause");
return 0;
}