当前位置:网站首页>贪吃蛇小游戏——C语言

贪吃蛇小游戏——C语言

2022-08-09 07:24:00 哈希疯

效果演示
在这里插入图片描述
Snach.c文件

#include <stdlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <time.h>
#include "consfun.h"
#include "config.h"

#define NR(x) (sizeof(x)/sizeof(x[0]+0)) /*计算数组长度*/
#define TITLE "**贪吃蛇游戏**"
#define AUTHOR "作者:张某某"
#define DATE "日期:2020年06月10日"
#define WIDTH 54 /*地图宽度*/
#define HEIGHT WIDTH/2 /*地图高度*/
#define SHead "◆" /*蛇头*/
#define SBody "■" /*蛇身体*/


/*定义一条蛇,蛇头方向RIGHT,生命值1,长度0,分数0*/
Snake S;

/*定义食物*/
Point Food;

/*菜单项*/
const char *menu[] = {
    "*开始游戏*","*游戏难度*","*退出游戏*"};
/*游戏难度*/
const char *rank[] = {
    "初级","中级","高级"};
/*游戏难度标记*/
int lev = 0;

/*打印字符到指定位置*/
int Print(int x, int y,const char str[],short color){
    
	gotoxy(x,y);		/*设置光标位置*/
	textcolor(color);	/*设置前景颜色*/
	printf("%s",str);
	return 0;
}

/*显示菜单*/
int showMenu(){
    
	int i = 0,size = NR(menu),ret = 0, ind = 0;
	consinit(70, 30);
	Print((WIDTH-strlen(TITLE))/2,0,TITLE,2);			/*打印标题*/
	Print(WIDTH+1,0,"游戏难度:",2);						/*打印游戏难度*/
	Print(WIDTH+11,0,rank[lev],2);
	Print((WIDTH-strlen(AUTHOR))/2,HEIGHT-4,AUTHOR,2);	/*打印作者*/
	Print((WIDTH-strlen(DATE))/2,HEIGHT-3,DATE,2);		/*打印日期*/
	Print(WIDTH,HEIGHT + 1,"按Esc键退出游戏",2);
	while(1){
    											/*控制菜单选择*/
		for(i = 0;i < size;i++)							/*循环打印菜单项*/
			Print( (WIDTH-strlen(menu[i]))/2, (HEIGHT-size)/2+i,menu[i], i == ind ? 5 : 2 );
		while((ret = _getch()) != ESC && ret != UP && ret != DOWN && ret != ENTER);/*控制用户输入*/
		ret == UP && ind > 0 ? ind -= 1 : ret == DOWN && ind < size - 1 ? ind += 1 : 0;/*控制选中索引的变化*/
		if(ret == ENTER){
    /*判断在哪个选项上按了【回车】*/
			switch(ind){
    
				case 0:{
    							/*开始游戏*/
					/*显示地图*/
					showMap();
					if (!SnakeMove(&S)){
    /*返回值为0是提示游戏结束!*/
						Print(WIDTH/2-5,HEIGHT/2,"Game Over!",12);
						while((ret = _getch()) != ESC);/*控制用户输入*/
					}
					break ;
				}
				case 1:{
    							/*游戏难度*/
					/*循环变化难度等级*/
					lev = (lev + 1) % NR(rank);
					/*打印难度等级*/
					Print(WIDTH+11,0,rank[lev],2);
					break ;
				}
				case 2:		exit(0);				/*退出游戏*/
			}
		}else if(ret == ESC) exit(0);			/*按ESC键退出游戏*/
	}
	return 0;
}

/*创建地图*/
void showMap(){
    
	char str[20] = {
    0};
	int i,j;
	clrscr();
	Print(WIDTH+1,0, "游戏难度:",2);
	Print(WIDTH+1,2, "游戏时间:",2);
	Print(WIDTH+1,4, "蛇生命值:",2);
	Print(WIDTH+1,6, "当前得分:",2);
	Print(WIDTH,HEIGHT+1,"按Esc键返回菜单",2);
	Print(WIDTH+11,0,rank[lev],2);
	Print(WIDTH+11,2,"0",2);/*游戏时间*/
	Print(WIDTH+11,4,"1",2);/*蛇生命值*/
	Print(WIDTH+11,6,"0",2);/*当前得分*/
	for(i=0;i<WIDTH;++i)/*打印地图边界*/
		for(j=0;j<HEIGHT;++j)
			if(i==0||j==0||i==WIDTH-1||j==HEIGHT-1)
				Print(i,j,"#",2);
	InitSnake(&S,2);/*初始化蛇*/
	randFood(&S);/*随机生成食物*/
}

/*初始化蛇*/
void InitSnake(Snake *ps,const int len){
    
	Node *p;
	int i = 0;
	ps->hp = 1;/*初始生命值*/
	ps->len = 0;/*蛇长度清零*/
	ps->score = 0;/*当前得分*/
	ps->sym = RIGHT;/*蛇头方向*/
	ps->head = NULL;
	for(;i < len; i++){
    
		if((p = (Node*)malloc(sizeof(Node))) == NULL){
    /*判断是否成功分配内存*/
			Print(0,0,"分配内存失败,按任意键退出程序!",2);
			system("pause>nul");
			exit(0);
        }
		/*新节点坐标*/
		p->pos.x = ((WIDTH + len * 2) / 2 - (len - i) * 2) | 1; /* x坐标必须取奇数 */
		p->pos.y = HEIGHT / 2;
		/*采用头插法*/
		p->next = ps->head;
		ps->head = p;
		/*蛇长加1*/
		ps->len++;
		/*打印节点到屏幕*/
		Print(p->pos.x,p->pos.y,SBody,2);
	}
	Print(ps->head->pos.x,ps->head->pos.y,SHead,2);
}

/*随机生成食物*/
void randFood(const Snake *s){
    
	int x, y, k = 1;
	Node *p = s->head;/*创建指向蛇头的节点*/
	while(p != NULL && k){
    
		/*在地图边界范围内随机生成坐标*/
		x = (rand() % (WIDTH - 3) + 1) | 1;/* x坐标必须取奇数 */
		y = rand() % (HEIGHT - 2) + 1;
		while(p != NULL && k)/*判断随机坐标是否与蛇身重叠*/
			p->pos.x == x && p->pos.y == y ? k = 0 : (int)(p = p->next);
		k = !k;
	}
	Food.x = x;		Food.y = y;
	/*打印食物到屏幕*/
	Print(x,y,SBody,9);
}

/*控制蛇的移动*/
int SnakeMove(Snake *s){
    
	int vec = 0,i = 0,j;
	char key,str[10];
	DWORD old;
	long time;
	while(j = 1){
    
		if(_kbhit()){
    
			key = _getch();
			/*按蛇头方向屏蔽指定方向键*/
			if(s->sym == UP && key == DOWN)         continue;
			else if(s->sym == DOWN && key == UP)    continue;
			else if(s->sym == LEFT && key == RIGHT) continue;
			else if(s->sym == RIGHT && key == LEFT) continue;
			switch(key){
    /*如果按的是方向键,则改变蛇头方向*/
				case UP   : s->sym = UP   ;	break;
				case DOWN : s->sym = DOWN ;	break;
				case LEFT : s->sym = LEFT ;	break;
				case RIGHT: s->sym = RIGHT;	break;
				case ESC  : return 1;
				default   : continue;
			}
			vec = 1;
			/*蛇碰撞后生命-1*/
			if(!ChangeSnackXY(s)){
    
				s->hp -= 1;
				_itoa_s((int)s->hp,str,sizeof(str)-1,10);
				Print(WIDTH+11,4,str,2);/*蛇生命值*/
				Print(0,HEIGHT,"撞到障碍物,生命值-1!",12);
			}
		}
		if(vec != 0){
    
			vec = (lev == 0 ? 300 : (lev == 1 ? 150 : 30));
			time = GetTickCount() - old;/*计算开始游戏时间*/
			_itoa_s((int)(time / 1000),str,sizeof(str)-1,10);
			Print(WIDTH+11,2,str,2);/*打印游戏时间*/
			if((time / vec) % 2){
    /*控制移动速度*/
				/*蛇碰撞后生命-1*/
				if(i-- == 0 && !(ChangeSnackXY(s))){
    /*用i保证ChangeSnackXY(s)在有效时间内只运行一次*/
					s->hp -= 1;
					_itoa_s((int)s->hp,str,sizeof(str)-1,10);
					Print(WIDTH+11,4,str,2);/*蛇生命值*/
					Print(0,HEIGHT,"撞到障碍物,生命值-1!",12);
				}
			}else
				i = 0;
		}else{
    
			old = GetTickCount();
		}
		if((int)s->hp <= 0) return 0;/*生命值小于或等于0时,函数返回0*/
	}
	return 1;
}

/*更新蛇位置*/
int ChangeSnackXY(Snake *s){
    
	int x = s->head->pos.x, y = s->head->pos.y;
	Node *p = s->head->next;
	int vx = 0, vy = 0;
	switch(s->sym){
    
		case UP   : vy = -1; break;
		case DOWN : vy = 1;  break;
		case LEFT : vx = -2; break;
		case RIGHT: vx = 2;  break;
	}
	vx += x;  vy += y;
	if(vx <  1 || vx >= WIDTH - 2 || vy == 0 || vy >= HEIGHT - 1) return 0;/*蛇头碰到边界返回0*/
	while(p != NULL){
    /*蛇头碰到蛇身返回0*/
		if(p->pos.x == vx && p->pos.y == vy)
			return 0;
		else
			p = p->next;
	}
	if(vx == Food.x && vy == Food.y){
    /*吃到食物蛇长加1*/
		SAdd(s);
		return 1;
	}
	p = s->head->next;/*重新指向蛇头的下一个节点*/
	s->head->pos.x = vx;/*修改蛇头坐标*/
	s->head->pos.y = vy;
	Print(x,y,SBody,2);/*旧蛇头改为身体*/
	Print(vx,vy,SHead,2);/*重新蛇头位置*/
	while(p != NULL){
    /*循环更新蛇每个节点的坐标*/
		vx = p->pos.x;
		vy = p->pos.y;
		p->pos.x = x;
		p->pos.y = y;
		x = vx; y = vy;
		p = p->next;
	}
	Print(x,y," ",2);/*抹除旧蛇尾位置字符*/
	Sleep(30);/*休眠30毫秒*/
	return 1;
}

/*吃食物后蛇长加1*/
void SAdd(Snake *s){
    
	char str[20] = {
    0};
	Node *p;
	if((p = (Node*)malloc(sizeof(Node))) == NULL){
    /*判断是否成功分配内存*/
		Print(0,0,"分配内存失败,按任意键退出程序!",2);
		system("pause>nul");
		exit(0);
	}
	Print(s->head->pos.x,s->head->pos.y,SBody,2);/*将旧蛇头坐标位置改为身体字符*/
	p->pos.x = Food.x;/*将食物作为新蛇头*/
	p->pos.y = Food.y;
	p->next = s->head;
	s->head = p;
	s->len++;
	s->score++;
	s->hp += 0.1f;
	_itoa_s((int)s->hp,str,sizeof(str)-1,10);
	Print(WIDTH+11,4,str,2);/*更新蛇生命值*/
	_itoa_s(s->score,str,sizeof(str)-1,10);
	Print(WIDTH+11,6,str,2);/*更新当前得分*/
	Print(p->pos.x,p->pos.y,SHead,2);/*更新蛇头位置*/
	Print(0,HEIGHT,"吃到食物,蛇长+1! ",12);
	randFood(s);/*随机生成食物*/
}

int main(){
    
	SetConsoleTitleA("贪吃蛇游戏");
	/*定义随机数种子*/
	srand((unsigned)time(NULL));
	showMenu();
	return 0;
}

consfun.h——WINDOWS命令窗口编程常用功能(网上找的,非原创)

#include <windows.h>
#include <wincon.h>

void consinit(short x,short y);
void gotoxy(int,int);
void clrscr(void);
void textcolor(short newcolor);
void textbackground(short newcolor);
void textattr(short newattr);

/* 功能:初始化命令窗口,设置窗口的行列数 参数:y-行数,x-列数 */
void consinit(short x, short y)
{
      
    COORD scr={
    x,y};
    CONSOLE_SCREEN_BUFFER_INFO info;
	SMALL_RECT rect={
    0,0,x-1,y-1};
	HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
	GetConsoleScreenBufferInfo(stdOut,&info);	//获取命令窗口信息
	//如果原屏缓冲区小必须先设置新的缓冲区大小,再调整窗口大小
	if(info.dwSize.X*info.dwSize.Y<x*y)			
	{
    	SetConsoleScreenBufferSize(stdOut,scr);
		SetConsoleWindowInfo(stdOut,TRUE,&rect);//缓冲区比需要的小时会出错
	}
	else
	{
    	SetConsoleWindowInfo(stdOut,TRUE,&rect);
		SetConsoleScreenBufferSize(stdOut,scr);	//设置值比窗口区域需要的小时会出错
	}
 	gotoxy(0,0);

}


/* 设置光标位置:使后续的输出从屏幕的x行y列输出 参数:x-行(从0开始) y-列(从0开始) */
void gotoxy(int x, int y)
{
    
    COORD Position;
	CONSOLE_CURSOR_INFO cci;
	Position.X=x; Position.Y=y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),Position);
	GetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci); /*获取光标信息*/
    cci.dwSize = 1; 	cci.bVisible =  0;	/*设置光标大小和可见参数*/
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);	/*设置(应用)光标信息*/
}

/* 清屏:用当前的背景颜色清除屏幕,并将光标定位在左上角(0,0) 参数:无 */
void clrscr(void)
{
    
    COORD scr={
    0,0};
	DWORD num;
	HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_SCREEN_BUFFER_INFO info;
    GetConsoleScreenBufferInfo(stdOut,&info);
	num=info.dwMaximumWindowSize.X*info.dwMaximumWindowSize.Y;
    FillConsoleOutputAttribute(stdOut,(WORD)info.wAttributes,num,scr,&num);
	FillConsoleOutputCharacter(stdOut,' ',num,scr,&num);
	gotoxy(0,0);
}

/* 设置字体颜色:使以后的输出字体前景颜色改为给定值 参数:newcolor-新的颜色值 颜色取值范围为:0x00 ~ 0x0f */
void textcolor(short newcolor)
{
    
    HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_SCREEN_BUFFER_INFO info;
    GetConsoleScreenBufferInfo(stdOut,&info);
	SetConsoleTextAttribute(stdOut, (WORD)(newcolor|info.wAttributes & 0xF0));
}

/* 设置背景颜色:使以后的输出的背景颜色改为给定值 参数:newcolor-新的颜色值 颜色取值范围为:0x00 ~ 0x0f */
void textbackground( short newcolor)
{
    
    HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_SCREEN_BUFFER_INFO info;
	GetConsoleScreenBufferInfo(stdOut,&info);
	SetConsoleTextAttribute(stdOut,(WORD)((newcolor<<4)|(info.wAttributes&0x0F)));
}

/* 设置文本属性:同时设定前景和背景颜色 参数:newattr-新的属性值 颜色取值范围为:0x00 ~ 0xff,低4位为前景颜色,高4位为背景颜色 */
void textattr(short newattr)
{
    
    HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(stdOut,newattr);
}

config.h——包含了蛇的数据结构、常量及相关函数声明

/*定义实体类型标记*/
enum {
    
	EMPTY ,		/*空*/
	FOOD ,		/*食物*/
	SNACK ,		/*蛇*/
};
/*定义Keyboard的键值数据 */
enum {
    
	UP = 72 ,
	DOWN = 80 ,
	LEFT = 75 ,
	RIGHT = 77 ,
	ENTER = 13 ,
	ESC = 27 ,
};

/*定义坐标结构类型*/
typedef struct {
    
    short x,y;
}Point;

/*定义节点结构类型*/
typedef struct node{
    
    Point pos;  
    struct node *next;  
}Node;

/*定义蛇结构类型*/
typedef struct snake{
    
    Node *head;
	short sym;	/*蛇头方向*/
	float hp;	/*生命值*/
    short len;	/*长度*/
	int score;/*分数*/
}Snake;	/*指向一条蛇*/

/*打印字符到指定位置*/
int Print(int,int,const char[],short);

/*显示菜单*/
int showMenu();

/*创建地图*/
void showMap();

/*初始化蛇,并打印到屏幕*/
void InitSnake(Snake*,const int);

/*随机生成食物,并打印到屏幕*/
void randFood(const Snake *);

/*控制蛇的移动*/
int SnakeMove(Snake *);

/*更新蛇位置*/
int ChangeSnackXY(Snake *);

/*吃食物后蛇长加1*/
void SAdd(Snake *);
原网站

版权声明
本文为[哈希疯]所创,转载请带上原文链接,感谢
https://blog.csdn.net/yu57955/article/details/118500341