前言

最近在学习C语言,做了一个C语言经典小项目贪吃蛇

功能如下:

目录结构:

没有分太多的文件结构,偷懒了下,就一个入口main文件和Init初始化文件(其实初始化里边还细分很多的操作),真正的项目开发其实这样不是很好,结构不清晰。

main入口文件


#include<stdio.h>
#include<stdlib.h>
#include"Init.h"void main(){//初始化Init();while(1){//画地图DrawMap();//判断有没有方向键输入directSelect();//蛇的移动moveSnake();//清空屏幕system("cls");}}

Init头文件

/*********宏定义区**************/
#ifndef C_SNAKE
#define C_SNAKE#include<stdio.h>
#include<time.h>
#include <windows.h>
#include<stdlib.h>
#include<conio.h>//kbhit 遇到kbhit不会停  与getch()区别#define FOOD 1
#define INIT_X 5 //蛇出生点
#define INIT_Y 2
#define MIN_SPEED 100
#define MAX_SPEED 10
#define INIT_LENTH 5 //初始长度/*********结构体定义区****************/
typedef struct node
{/*蛇身节点*/int x;int y;//定义链表struct node *next;}SnakeNode;typedef struct body{char direct;int speed;int length;
}SnakeBody;typedef struct Food{int x;int y;}SnakeFood;/********函数定义区**********/
void DrawMap();
void createSnake();
void createFood();
void printSnake();
SnakeNode *createNode(SnakeNode *head,SnakeNode *data);
void setColor(unsigned short ForeColor,unsigned short BackGroundColor);
void gameOver();
void continueGame();
void directSelect();
void moveSnake();
void Init();
/****************************/#endif

init.c文件

#include"Init.h"
SnakeNode *p;
SnakeBody *b;
SnakeFood *food;
SnakeNode *currentNode=NULL;
//设置光标位置
void setPos(int x,int y){HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);COORD pos = { 0 };pos.X = x;pos.Y = y;SetConsoleCursorPosition(handle, pos);
}//初始化程序
void Init(){char ch;system("cls");setPos(40, 10);printf("******************************");setPos(40, 12);printf("   欢迎来到贪吃蛇游戏!");setPos(40, 14);printf("******************************");setPos(40, 18);system("pause");//清除屏幕system("cls");setPos(70, 5);printf("请选择<1开始 2退出>:");setPos(70, 7);printf("使用↑ ↓ ← →控制方向");setPos(70, 9);printf("4加速,5减速\n");setPos(70,11);printf("按空格暂停/继续,ESC退出");setPos(70, 13);printf("当前速度:");setPos(70, 15);printf("得分:");setPos(92, 5);//判断键盘输入wait:
if(kbhit()){if((ch=getchar())=='1'){createSnake();}if((ch=getchar())=='2'){setPos(25,13);setColor(4,2);puts("Game over!");setPos(0,28);exit(0);}}
else goto wait;}
//画地图
void DrawMap(){setColor(7,2);setPos(70, 5);printf("请选择<1开始 2退出>:");setPos(70, 7);printf("使用↑ ↓ ← →控制方向");setPos(70, 9);printf("4加速,5减速\n");setPos(70,11);printf("按空格暂停/继续,ESC退出"); setPos(70, 13);printf("当前速度:%ld",10000/b->speed);setPos(70, 15);printf("得分:%ld",b->length-1);setPos(92, 5);setPos(0,0);//长为30 宽为27  也可以用循环的方法生成printf("■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■                                                        ■\n");printf("■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■\n");}
//初始化蛇
void createSnake(){int x,y;//设置播种 随机生成位置srand((int)time(0));//保存currentp=(SnakeNode *)malloc(sizeof(SnakeNode));b=(SnakeBody *)malloc(sizeof(SnakeBody));currentNode=(SnakeNode *)malloc(sizeof(SnakeNode));//动态分配大小并返回指向改内存的指针x=INIT_X*rand()%28+2;y=INIT_Y*rand()%25+2;x=(x%2==0)?(x):(x+1);y=(y%2==0)?(y):(y+1);p->x=x;p->y=y;b->direct=77;b->speed=MIN_SPEED;b->length=1;p->next=NULL;//创建食物
createFood();
}//判断有没有方向键输入
void directSelect(){char ch;if(kbhit()){ch = getch();switch(ch){//判断上下左右case 72:b->direct=72  ;break;case 80:b->direct=80  ;break;case 75:b->direct=75  ;break;case 77:b->direct=77  ;break;case 52:(b->speed>MAX_SPEED)?b->speed-=1:b->speed ;break;case 53:(b->speed<MIN_SPEED)?b->speed+=1:b->speed ;break;case 27:exit(0);case 32:{setPos(23,13);setColor(4,2);printf("按空格继续游戏!\n");setPos(0,28);system("pause");break;}}}}//蛇的移动
void moveSnake(){int x=0,y=0;setPos(food->x,food->y);setColor(6,2);printf("%c",FOOD);//判断有没有键盘输入,不然会存在延时问题directSelect();switch(b->direct){case 72:y=-1 ;break;case 80:y=1 ;break;case 75:x=-2 ;break;case 77:x=2 ;break;}if(p->x>=57||p->y>=25||p->x<=0||p->y<=0){setPos(25,13);setColor(4,2);printf("Game Over!!\n");gameOver();}else{currentNode=createNode(currentNode,p);p->x=p->x+x;p->y=p->y+y;if(p->x==food->x&&p->y==food->y){b->length++;b->speed-=1;createFood();}//将x,y传入获取上一次的位置printSnake();//VC中大写S  标准C中sSleep(b->speed); }}
//创建食物
void createFood(){int x,y;food=(SnakeFood *)malloc(sizeof(SnakeFood));again:srand((int)time(0));memset(food, 0, sizeof(SnakeNode));x=INIT_X*rand()%25+2;y=INIT_Y*rand()%24+2;x=(x%2==0)?(x):(x+1);y=(y%2==0)?(y):(y+1);if(x==p->x&&y==p->y){goto again;}else{food->x=x;food->y=y;}}//输出链表打印蛇
void printSnake(){SnakeNode *Node;
int i;Node=currentNode;for(i=0;i<b->length&&Node!=NULL;i++){if(Node->x==p->x&&Node->y==p->y){setPos(25,13);setColor(4,2);printf("Game Over!!\n");gameOver();}setPos(Node->x,Node->y);setColor(4,2);printf("■");Node=Node->next;}//free(Node->next);
}//创建链表SnakeNode *createNode(SnakeNode *head,SnakeNode *data){SnakeNode *node;node=(SnakeNode *)malloc(sizeof(SnakeNode));// 将原始头结点链接到此结点后面,并将数据进行设置node->next = head;node->x = data->x;node->y=data->y;head = node;return head;}//设置颜色 第一个参数为字体颜色 第二个为窗口颜色
void setColor(unsigned short ForeColor,unsigned short BackGroundColor){HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);//获取当前窗口句柄SetConsoleTextAttribute(handle,ForeColor+BackGroundColor*0x10);//设置颜色} //游戏结束/判断是否继续还是退出
void gameOver(){char ch;
//延时1s后清除屏幕setColor(4,7);Sleep(500);system("cls");setPos(40, 12);printf("        重开游戏[空格]        ");setPos(40, 14);printf("                              ");setPos(40, 16);printf("        退出游戏[ESC]         ");setPos(40, 20);wait:if((kbhit())>0){switch((ch=getch())){case 32:continueGame();case 27:exit(0);break;}}else goto wait;
}   void continueGame(){createSnake();while(1){//画地图DrawMap();//判断有没有方向键输入directSelect();//蛇的移动moveSnake();//清空屏幕system("cls");}}

详解

结构体、指针等定义:-具体解释在代码注释中给出

/*********结构体定义区****************/
typedef struct node
{/*蛇身位置节点,以及下一位置的节点*/int x;int y;//定义链表struct node *next;}SnakeNode;typedef struct body{//蛇本身节点,方向、速度、长度char direct;int speed;int length;
}SnakeBody;typedef struct Food{//食物位置节点 x方向和y方向int x;int y;}SnakeFood;
/****************************/
//定义蛇当前位置指针
SnakeNode *p;
//定义蛇身体信息指针
SnakeBody *b;
//定义食物位置指针
SnakeFood *food;
//定义位置头指针
SnakeNode *currentNode=NULL;

函数:

  • setPos(int x,int y)-设置光标位置

    这个函数没有什么好讲的,就是一些windows操作,需引入#include <windows.h>


  • setColor(unsigned short ForeColor,unsigned short BackGroundColor)-设置显示文本及背景颜色

    参数:

    1. 第一个参数为字体颜色
    2. 第二天参数为窗口背景颜色

    .
    具体如下:

    对应的颜色码表:

    0=黑色      8=灰色
    1=蓝色      9=淡蓝色
    2=绿色     10=淡绿色
    3=浅绿色  11=淡浅绿色
    4=红色      12=淡红色
    5=紫色      13=淡紫色
    6=黄色      14=淡黄色
    7=白色      15=亮白色

    传入相应的颜色码即可


  • Init()-初始化页面

    详解:

    1. 通过setPos(int x,int y)及显示文本显示基本操作
    2. 通过kbhit()判断是否有按键输入,如果没有继续判断,如果存在则kbhit()返回值大于0,如果没有,返回小于等于0,则判断是否为1(开始游戏)或者2(退出),需引入#include<conio.h>库文件,如果不存在,继续判断是否有按键按下(goto)
    3. system("pause")会清空屏幕,在使用的时候注意,目前我还没有发现比较简单的局部清空的操作

  • createSnake()-初始化蛇

    详解:

    1. 为各指针动态分配大小

    2. 通过srand() 设置随机播种数

    3. 随机设置蛇初始位置,因为构建地图的字符,占两个光标位置,故不能为奇数不然会出现蛇和食物错位的问题

    4. 设置蛇初始长度、速度、方向

      srand((unsigned)time(NULL))函数讲解

      srand 函数是随机数发生器的初始化函数。

      函数:

      void srand(unsigned seed);

      它初始化随机种子,会提供一个种子,这个种子会对应一个随机数,如果使用相同的种子后面的 rand()函数会出现一样的随机数,如: srand(1); 直接使用 1 来初始化种子。不过为了防止随机数每次重复,常常使用系统时间来初始化,即使用 time函数来获得系统时间,它的返回值为从 00:00:00 GMT, January 1, 1970 到现在所持续的秒数,然后将time_t型数据转化为(unsigned)型再传给srand函数,即: srand((unsigned) time(&t)); 还有一个经常用法,不需要定义time_t型t变量,即: srand((unsigned) time(NULL)); 直接传入一个空指针,因为你的程序中往往并不需要经过参数获得的数据。

      计算机并不能产生真正的随机数,而是已经编写好的一些无规则排列的数字存储在电脑里,把这些数字划分为若干相等的N份,并为每份加上一个编号用srand()函数获取这个编号,然后rand()就按顺序获取这些数字,当srand()的参数值固定的时候,rand()获得的数也是固定的,所以一般srand的参数用time(NULL),因为系统的时间一直在变,所以rand()获得的数,也就一直在变,相当于是随机数了。只要用户或第三方不设置随机种子,那么在默认情况下随机种子来自系统时钟。如果想在一个程序中生成随机数序列,需要至多在生成随机数之前设置一次随机种子。


  • createFood()-初始化食物

    食物位置也必须为偶数,且不能和蛇位置相同,如果相同则重新生成(goto)并赋值给食物位置结构指针,以便之后调用


  • DrawMap()-画地图

    1. 同样通过setPos() 函数控制光标画地图,还可以通过for循环的方式生成
    2. 由于在执行一次main函数while循环执行过后会重新清掉屏幕,故需重新画一下地图,得分、速度通过结构体b改变,结构体b会在吃掉食物后进行操作

  • directSelect()-判断有没有方向键、暂停键等键盘键值输入

    1. 判断是否有按键输入,如果有对按键进行判断进行操作

    2. ↑ ↓ ← → 的ascll码分别为72、80、75、77

    3. 4(加速)、5(减速)的ascll码分别为52、53

    4. 空格(暂停)、退出(ESC)的ascll码分别为32、70

    5. 通过getch()接收输入的按键

      getch()getchar()的区别:

      getch()不需要回车来作为结束符,getch()接收输入一个字符,则接收一个,如果这使用getchar()会多按一下空格


  • moveSnake()-蛇移动

    1. 获取食物位置,并打印

    2. 多判断一次,不然会存在蛇的移动会存在延时问题

    3. 通过b指针判断蛇现在的方向,x±方向、y±方向左相应的操作

    4. 判断当前位置是否大于地图边界,如果大于则结束游戏,不大于则保存当前节 (currentNode=createNode(currentNode,p))?

    5. 判断位置是否等于食物的位置,如果等于则蛇的长度+1(b->length++)、速度增加(b->speed-=1)、重新刷新食物位置并显示 (createFood())?

      蛇的速度通过延时时间实现,所以,如果加速的话b->speed应该相应的减

    6. 通过链表打印蛇 (printSnake(x,y))?

    7. 延时一段时间()从而到达控制速度的目的 (Sleep(b->speed))?

      Sleep()传入整形数值控制程序睡眠(延时),单位是ms,在VC中需大写,标准C中为小写


  • SnakeNode *createNode(SnakeNode *head,SnakeNode *data)-创建(保存)节点(蛇的位置)

    这个函数是这个程序最关键的部分

        //创建链表SnakeNode *createNode(SnakeNode *head,SnakeNode *data){SnakeNode *node;node=(SnakeNode *)malloc(sizeof(SnakeNode));// 将原始头结点链接到此结点后面,并将数据进行设置node->next = head;node->x = data->x;node->y=data->y;head = node;return head;}
    

    传入参数:

    • SnakeNode *head:头指针
    • SnakeNode *data:数据指针

    传入头指针并返回头指针,这里的头指针即程序一开始定义的currentNode(其实命名有点误解人),每次传入当前当前指针p(同上),将原始头结点链接到此结点后面,并将数据进行设置,这样每次运动的位置都保存在currentNode节点中了,节点头为当前位置


  • printSnake()-游戏结束

      Node=currentNode;for(i=0;i<b->length&&Node!=NULL;i++){if(Node->x==p->x&&Node->y==p->y){setPos(25,13);setColor(4,2);printf("Game Over!!\n");gameOver();}setPos(Node->x,Node->y);setColor(4,2);printf("■");Node=Node->next;}
    
  1. 通过蛇位置链表,打印链表中的内容,只打印b->length长度这么长的身体
  2. 如果某个节点等于当前节点(蛇要到自己)则结束游戏

  • gameOver()-游戏结束

  • continueGame()-游戏继续

    游戏结束后判断 重开游戏[空格] 还是退出游戏[ESC]
    如果重开游戏,则重新createSnake()重新while(1) 循环画地图、判断、蛇移动等

游戏截图:



总结

暂且先这么多,以后有空可能会继续完善

附GitHub:https://github.com/forcehack/C_snake

C语言实现贪吃蛇(使用链表,适合初学者)相关推荐

  1. linux贪吃蛇c程序,Linux环境下C语言实现贪吃蛇游戏

    Linux环境下C语言实现贪吃蛇游戏 [liul@test snake]$ more snake.c #include #include #include #include #include #def ...

  2. 安卓c语言自动补全软件吾爱,C语言实现贪吃蛇小游戏

    本文实例为大家分享了C语言实现贪吃蛇小游戏的具体代码,供大家参考,具体内容如下 一.程序实现的原理: 1.构造蛇身:定义一个坐标数组,存放的是蛇的每一节蛇身所在的坐标位置.这样就将移动蛇身的操作转换为 ...

  3. c语言安卓贪吃蛇代码下载,C语言贪吃蛇代码

    c语言编写贪吃蛇源代码,简单易懂,文件为VC源代码.如果你正在学习c语言,就来下载吧.很经典的 C语言贪吃蛇代码部分 #include #include #include#include #defin ...

  4. C语言实现贪吃蛇代码

    C语言实现贪吃蛇,有点难写啊. 我是用VS写的 代码里有注释 我就不多说了 大家也可以直接去下载我的可执行文件 #include<stdio.h> #include<time.h&g ...

  5. 贪吃蛇c语言经典程序下载,C语言实现贪吃蛇游戏(单人版)

    本文实例为大家分享了C语言实现贪吃蛇游戏的具体代码,供大家参考,具体内容如下 相比于第一个:贪吃蛇游戏,功能更加丰富 #include"snake.h" //蛇的移动 void m ...

  6. C语言实现贪吃蛇小游戏1.0

    C语言实现贪吃蛇小游戏1.0 贪吃蛇游戏要有三个东西:边框.蛇.食物 还有两个灵魂的东西:光标的移动与按键监控 一.光标的移动 在我看来在控制台上移动光标画图是实现这个小游戏的灵魂了,在这之前我一直以 ...

  7. 【笔记】用vs2017 c语言写 贪吃蛇 小游戏

    目录 文章目录 前言 一.贪吃蛇游戏 设计逻辑 二.详细介绍 1.设置界面(界面大小,背景颜色) 2.设置贪吃蛇的身体并显示 3.使之能够移动并能够穿墙 4.添加食物并让蛇吃掉 5.设置背景音乐 完整 ...

  8. c语言贪吃蛇游戏的论文,基于c语言的贪吃蛇游戏论文.doc

    基于c语言的贪吃蛇游戏论文 第 PAGE \* Arabic \* MERGEFORMAT 15 页第 PAGE \* Arabic \* MERGEFORMAT 15 页 学校代码 学号 分 类 号 ...

  9. c语言字符蛇代码,C语言实现贪吃蛇代码

    本文实例为大家分享了C语言实现贪吃蛇代码的具体代码,供大家参考,具体内容如下 #include"stdafx.h" #include #include #include #incl ...

最新文章

  1. [博客之路]如何增加一个博客的PR值(一)
  2. socket接收的消息怎么更新到页面_spring boot 集成 websocket 实现消息主动
  3. Python占位符总结:%方式和format方式
  4. BZOJ4403 序列统计—Lucas你好
  5. ASP.NET的IIS映射
  6. 手写 Promise.race
  7. python输入文本的缩写是什么_Python如何使用NLP从缩写文本中插入单词?
  8. 391 卡信乐卡盟程序
  9. web网页设计实例作业 我的家乡- 达州(4页) HTML+CSS+JavaScript dreamweaver作业静态HTML网页设计模板
  10. java 数据库异常,数据库常见异常
  11. c语言用循环转换单词首字母,用c++实现将文本每个单词首字母转换为大写
  12. 软件测试教学实训平台
  13. 黑暗森林法则和猜疑链同样存在人和人之间
  14. 谢烟客---------Linux之网络基础知识
  15. 用网线实现两台电脑之间的文件共享
  16. 服务器的ip端口加密协议混淆,Obfsproxy - 混淆/加密端口数据
  17. 从三室心脏MRI影像检测主动脉瓣病变
  18. ImportError: packaging>=20.0 is required for a normal functioning of this mo
  19. 爬爬爬!使用scrapy爬取你懂得的网站自建数据库!
  20. 财务报表分析:理论框架方法与案例

热门文章

  1. SpringBoot入门建站全系列(二十七)WebSocket做简单的聊天室
  2. 2022开年第一个爆款凭什么是山东蓝翔?如何实现破圈传播?
  3. vue项目的实践总结
  4. 【面试准备之】HR面试时100个关键问题
  5. 介绍部电影“Sicko”,迈克尔摩尔拍摄的抨击美国医疗体制的纪录片
  6. Ubuntu操作系统综合贴
  7. WAP版手机外卖订餐系统设计与实现(含论文)SSM
  8. Python入门神图一张
  9. 工业视觉需要时可抄的代码---持续更新
  10. 2017国家集训队作业[agc016e]Poor Turkey