#include
#include
#include //sprintf将整数转换为字符串
#include //srand产生随机数
#define SQU_LONG 15 //正方形的长
#define SQU_NUMBER 30 //正方形个数
#pragma comment(lib,"Winmm.lib") //导入音乐头文件
IMAGE img,right,stop,play,fail,change; //定义背景,右边,游戏暂停,正在游戏,游戏失败,分数改变图片
int NUM=3; //节点个数
char score[5]; //存储分数的字符串
int foodx,foody; //食物的坐标,用于批绘制
void food(); //产生食物
void snakeMove(); //蛇移动
int Direction(int savec); //控制方向
void eat(); //吃食物
void over(); //蛇死亡
struct snake
{
int x; //蛇头x坐标
int y; //蛇头y坐标
int dire; //蛇头方向
struct snake *next; //结构体指针
};
struct snake *head=(snake *)malloc(sizeof(snake)); //定义头指针
struct snake *p; //用于遍历结构体
struct snake saveLast,saveBody; //saveLast用于保存前一个节点,saveBody用于保存后一个节点
void main()//主方法
{
initgraph(SQU_LONG*SQU_NUMBER+200,SQU_LONG*SQU_NUMBER); //创建画布
loadimage(&img,"image/bk_picture.jpg"); //读取背景图片
putimage(0,0,&img); //在坐标(0,0)处放置背景图片
loadimage(&right,"image/right.jpg");
putimage(495,0,&right);
settextcolor(WHITE); //文字颜色为白色
setbkmode(TRANSPARENT); //文字背景为透明
settextstyle(20,0,"楷体"); //文字字号为20,正常宽度,字体为楷体
outtextxy(530,40,"游戏规则:"); //在坐标(530,70)处输出“游戏规则”四个字,只能输出字符串格式
outtextxy(502,70,"方向键控制方向");
outtextxy(502,90," Space键暂停");
outtextxy(502,110," Shift键加速");
loadimage(&play,"image/play.jpg");
putimage(495,290,&play);
mciSendString("open music/bk_audio.mp3 alias bk_audio",NULL,0,NULL); //读取背景音乐文件并命名为bk_audio
mciSendString("play bk_audio repeat",NULL,0,NULL); //repeat为循环播放
int x,y; //蛇头坐标
srand((unsigned)time(NULL)); //产生随机数的语句,与rand()连用
do
{
x=rand()%SQU_LONG*SQU_NUMBER; //先算求余,后算乘法,随机产生蛇头的坐标
y=rand()%SQU_LONG*SQU_NUMBER;
}while(x>SQU_LONG*(SQU_NUMBER-3)||y>SQU_LONG*(SQU_NUMBER-3)||x<3*SQU_LONG||y<3*SQU_LONG); //不能出现在四周靠边的位置
struct snake *a=(snake *)malloc(sizeof(snake)); //用于存储蛇头
struct snake *b=(snake *)malloc(sizeof(snake)); //一开始出现的第一节身子
struct snake *c=(snake *)malloc(sizeof(snake)); //一开始出现的第二节身子
p=head; //p指向head
p->next=a;
a->x=x;
a->y=y;
a->dire=rand()%4+1; //a为蛇头,随机产生方向
a->next=b;
b->next=c;
c->next=NULL; //一开始只有一个蛇头两个身子,最后一个身子无指向
p=p->next; //p指向蛇头
setfillcolor(RED); //设置填充颜色红色,表示蛇头
bar(p->x,p->y,p->x+SQU_LONG-1,p->y+SQU_LONG-1); //在(p->x,p->y)处画一个长度为SQU_LONG的填充红色的正方形
saveLast.x=p->x; //将移动前蛇头信息保存为上一个节点的信息
saveLast.y=p->y;
saveLast.dire=p->dire;
saveLast.next=p->next;
if(p->dire==1)
p->x-=SQU_LONG; //向左,p->x-1
else if(p->dire==2)
p->y-=SQU_LONG; //向上,p->y-1
else if(p->dire==3)
p->x+=SQU_LONG; //向右,p->x+1
else if(p->dire==4)
p->y+=SQU_LONG; //向下,p->y+1
setfillcolor(RED); //移动后重新画蛇头
bar(p->x,p->y,p->x+SQU_LONG-1,p->y+SQU_LONG-1); //在移动后坐标处画一个长度为SQU_LONG的填充红色的正方形
p=p->next; //p指向第一个身子
while(p) //p不为空就进入循环,一开始设置链表有三个节点,一共可进入两次
{
saveBody.x=p->x; //将移动后的信息保存为现在节点的信息
saveBody.y=p->y;
saveBody.dire=p->dire;
p->x=saveLast.x; //重新将保存的移动前的信息读取出,赋给当前p指向的身子
p->y=saveLast.y;
p->dire=saveLast.dire;
saveLast.x=saveBody.x; //保存的移动前的失去作用,重新将移动后的再作为上一个节点,保证蛇每个身子记录前一个身子的信息
saveLast.y=saveBody.y;
saveLast.dire=saveBody.dire;
setfillcolor(YELLOW); //设置填充颜色黄色,表示蛇身子
bar(p->x,p->y,p->x+SQU_LONG-1,p->y+SQU_LONG-1); //在读取到信息的坐标处画一个长度为SQU_LONG的填充黄色的正方形
p=p->next; //p指向下一个身子
}
food(); //调用生成食物方法
snakeMove(); //调用蛇移动方法
}
void food()//生成食物
{
int x,y; //食物坐标
do
{
x=rand()%SQU_LONG*SQU_NUMBER+SQU_LONG;
y=rand()%SQU_LONG*SQU_NUMBER+SQU_LONG;
}while(getpixel(x,y)==RED||getpixel(x,y)==YELLOW||x>SQU_LONG*(SQU_NUMBER-1)||y>SQU_LONG*(SQU_NUMBER-1)); //不能出现在蛇头和蛇身上还有边框右边和下边
foodx=x; //将坐标保存为全局变量,用于其他方法批绘制
foody=y;
}
void snakeMove()//蛇移动
{
int i,savec; //savec用于在选择方向是临时保存
while(1)
{
p=head->next; //p指向蛇头
saveLast.x=p->x; //依然是将移动前蛇头信息保存为上一节点信息
saveLast.y=p->y;
saveLast.dire=p->dire;
if(p->dire==1)
p->x-=SQU_LONG; //向左,p->x-1
else if(p->dire==2)
p->y-=SQU_LONG; //向上,p->y-1
else if(p->dire==3)
p->x+=SQU_LONG; //向右,p->x+1
else if(p->dire==4)
p->y+=SQU_LONG; //向下,p->y+1
p=p->next; //p指向第一个身子
while(p)
{
saveBody.x=p->x; //依然是将移动后信息保存为现在节点的信息
saveBody.y=p->y;
saveBody.dire=p->dire;
p->x=saveLast.x;
p->y=saveLast.y;
p->dire=saveLast.dire;
saveLast.x=saveBody.x;
saveLast.y=saveBody.y;
saveLast.dire=saveBody.dire;
p=p->next; //p指向第二个身子
}
p=head->next; //p重新指向蛇头
if(getpixel(p->x,p->y)==YELLOW||p->x<0||p->y<0||p->x>(SQU_LONG*(SQU_NUMBER-1)+45)||p->y>SQU_LONG*(SQU_NUMBER-1)) //如果获取到蛇头坐标的颜色为黄色(蛇头覆盖在身子上)活着蛇头碰到边缘
over(); //调用失败方法
else if(getpixel(p->x,p->y)==GREEN) //如果蛇头坐标的颜色为绿色(蛇头覆盖在食物上,代表吃掉食物)
{
mciSendString("open music/eat_food.mp3 alias eat_food",NULL,0,NULL); //调用吃掉食物的音乐
mciSendString("play eat_food from 0",NULL,0,NULL); //from 0为播放一次
eat(); //调用吃食物方法
food(); //调用生成食物方法
}
BeginBatchDraw(); //开始批绘制
putimage(0,0,&img); //每一次绘制都需要刷新,所以每次都需要调用背景图片
p=head->next;
setfillcolor(RED); //移动后重新画蛇头
bar(p->x,p->y,p->x+SQU_LONG-1,p->y+SQU_LONG-1);
p=p->next;
while(p)
{
setfillcolor(YELLOW); //移动后重新画身子
bar(p->x,p->y,p->x+SQU_LONG-1,p->y+SQU_LONG-1);
p=p->next;
}
setfillcolor(GREEN); //设置填充颜色为绿色,表示食物
bar(foodx,foody,foodx+SQU_LONG-1,foody+SQU_LONG-1); //绿色代表食物
outtextxy(540,200,"得分:");
sprintf(score,"%d",(NUM-3)*10); //将整数(NUM-3)*10转换为字符串保存在score中
loadimage(&change,"image/score.jpg");
putimage(495,220,&change);
outtextxy(546,220,score);
FlushBatchDraw(); //每更新一次绘制一次
p=head->next;
for(i=0;i<5;i++)
{
Sleep(NUM<20?(200-4*NUM):100); //蛇移动停顿的时间,随着吃的食物个数的增多停顿时间减少,减少到0.1秒后保持不变
savec=p->dire; //将蛇头原来的方向保存到savec中
p->dire=Direction(savec); //调用按键方法并传递savec参数
if(p->dire==5) //如果选择了加速按键
{
p->dire=savec; //方向仍然是原方向
break; //跳出循环,不停顿
}
}
EndBatchDraw(); //关闭批量绘图
}
}
int Direction(int savec)//按键
{
p=head->next; //p指向蛇头
if(p->dire!=3&&GetAsyncKeyState(VK_LEFT)&0x8000) //如果原来方向不向右并且按下左键
p->dire=1;
else if(p->dire!=4&&GetAsyncKeyState(VK_UP)&0x8000) //如果原来方向不向下并且按下上键
p->dire=2;
else if(p->dire!=1&&GetAsyncKeyState(VK_RIGHT)&0x8000) //如果原来方向不向左并且按下右键
p->dire=3;
else if(p->dire!=2&&GetAsyncKeyState(VK_DOWN)&0x8000) //如果原来方向不向上并且按下下键
p->dire=4;
else if(GetAsyncKeyState(VK_SHIFT)&0x8000) //按shift键
p->dire=5;
else if(GetAsyncKeyState(VK_SPACE)&0x8000) //按空格键
{
loadimage(&stop,"image/stop.jpg");
putimage(495,290,&stop); //更新游戏暂停图片
FlushBatchDraw(); //批绘制
mciSendString("stop bk_audio",NULL,0,NULL); //暂停,音乐停止
system("pause"); //暂停,按任意键继续
loadimage(&play,"image/play.jpg");
putimage(495,290,&play); //更新正在游戏图片
FlushBatchDraw(); //批绘制
mciSendString("play bk_audio",NULL,0,NULL); //音乐重新从上一次结束的地方播放
p->dire=savec; //暂停结束继续向同一个方向走
}
else
p->dire=savec; // 若是没有选择依然是原先的方向
return p->dire;
}
void over()//结束
{
loadimage(&fail,"image/fail.jpg");
putimage(495,290,&fail); //更新游戏失败图片
FlushBatchDraw(); //批绘制
mciSendString("stop bk_audio",NULL,0,NULL); //结束,音乐停止
system("pause");
exit(0); //退出到控制台
}
void eat()//吃食物
{
p=head;
while(p->next!=NULL) //若p不是尾节点,则继续进入循环遍历
p=p->next;
struct snake *a=(snake *)malloc(sizeof(snake)); //重新声明一个节点,用于添加身子
a->x=saveLast.x; //将最后保存的节点信息赋给新节点
a->y=saveLast.y;
a->dire=saveLast.dire;
a->next=NULL;
p->next=a; //连接到链表中
p=p->next;
NUM+=1; //节点个数加1
}
源代码文件:源代码