c语言实现贪吃蛇教程

c语言实现贪吃蛇教程效果图如图 nbsp 首先发现组成元素是 实心方块 我们可以百度也可以在我这里直接复制 进编译环境 这个方块是两个字节这个很重要 完成这个小程序基本上我们分以下几步 1 完成所有静态的元素 四周的方块界线 2 绘制蛇 3 使蛇吃东西 nbsp 下面分布进行实现首先完成第一步

效果图如图 

c语言实现贪吃蛇教程

首先发现组成元素是“实心方块”我们可以百度 也可以在我这里直接复制 ▇ 进编译环境 (这个方块是两个字节这个很重要)

完成这个小程序基本上我们分以下几步

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; }

c语言实现贪吃蛇教程

下面我们来描绘蛇,这里要用到结构体指针和链表

主要思想是 一个▇是蛇的一段 然后用链表将它们链接起来

具体的链表和结构体指针可以看下面的文章:(只用掌握最基本的用法即可)

结构体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;//将尾指针传向下一个头指针 }

 

结果是

c语言实现贪吃蛇教程

我们错在原来蛇是从右向左打印了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; }

c语言实现贪吃蛇教程

这样我用四个永远紧紧相连的方块构成了蛇的身体,下面我们来生成食物

思路是随机生成两个随机数使x,y分别满足在边界内,但是x有一个要求就是要是偶数这个和实心方块两个字节有关,我们要避免下图的情况产生

c语言实现贪吃蛇教程

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/

这里介绍一下遍历 就是将所有链表中的数据访问一遍 通常是打印出来

蛇的移动主要通过获取键值,然后如下操作

c语言实现贪吃蛇教程

c语言实现贪吃蛇教程

c语言实现贪吃蛇教程

但是我们要将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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • stringutil.isnotempty_中低腰和低腰的区别

    stringutil.isnotempty_中低腰和低腰的区别学习中遇到了这个地方,搜了一下,这位仁兄总结的挺详细,就粘了过来原文链接(https://www.cnblogs.com/dixinyunpan/p/6088612.html)isNotEmpty(str)等价于str!=null&&str.length>0isNotBlank(str)等价于str!=null&&str…

    2022年8月12日
    6
  • 人工与软件刷流量有什么区别,如何做刷流量效果才最好?

    人工与软件刷流量有什么区别,如何做刷流量效果才最好?大家好,对于流量这个关键词我相信有绝大部分的是了解的,无论是对于开网店的,还是做直播亦或是做其他事情的,都需要流量进行曝光、推广等,但是,一些网店因为流量不够,所能得到的流量池有限,所以就导致了许多开网店的朋友们进行刷流量,现在刷流量的软件也是千千万万,那么选择哪种会比较好一些呢,亦或是人工刷流量与软件刷流量有什么区别呢?商家都了解真实的人工访客流量是需要有店铺停留时间或者是商品详情页的停留时间的,而这个停留时间是根据商家的店铺权重而不同的,每一个等级的店铺所算的停留时间是不相同的,如果商品优.

    2022年9月29日
    3
  • 【Unity3D开发小游戏】《文字冒险游戏》Unity开发教程

    【Unity3D开发小游戏】《文字冒险游戏》Unity开发教程基本程序设计(故事卡)游戏会为玩家呈现一个“故事卡”。故事卡上包含一些文字,其中一部分是用于描述玩家当前的状态,另外一部分是在当前情况下玩家可以做出的一系列选择。根据玩家的不同选择,剧情也会按照不同的分支向前发展,并持续出现新的卡片与选择,直到最终的卡片不再有新的选择,则游戏结束。制作一张“故事卡”很简单。根据上诉需求,我们新建StoryCard脚本,脚本代码如下:StoryCard在…

    2022年5月26日
    56
  • .NET(c#) 移动APP开发平台 – Smobiler(1)

    .NET(c#) 移动APP开发平台 – Smobiler(1)如果说基于.net的移动开发平台,目前比较流行的可能是xamarin了,不过除了这个,还有一个比xamarin更好用的国内的.net移动开发平台,smobiler,不用学习另外一套开发模式或者搭建复杂的开发环境,smobiler能够让大家像开发传统windows一样去开发移动应用,那么列举一下这个平台的特点。1. 基于VisualStudio的可视化开发。如同开发传统Windows平台一样的…

    2022年5月6日
    168
  • MessageBox()功能

    MessageBox()功能

    2022年1月1日
    64
  • 防火墙基础

    防火墙基础

    2021年4月15日
    150

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号