C语言如何实现单链表控制台贪吃蛇小游戏
这篇文章给大家分享的是有关C语言如何实现单链表控制台贪吃蛇小游戏的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。
C语言实现单链表控制台贪吃蛇小游戏,供大家参考。
编译环境:vs2019
需求:
统计游戏开始后的时间,控制贪吃蛇;吃到食物蛇身加长,得分加一;碰墙或蛇头碰到身体减一条生命;生命消耗完则结束游戏。
思路:
使用wasd键控制蛇的移动方向,蛇头碰到食物得分加一,并在地图上随机产生一个食物,累加得分,碰墙或碰自己减一条生命,并初始化整条蛇,生命值为0时结束游戏。
做法:
使用单链表控制贪吃蛇移动的核心思想就是:链表存储贪吃蛇所有坐标,每次循环贪吃蛇不断向一个方向插入一个新的结点作为新的蛇头,按下按键控制新蛇头产生的位置,然后从新蛇头处遍历链表输出蛇身到上一个蛇尾,清除上一个蛇尾的痕迹,并释放相关结点。
每次向链表插入新节点后,判断新节点的坐标是否和食物的坐标重合,如果重合本轮循环不释放清除蛇尾结点,反之释放清除上一个蛇尾的结点。
另外,在写蛇生命相关代码的时候,还需要注意一下哪些值应该初始化,哪些值不应该初始化。只要明白了贪吃蛇运动的核心思想,整个程序其实就不难写出来。
难点:
wsad控制贪吃蛇上下左右移动,并清除蛇尾。
说明:
使用单链表实现贪吃蛇的核心思想是我一开始没有想到的,部分相关代码我学习并借鉴了一些网络上搜索到的代码,如果有违反了相关版权协议,请告知我修改相关代码。
注意:
由于编译器原因程序中_kbhit()和_getch()函数可能在其他编译器上编译会出现错误,解决办法是去掉函数前面的“_”。
运行效果:
代码实现:
#include<stdio.h> #include<stdlib.h> #include<conio.h> #include<time.h> #include<windows.h> voidHideCursor();//光标隐藏 voidgotoxy(intx,inty);//光标定位 typedefstructsnake { intx; inty; structsnake*next; }snake; #defineWIDTH100//控制台窗口宽度 #defineHEIGHT30//控制台窗口高度 #defineSNAKEN4//贪吃蛇初始长度 #defineLIFE3//初始生命次数 #defineSPEED200//游戏速度、循环休眠时间 #defineU1//使用宏代替需要数字代替的蛇的行动方向 #defineD2//宏名含义是各方向英文单词首字母 #defineL3//蛇的状态,U:上;D:下;L:左R:右 #defineR4 voiddtxxcsh()//输出地图 { for(inti=1;i<WIDTH-1;i++)//输出上下面墙 { gotoxy(i,26); printf("-"); gotoxy(i,0); printf("-"); } for(inti=0;i<HEIGHT-3;i++)//输出左右两面墙 { gotoxy(0,i); printf("|"); gotoxy(99,i); printf("|"); } gotoxy(24,28); printf("得分:0生命:%d时间:0",LIFE); //xy30,28可用得分数值14个空格 } intfoodx,foody;//食物位置坐标 voidsjcsswhs()//随机产生一个食物 { srand(time(NULL)); foodx=rand()%(WIDTH-4)+2;//使用宏运算随机数最大值需要加括号 while(foodx%2)//如果食物的x坐标不是偶数,再获取一个x坐标 { foodx=rand()%(WIDTH-4)+2;//宽度 } foody=rand()%(HEIGHT-7)+3;//高度 gotoxy(foodx,foody); printf("★"); } snake*head;//蛇头指针 voidcshs()//初始化蛇的位置 { snake*tail;//蛇尾指针 inti; tail=(snake*)malloc(sizeof(snake)); tail->next=NULL; tail->x=HEIGHT-6; tail->y=8; //贪吃蛇初始长度5SNAKEN for(i=1;i<=SNAKEN;i++)//在蛇尾处创建链表 { head=(snake*)malloc(sizeof(snake)); head->next=tail; head->x=24+i*2;//head->x这个数必须为偶数,和食物坐标偶数对应 head->y=8; tail=head;//此时蛇尾指针指向蛇头 } while(tail) { gotoxy(tail->x,tail->y); printf("■"); tail=tail->next; } } intstatus=R;//蛇前进状态 snake*p=NULL;//工作指针 snake*nexthead;//下一个蛇头 intscore=0;//得分 voidsnakemove()//蛇前进,上U,下D,左L,右R { nexthead=(snake*)malloc(sizeof(snake)); if(status==U) { nexthead->y=head->y-1;//确定新蛇头的下一个坐标x,y nexthead->x=head->x; } if(status==D)//下 { nexthead->y=head->y+1; nexthead->x=head->x; } if(status==L)//左 { nexthead->x=head->x-2; nexthead->y=head->y; } if(status==R)//右 { nexthead->x=head->x+2; nexthead->y=head->y; } nexthead->next=head; head=nexthead; p=head; if(p->x==foodx&&p->y==foody)//判断蛇头的位置是否和食物的位置重合 { while(p)//输出尾结点 { gotoxy(p->x,p->y); if(p==head) printf("●"); else printf("■"); p=p->next;//因为每次运动都是新创建一个头结点再删除一个尾结点, }//所以要增加一节身体,只需要这次循环不释放尾结点,并输出一次尾结点就好了 //sjcsswhs();//碰到食物则再产生一个食物 score++; gotoxy(32,28); printf("%d",score); } else { while(p->next->next)//不输出尾结点 { gotoxy(p->x,p->y); if(p==head) printf("●"); else printf("■"); p=p->next; } gotoxy(p->next->x,p->next->y); printf(""); free(p->next); p->next=NULL; } p=head; while(p)//如果食物的坐标刷新到了蛇身上则再产生一个食物* { if(p->x==foodx&&p->y==foody) sjcsswhs(); p=p->next; } } voidczfxhs()//操作方向函数,接收从键盘输入的按键,控制贪吃蛇行进方向 { charch=_getch(); switch(ch) { case'w': if(status!=D) status=U;break; case's': if(status!=U) status=D;break; case'a': if(status!=R) status=L;break; case'd': if(status!=L) status=R;break; case'': _getch();break;//空格暂停 } } intyxjstjjsmz_1()//生命掉落条件1咬自己 { snake*self=head->next;//self为蛇身上的一个结点 while(self) { if(self->x==head->x&&self->y==head->y)//head和self的成员作比较,蛇头一直存在,这里遍历的是蛇身 { return1; } self=self->next; } return0; } intyxjstjjsmz_2()//生命掉落条件2碰墙 { if(head->x<=1||head->x>=98||head->y<=0||head->y>=26) return1; return0; } inti=LIFE-1;//变量存储生命次数 voidqcsytmslbhs()//清除并释放上一条蛇留下来的痕迹,更新生命信息 { p=head; int_x_=p->x;//用于保存死掉的蛇的蛇头处的位置,用于输出被蛇头顶掉的墙壁 int_y_=p->y; while(head) { gotoxy(head->x,head->y); printf(""); head=head->next; free(p); p=head; } gotoxy(52,28);//更新生命信息 printf("%d",i); if(_y_==0||_y_==HEIGHT-4)//用于在蛇死掉后,蛇头的位置输出被清除蛇头顶替掉的墙壁 { gotoxy(_x_,_y_); printf("--"); } elseif(_x_==WIDTH-2) { gotoxy(_x_+1,_y_); printf("|"); } elseif(_x_==0) { gotoxy(_x_,_y_); printf("|"); } } voidsbjsjmhs()//失败结束界面 { p=head;//释放内存 while(head) { head=head->next; free(p); p=head; } system("cls"); gotoxy(45,12); printf("游戏结束!"); gotoxy(44,14); printf("最终得分:%d",score); gotoxy(0,28); } intupdatetime()//获取一次电脑现在的时间 { intnow; SYSTEMTIMEsystem_time; GetLocalTime(&system_time); now=system_time.wMinute*60+system_time.wSecond; returnnow; } inttime_1=updatetime();//保存游戏刚开始的时间 voidgametime()//写在每次循环之内 { inttime_2=updatetime()-time_1;//更新游戏开始后时间,用现在的时间减去刚开始的时间 gotoxy(72,28); printf("%ds",time_2); } intmain()//主函数 { system("modeconcols=100lines=30");//设置控制台大小 system("title贪吃蛇游戏");//设置标题 HideCursor();//隐藏光标 sjcsswhs();//初始化随机产生一个食物 dtxxcsh();//初始化地图、信息 for(;i>=0;i--)//生命 { cshs();//初始化蛇的位置 status=R;//初始化运动方向 while(1) { snakemove();//蛇行动动画,方向被控制变量控制 if(_kbhit()) { czfxhs();//接收键盘按键,控制控制变量 } if(yxjstjjsmz_1()||yxjstjjsmz_2())//两个掉落生命的条件 break; gametime();//更新游戏时间 Sleep(SPEED); } qcsytmslbhs();//清除上一条蛇留下来的痕迹,更新生命信息 } sbjsjmhs();//失败结束界面 return0; } voidHideCursor() { CONSOLE_CURSOR_INFOcursor_info={1,0}; SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info); } voidgotoxy(intx,inty) { COORDpos={x,y}; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos); }
不足之处:
因为这是我第一次使用链表做一个完整的小程序,所以对链表的运用还很稚嫩,在代码规范和严谨性上面还有很多问题。
另外关于“食物如果刷新到蛇身上则再随机产生一个食物”(172行)相关代码,我不是很确定到底完不完善。
作为一名c语言新手,我对未知的知识始终抱有学习和谦卑的态度,如有贵人能够对我的程序提出建议,我将不胜感激。
感谢各位的阅读!关于“C语言如何实现单链表控制台贪吃蛇小游戏”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!