效果图如图

首先发现组成元素是“实心方块”我们可以百度 也可以在我这里直接复制 ▇ 进编译环境 (这个方块是两个字节这个很重要)
完成这个小程序基本上我们分以下几步
1.完成所有静态的元素(四周的方块界线)
2.绘制蛇
3.使蛇吃东西
下面分布进行实现
首先完成第一步
/*头文件/ #include<stdio.h> #include<stdlib.h> #include<windows.h> #include<time.h> /*函数声明*/ void muban(); void Pos(int x, int y); /*自定义函数*/ void Pos(int x, int y)//设置光标位置,从哪里开始输出 { COORD pos;//表示一个字符在控制台屏幕上的坐标,左上角(0,0) HANDLE hOutput; pos.X = x; pos.Y = y; hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//返回标准的输入、输出或错误的设备的句柄,也就是获得输入、输出/错误的屏幕缓冲区的句柄 SetConsoleCursorPosition(hOutput, pos); } void muban() { int i; for(i=0;i<=60;i+=2)//方块水平方向占两个字节 { Pos(i,0); printf("▇");//上行 Pos(i,26); printf("▇");//下行 } for(i=0;i<=25;i+=1)//方块竖直方向占1个字节 { Pos(0,i);//左列 printf("▇"); Pos(60,i);//右列 printf("▇"); } } /*主函数/ int main() { muban(); return 0; }

下面我们来描绘蛇,这里要用到结构体指针和链表
主要思想是 一个▇是蛇的一段 然后用链表将它们链接起来
具体的链表和结构体指针可以看下面的文章:(只用掌握最基本的用法即可)
结构体https://blog.csdn.net/zhanghow/article/details/
链表https://blog.csdn.net/govshell/article/details/
指针https://blog.csdn.net/cyh/article/details/(这篇文章讲的很棒,配合结构体食用)
接下来继续撸代码
void initSnake() { snake *tail;//尾指针 snake *head;//头指针 tail = (snake*)malloc(sizeof(snake));//以snake结构体的形式开辟一块新的内存,内存中的数据是新的,用tail指向这个结构体 tail ->x=30;//因为实心方块宽度为2个单位长度,所以必须为偶数 tail ->y=10; tail ->next=NULL; for(i = 0;i<4;i++) { head=(snake*)malloc(sizeof(snake));//以snake结构体的形式开辟一块新的内存,内存中的数据是新的,用head指向这个结构体 head->next=tail;//将 tail=head;//将尾指针传向下一个头指针 } }
比较难理解的是这段尾插法,如果不会的可以去看这篇https://blog.csdn.net/viafcccy/article/details/
这里下面的代码
typedef struct Snake//相当于蛇一个节点 { int x;//横坐标 int y;//纵坐标 struct Snake *next; }snake;
等价于
struct Snake { int x;//横坐标 int y;//纵坐标 struct Snake *next; }; struct Snake snake;
关于typedef看这篇https://blog.csdn.net/viafcccy/article/details/
下面完成蛇身的打印
其中第一遍的代码是这样
void initSnake() { int i; snake *tail;//尾指针 snake *head;//头指针 tail = (snake*)malloc(sizeof(snake));//以snake结构体的形式开辟一块新的内存,内存中的数据是新的,用tail指向这个结构体 tail ->x=30;//因为实心方块宽度为2个单位长度,所以必须为偶数 tail ->y=10; tail ->next=NULL; for(i = 0;i<4;i++) { head=(snake*)malloc(sizeof(snake));//以snake结构体的形式开辟一块新的内存,内存中的数据是新的,用head指向这个结构体 head->next=tail;//链接成链 head->x=30+2*i;//下一节点的位置 head->y=10; tail=head;//将尾指针传向下一个头指针 }
结果是

我们错在原来蛇是从右向左打印了8个单位 但是应为固定输出press any keyt continues将三个实心方块挡住了,于是将代码修改
/*头文件/ #include<stdio.h> #include<stdlib.h> #include<windows.h> #include<time.h> /*函数声明*/ void muban();//打印四周的边界 void Pos(int x, int y);//设置光标输出位置 void initSnake();//初始化蛇身,将结构体中的坐标读取打印实心方块 /*结构体*/ typedef struct Snake//将蛇身的位置存入结构体,相当于蛇身上的一个实心方块 { int x;//横坐标 int y;//纵坐标 struct Snake *next; }snake; /*自定义函数*/ void Pos(int x, int y)//设置光标位置,从哪里开始输出 { COORD pos;//表示一个字符在控制台屏幕上的坐标,左上角(0,0) HANDLE hOutput; pos.X = x; pos.Y = y; hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//返回标准的输入、输出或错误的设备的句柄,也就是获得输入、输出/错误的屏幕缓冲区的句柄 SetConsoleCursorPosition(hOutput, pos); } void muban() { int i; for(i=0;i<=60;i+=2)//方块水平方向占两个单位 { Pos(i,0); printf("▇");//上行 Pos(i,26); printf("▇");//下行 } for(i=0;i<=25;i+=1)//方块垂直方向占1个单位 { Pos(0,i);//左列 printf("▇"); Pos(60,i);//右列 printf("▇"); } } void initSnake() { int i; snake *tail;//尾指针 snake *head;//头指针 tail = (snake*)malloc(sizeof(snake));//以snake结构体的形式开辟一块新的内存,内存中的数据是新的,用tail指向这个结构体 tail ->x=30;//因为实心方块宽度为2个单位长度,所以必须为偶数 tail ->y=10; tail ->next=NULL; for(i = 1;i<=4;i++) { head=(snake*)malloc(sizeof(snake));//以snake结构体的形式开辟一块新的内存,内存中的数据是新的,用head指向这个结构体 head->next=tail;//链接成链 head->x=30-2*i;//下一节点的位置 head->y=10; tail=head;//将尾指针传向下一个头指针 } //遍历打印出来 while(tail->next!=NULL) { Pos(tail->x,tail->y); printf("▇"); tail = tail->next; } } /*主函数/ int main() { muban(); initSnake(); return 0; }

这样我用四个永远紧紧相连的方块构成了蛇的身体,下面我们来生成食物
思路是随机生成两个随机数使x,y分别满足在边界内,但是x有一个要求就是要是偶数这个和实心方块两个字节有关,我们要避免下图的情况产生

void creatFood()//创建食物 { snake *food;//创造一个食物 food=(snake*)malloc(sizeof(snake)); srand((unsigned int)time(NULL));//随着时间变化,产生不一样种子,就会得到没规律的食物 while(food->x%2!=0) { food->x=rand()%56+2; } food->y=rand()%23+1; //上面虽然解决了食物不会出现在城墙里,没有考虑食物出现在蛇本身里面 p=head;//用p来遍历 while(p!=NULL)//解决食物出现在蛇本身 { if(food->x==p->x&&food->y==p->y) { free(food); creatFood(); } p=p->next; } Pos(food->x,food->y); food1=food;//food1用来标记的作用 printf("▇"); }
关于产生随机数可以看这篇文章https://blog.csdn.net/viafcccy/article/details/
于是我们下一步就是要做到我们按键蛇会动我们使用头文件windows.h,具体看这篇https://blog.csdn.net/viafcccy/article/details/
这里介绍一下遍历 就是将所有链表中的数据访问一遍 通常是打印出来
蛇的移动主要通过获取键值,然后如下操作



但是我们要将1的位置打印空格 否则实心方块不会消失 但是这样打印时会有光标始终跟着蛇尾 因此需要在外面打印一个东西让光标绝大部分时间在外面 只有一个瞬间在蛇尾我们就不会看到了
void snakeMove() { snake *nexthead; nexthead=(snake*)malloc(sizeof(snake)); if(status=='R') { nexthead->x=head->x+2; nexthead->y=head->y; if(nexthead->x==food1->x&&nexthead->y==food->y) { nexthead->next=head; head=nexthead; p=head;//遍历 while(p!=NULL) { Pos(p->x,p->y); printf("▇"); p=p->next; } } else { nexthead->next=head; head=nexthead; p=head;//遍历 while(p->next->next!=NULL) { Pos(p->x,p->y); printf("▇"); p=p->next; } Pos(p->next->x,p->next->y); printf(" ");//会带来一个光标闪烁 Pos(70,20); printf("您的分数是:%d",score); free(p->next); p->next=NULL; } if(status=='L')//向左走 { nexthead->x=head->x-2; nexthead->y=head->y; if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物 { nexthead->next=head; head=nexthead; p=head;//p用来从头遍历,打印方块 while(p!=NULL) { Pos(p->x,p->y); printf("▇"); p=p->next; }//吃掉了食物得创造 score=score+add; creatFood(); } else//没有食物 { nexthead->next=head; head=nexthead; p=head;//p用来从头遍历,打印方块 while(p->next->next!=NULL) { Pos(p->x,p->y); printf("▇"); p=p->next; } Pos(p->next->x,p->next->y); printf(" "); Pos(70,20);//解决办法 printf("您的分数是:%d",score); free(p->next); p->next=NULL; } } if(status=='U')//向上走 { nexthead->x=head->x; nexthead->y=head->y-1; if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物 { nexthead->next=head; head=nexthead; p=head;//p用来从头遍历,打印方块 while(p!=NULL) { Pos(p->x,p->y); printf("▇"); p=p->next; }//吃掉了食物得创造 score=score+add; creatFood(); } else//没有食物 { nexthead->next=head; head=nexthead; p=head;//p用来从头遍历,打印方块 while(p->next->next!=NULL) { Pos(p->x,p->y); printf("▇"); p=p->next; } Pos(p->next->x,p->next->y); printf(" "); Pos(70,20);//解决办法 printf("您的分数是:%d",score); free(p->next); p->next=NULL; } } if(status=='D')//向下走 { nexthead->x=head->x; nexthead->y=head->y+1; if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物 { nexthead->next=head; head=nexthead; p=head;//p用来从头遍历,打印方块 while(p!=NULL) { Pos(p->x,p->y); printf("▇"); p=p->next; }//吃掉了食物得创造 score=score+add; creatFood(); } else//没有食物 { nexthead->next=head; head=nexthead; p=head;//p用来从头遍历,打印方块 while(p->next->next!=NULL) { Pos(p->x,p->y); printf("▇"); p=p->next; } Pos(p->next->x,p->next->y); printf(" "); Pos(70,20);//解决办法 printf("您的分数是:%d",score); free(p->next); p->next=NULL; } } Sleep(sleepTime);//蛇移动的速度,里面是毫秒,越大速度越慢 status=reDirection();//判别下方向先 if(crossWall()==1||eatSelf()==1) //exit(0);//直接把程序关闭了 endleap=1; return endleap; } int crossWall()//判断蛇有没穿透墙 { if(head->x==0||head->y==0||head->x==60||head->y==25) leap=1; return leap; } int eatSelf()//判断是否咬到了自己 { snake *q;//遍历的 q=head->next; while(q!=NULL) { if(q->x==head->x&&head->y==q->y) leap=1; q=q->next; } return leap; }
下面我们要解决gameover的判断
贪吃蛇结束就两种情况
1)撞到墙壁
2)咬到自己
int crossWall()//判断蛇有没撞墙 { if(head->x==0||head->y==0||head->x==60||head->y==25) leap=1; return leap; }
int eatSelf()//判断是否咬到了自己 { snake *q;//遍历的 q=head->next; while(q!=NULL) { if(q->x==head->x&&head->y==q->y) leap=1; q=q->next; } return leap; }
最后经过优化的源代码在这里
/*头文件/ #include<stdio.h> #include<stdlib.h> #include<windows.h> #include<time.h> /*函数声明/ void Pos(int x, int y);//光标位置设定 void muban();//打印模板 void initSnake();//蛇身的初始化 void creatFood();//创建食物 char reDirection();//识别方向 int snakeMove();//蛇移动 int crossWall();//不能穿墙 int eatSelf();//不能吃自己 /结构体*/ typedef struct Snake//相当于蛇一个节点 { int x;//横坐标 int y;//纵坐标 struct Snake *next; }snake; /*全局变量/ snake *head;//头指针 snake *p;//用来遍历 snake *food1;//用来标记的 char status='L';//初始方向的状态,解决开始会动的问题 int score=0;//分数 int add=10;//一个食物的分 int leap=0;//用来标志是否结束,0没有,1代表蛇死了代表结束了 int endleap=0;//结束标志 1就是结束 int sleepTime=500; /*自定义函数/ void initSnake()//蛇身初始化,给定一个长度,用结构体表示是蛇的骨架,真正要显示出来是打印▇ { int i; snake *tail;//尾指针 tail=(snake*)malloc(sizeof(snake));//第一个节点/头结点 tail->x=30;//2的倍数,因为方块的长是两个单位 tail->y=10;//1个单位 tail->next=NULL; for(i=1;i<=4;i++)//尾插法 { head=(snake*)malloc(sizeof(snake));//申请一个节点 head->next=tail;//连接成链 head->x=30-2*i;//下一个节点的位置 head->y=10; tail=head; } //遍历打印出来 while(tail!=NULL) { Pos(tail->x,tail->y); printf("▇"); tail=tail->next; } } char reDirection()//识别用户按下的键值 保留方向值 { if(GetAsyncKeyState(VK_F7))//热键 { if(sleepTime>300)//最多减到300 { sleepTime-=50;//每次减50 add++;//每次食物加1分 } } if(GetAsyncKeyState(VK_F8)) { if(sleepTime<800)//最多加到800 { sleepTime+=50;//每次加50 add--;//每次食物减1分 } } if(GetAsyncKeyState(VK_UP)&&status!='D') status='U'; if(GetAsyncKeyState(VK_DOWN)&&status!='U') status='D'; if(GetAsyncKeyState(VK_LEFT)&&status!='R') status='L'; if(GetAsyncKeyState(VK_RIGHT)&&status!='L') status='R'; return status; } void Pos(int x, int y)//设置光标位置,从哪里开始输出 { COORD pos;//表示一个字符在控制台屏幕上的坐标,左上角(0,0) HANDLE hOutput; pos.X = x; pos.Y = y; hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//返回标准的输入、输出或错误的设备的句柄,也就是获得输入、输出/错误的屏幕缓冲区的句柄 SetConsoleCursorPosition(hOutput, pos); } void creatFood()//创建食物 { snake *food;//创造一个食物 food=(snake*)malloc(sizeof(snake)); srand((unsigned int)time(NULL));//随着时间变化,产生不一样种子,就会得到没规律的食物 while(food->x%2!=0) { food->x=rand()%56+2; } food->y=rand()%23+1; //上面虽然解决了食物不会出现在城墙里,没有考虑食物出现在蛇本身里面 p=head;//用p来遍历 while(p!=NULL)//解决食物出现在蛇本身 { if(food->x==p->x&&food->y==p->y) { free(food); creatFood(); } p=p->next; } Pos(food->x,food->y); food1=food;//food1用来标记的作用 printf("▇"); Pos(70,20);//解决有光标闪烁的办法 printf("您的分数是:%d",score); } void muban() { int i; for(i=0;i<=60;i+=2)//方块水平方向占两个单位 { Pos(i,0); printf("▇");//上行 Pos(i,26); printf("▇");//下行 } for(i=0;i<=25;i+=1)//方块垂直方向占1个单位 { Pos(0,i);//左列 printf("▇"); Pos(60,i);//右列 printf("▇"); } } int snakeMove() { snake *nexthead; nexthead=(snake*)malloc(sizeof(snake)); if(status=='R')//向右走 { nexthead->x=head->x+2; nexthead->y=head->y; if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物 { nexthead->next=head; head=nexthead; p=head;//p用来从头遍历,打印方块 while(p!=NULL) { Pos(p->x,p->y); printf("▇"); p=p->next; }//吃掉了食物得创造 score=score+add; creatFood(); } else//没有食物 { nexthead->next=head; head=nexthead; p=head;//p用来从头遍历,打印方块 while(p->next->next!=NULL) { Pos(p->x,p->y); printf("▇"); p=p->next; } Pos(p->next->x,p->next->y); printf(" ");//会带来一个光标闪烁 Pos(70,20);//解决办法 printf("您的分数是:%d",score); free(p->next); p->next=NULL; } } if(status=='L')//向左走 { nexthead->x=head->x-2; nexthead->y=head->y; if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物 { nexthead->next=head; head=nexthead; p=head;//p用来从头遍历,打印方块 while(p!=NULL) { Pos(p->x,p->y); printf("▇"); p=p->next; }//吃掉了食物得创造 score=score+add; creatFood(); } else//没有食物 { nexthead->next=head; head=nexthead; p=head;//p用来从头遍历,打印方块 while(p->next->next!=NULL) { Pos(p->x,p->y); printf("▇"); p=p->next; } Pos(p->next->x,p->next->y); printf(" "); Pos(70,20);//解决办法 printf("您的分数是:%d",score); free(p->next); p->next=NULL; } } if(status=='U')//向上走 { nexthead->x=head->x; nexthead->y=head->y-1; if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物 { nexthead->next=head; head=nexthead; p=head;//p用来从头遍历,打印方块 while(p!=NULL) { Pos(p->x,p->y); printf("▇"); p=p->next; }//吃掉了食物得创造 score=score+add; creatFood(); } else//没有食物 { nexthead->next=head; head=nexthead; p=head;//p用来从头遍历,打印方块 while(p->next->next!=NULL) { Pos(p->x,p->y); printf("▇"); p=p->next; } Pos(p->next->x,p->next->y); printf(" "); Pos(70,20);//解决办法 printf("您的分数是:%d",score); free(p->next); p->next=NULL; } } if(status=='D')//向下走 { nexthead->x=head->x; nexthead->y=head->y+1; if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物 { nexthead->next=head; head=nexthead; p=head;//p用来从头遍历,打印方块 while(p!=NULL) { Pos(p->x,p->y); printf("▇"); p=p->next; }//吃掉了食物得创造 score=score+add; creatFood(); } else//没有食物 { nexthead->next=head; head=nexthead; p=head;//p用来从头遍历,打印方块 while(p->next->next!=NULL) { Pos(p->x,p->y); printf("▇"); p=p->next; } Pos(p->next->x,p->next->y); printf(" "); Pos(70,20);//解决办法 printf("您的分数是:%d",score); free(p->next); p->next=NULL; } } Sleep(sleepTime);//蛇移动的速度,里面是毫秒,越大速度越慢 status=reDirection();//判别下方向先 if(crossWall()==1||eatSelf()==1) //exit(0);//直接把程序关闭了 endleap=1; return endleap; } int crossWall()//判断蛇有没穿透墙 { if(head->x==0||head->y==0||head->x==60||head->y==25) leap=1; return leap; } int eatSelf()//判断是否咬到了自己 { snake *q;//遍历的 q=head->next; while(q!=NULL) { if(q->x==head->x&&head->y==q->y) leap=1; q=q->next; } return leap; } //打印食物的时候会出现光标,解决办法就是引开它 /*主函数*/ int main() { muban();//打印模板 initSnake();//初始化蛇 creatFood();//创建食物 while(1)//死循环,让蛇一直动起来,直到蛇死了 { if(snakeMove()==1)//判断是否结束 { Pos(70,23); printf("蛇死了"); system("pause");//用来暂停 Pos(70,24);//解决press any key to continue 在该地点打印 大家试下 break; } } printf("是否继续游戏,y or n:");//y 继续 if(getch()=='y')//重新游戏 { //蛇一开始就死了,因为全局变量没有恢复原值,仍然保留上一局的值 status='L';//初始方向的状态,解决开始会动的问题 score=0;//分数 add=10;//一个食物的分 leap=0;//用来标志是否结束,0没有,1代表蛇死了代表结束了 endleap=0;//结束标志 1就是结束 sleepTime=500; system("cls");//清理屏幕 main();//自己调用自己 看不一样的编译器,vc6.0允许调用自己 } if(getch()=='n') { Pos(70,25);//定一个位置,再打印press exit(0);//退出程序 } return 0; } //蛇的速度变化,每个食物的分数增加 //是否继续游戏 //按键的作用
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/231639.html原文链接:https://javaforall.net
