c++游戏:贪吃蛇

一个使用c++制作的贪吃蛇

这个东西我只做了一个下午,有没有bug就不知道了
希望各位大佬能帮忙查查错

本游戏的“前置代码"

前置数据

//头文件
#include<bits/stdc++.h> 
#include<windows.h> 
#include<conio.h>
常量
#define _for(i,a,b) for(int i=a;i<b;i++) //重定义for
#define Sbody 136 //蛇身体,136是颜色代码
#define Shead 119 //蛇头,119也是颜色代码
#define stdfood 8 //食物分数
#define handle GetStdHandle(STD_OUTPUT_HANDLE) //标准输出句柄

前置函数

1. 控制台窗口初始化函数

去翻译了下 初始化 被译为了 initialize
∴ 这个函数长这样

void Initialize(){
	cursor(0); //把光标隐藏(下面有解释)
	system("color f0"); //用cmd里的color指令把控制台设为白底黑字
	RECT rect; 
	HWND hwnd=GetForegroundWindow(); //返回前台窗回的句柄
	GetWindowRect(hwnd,&rect); //得到窗口的位置
	//修改窗口的位置,这行代码就是将窗口移动到左上角(不改变窗口大小)
	MoveWindow(hwnd,0,0,rect.right-rect.left,rect.bottom-rect.top,TRUE); 
	system("mode con cols=90 lines=30"); //将窗口大小设定为宽90字符高30字符
}

2. 光标定位函数和颜色函数

void gotoxy(double x,int y){//光标定位
	COORD pos; //定义一个坐标
	pos.X=2*x; //设置X,因为一个中文是两个字符,所以*2
	pos.Y=y; //设置Y
	SetConsoleCursorPosition(handle,pos); //设置坐标位置
}
void color(int a){//颜色
	SetConsoleTextAttribute(handle,a);//修改
}

3. 延迟输出函数

void print(const char a[],int interval){ //传入文字和输出间隔(ms)
	int len=strlen(a); //获得文字长度
	_for(i,0,len){ //循环输出
		printf("%c",a[i]); 
		Sleep(interval); //暂停interval时间,Sleep来自于Window.h,可以把函数暂停一会儿(ms)
	}
}

4. 光标显示与隐藏

因为运行时还有一个光标在闪闪闪看着不舒服,所以就有了这么一个东西

void cursor(bool a){
	CONSOLE_CURSOR_INFO CursorInfo;//创建一个光标变量
	GetConsoleCursorInfo(handle, &CursorInfo);//获取光标
	CursorInfo.bVisible = a;//将光标显示状态设为 a
	SetConsoleCursorInfo(handle, &CursorInfo);//设置光标
}

5. enter()

这玩意说白了就是个广告


void enter(){
	print("本游戏由 AichiStudio爱知工作室(http://aichistudio.space) 制作",50);
	Sleep(1000);
}

这太容易了,就不解释了,反正也就是个广告

功能代码

蛇和食物的数据定义

struct Snake{//蛇身体的结构体
	int direction;//方向,这一段身体运动的方向
	int x,y;//这一段身体在的位置
	int type;//身体的种类,就是之前的颜色(为了省事)
}snake[500];//蛇
int slong,foodx,foody,foodsize=stdfood,score; //蛇的长度,食物的X,Y坐标和大小(分数),玩家的得分
int pmap[30][30];//后面用来判断的数组
Snake setSnake(int x,int y,int type){//创建蛇的身体
	Snake t;//定义一个临时蛇
	t.x=x;//赋值X
	t.y=y;//赋值Y
	t.type=type;//赋值种类
	t.direction=0;//设置方向
	return t;//回传
}

蛇的移动

蛇的移动就是把每一节身体都往这节身体的方向移动一格,然后上一节把自己的方向传递给下一节;碰到边缘就进行传送

void snake_move(){
	for(int i=slong-1;i>=0;i--){//这里要倒着循环,这样就会先移动后面的在进行方向传递
		if(snake[i].direction!=0){//方向不等等于0,就是它不是停在原地不动的
			//首先清空之前输出的身体,这样直接定位到这身体的坐标再清空就不会导致闪屏
			gotoxy(snake[i].x,snake[i].y);color(0);printf("■");
			if(snake[i].direction==1){//方向为1,向左
				if(snake[i].x>0)snake[i].x--;//它不是贴着左边界的就直接x--
				else snake[i].x=24;//如果贴着左边界就传送,让它移动到右边界
			}else if(snake[i].direction==2){//方向为2,向右
				if(snake[i].x<24)snake[i].x++;//它不是贴着右边界的就x++
				else snake[i].x=0;//如果贴着右边界就传送,让它移动到左边界
			}else if(snake[i].direction==3){//方向为3,向上
				if(snake[i].y>0)snake[i].y--;//它不是贴着上边界的就y--
				else snake[i].y=24;//如果贴着上边界就传送,让它移动到下边界
			}else if(snake[i].direction==4){//方向为4,向下
				if(snake[i].y<24)snake[i].y++;//如果不是贴着下边界就y++
				else snake[i].y=0;//如果贴着下边界就传送,让它移动到上边界
			}
			//方向传递,这节身体的方向和下一节身体方向不同且自己不是最后一节身体时才进行传递
			if(snake[i].direction!=snake[i+1].direction&&i+1<slong){
				snake[i+1].direction=snake[i].direction;//将后面一节身体的方向设为自己的方向
			}
		}
	}
	snake_testing();//进行检测
}

检测蛇

这个函数检测蛇的状态,包括是否咬到自己,是否吃到食物

void snake_testing(){
	memset(pmap,0,sizeof(pmap));//清空地图
	//绘制地图,蛇的身体所在的位置设为1,其余默认为0,注意:绘制时不要绘制蛇头
	_for(i,1,slong){
		pmap[snake[i].y][snake[i].x]=1;
	}
	if(pmap[snake[0].y][snake[0].x]==1)gameover();//如果蛇头的位置上有一节身体那么游戏结束
	if(snake[0].y==foody&&snake[0].x==foodx){//蛇头的坐标和食物坐标相同,吃到食物
		snake[slong]=setSnake(snake[slong-1].x,snake[slong-1].y,Sbody);//添加一节身体
		slong++;//蛇的长度增加
		createFood();//重新构造食物
		score+=foodsize;//分数增加
	}
	game_print();//绘制
}

绘制

void game_print(){
	color(4);//红色,食物的颜色
	gotoxy(foodx,foody);printf("●");//绘制食物
	_for(i,0,slong){//循环身体
		color(snake[i].type);//将颜色设为这节身体的颜色
		gotoxy(snake[i].x,snake[i].y);printf("■");//光标移动到身体的位置,绘制
	}
	color(240);//还原颜色,白底黑字
	gotoxy(28,5);printf("SCORE: %d",score);//打印分数
}

构造食物

构造食物就是随机生成食物坐标,并确定它不在蛇身上

void createFood(){
	memset(pmap,0,sizeof(pmap));//清空地图
	_for(i,0,slong){//记录蛇的位置
		pmap[snake[i].y][snake[i].x]=1;
	}
	srand(time(NULL));//随机数种子
	foody=rand()%25;//生成X
	srand(rand());
	foodx=rand()%25;//生产Y
	while(pmap[foody][foodx]==1){//循环生成直到食物不在蛇身上
		//如果在,那么重新生成
		srand(rand());
		foody=rand()%25;
		srand(rand());
		foodx=rand()%25;
	}
	return;//生成成功就结束函数
}

gameover()函数

void gameover(){
	system("cls&color f0");//清屏
	//制作界面
	gotoxy(19,10);printf("GAME OVER");
	if(score<100){gotoxy(19,12);printf("SCORE: %d",score);}//分数只有两位就放在中间输出
	else if(score<1000){gotoxy(19,12);printf("SCORE:%d",score);}//有三位就删去一个空格
	else if(score<10000){gotoxy(18,12);printf("SCORE: %d",score);}//如果有四位就...
	gotoxy(19,14);printf("按 R 重玩");
	//就是把分数放在中间
	while(_kbhit());//将积累的按键事件清空
	char k=getch();//暂停并读取一个键值(可以理解为请按任意键退出)
	if((k=='r'||k=='R')){//如果按的是 R 就重启游戏
		slong=0,foodx=0,foody=0,foodsize=8,score=0;
		main_interface();
	}
	exit(0);//退出
}

主界面

void main_interface(){
	system("cls&title 贪吃蛇c++版");//清屏并设置标题
	//构造界面,这里不多解释
	_for(i,0,7){gotoxy(27,i);printf("|");}
	gotoxy(27, 7);printf("|        W上");
	gotoxy(27, 8);printf("| ");
	gotoxy(27, 9);printf("|   A左  S下  D右");
	gotoxy(27,10);printf("| ");
	gotoxy(27,11);printf("|   Q暂停   R重玩");
	gotoxy(27,12);printf("| ");
	gotoxy(27,13);printf("|   F打开Aichi网站");
	_for(i,14,26){gotoxy(27,i);printf("|");}
	//构造地图
	color(0);
	_for(i,0,25){
		_for(j,0,25){
			gotoxy(j,i);printf("■"); 
		}
	}
	color(240);
	int running=0;//计时器
	bool started=0,turning=0;//是否开始和是否正在转向
	//定义头和两节身体
	snake[slong++]=setSnake(12,12,Shead);
	snake[slong++]=setSnake(12,12,Sbody);
	snake[slong++]=setSnake(12,12,Sbody);
	createFood();//构造食物
	while(_kbhit());//清空累积的键盘事件
	while(1){//循环
		if(running==3){//到150ms时
			if(started){//如果已开始
				snake_move();//移动蛇
				turning=0;//转向成功
			}
			running=0;//清空计时器(怕爆int)
		}
		if(_kbhit()){//如果键盘被按下
			char k=_getch();//获取键值
			if((k=='a'||k=='A')&&snake[0].direction!=2&&!turning){
				//如果 A 被按下并且不在转向,不是方向从右直接转到左,那么向左转
				snake[0].direction=1;//转向
				started=1;//表示已经开始
				turning=1;//正在转向
			}else if((k=='d'||k=='D')&&snake[0].direction!=1&&!turning){
				//如果 D 被按下并且不在转向,不是方向从左直接转到右,那么向右转
				snake[0].direction=2;//转向
				started=1;//表示已经开始
				turning=1;//正在转向
			}else if((k=='w'||k=='W')&&snake[0].direction!=4&&!turning){
				//如果 W 被按下并且不在转向,不是方向从下直接转到上,那么往上转
				snake[0].direction=3;//转向
				started=1;//表示已经开始
				turning=1;//正在转向
			}else if((k=='s'||k=='S')&&snake[0].direction!=3&&!turning){
				//如果 S 被按下并且不在转向,不是方向从上直接转到下,那么往下转
				snake[0].direction=4;//转向
				started=1;//表示已经开始
				turning=1;//正在转向
			}else if((k=='q'||k=='Q'))//如果 Q 被按下,那么 暂停 或 继续
				started=!started;//将状态切换
			else if((k=='f'||k=='F')){//如果 F 被按下
				system("explorer.exe http://aichistudio.space");//打开网站
				started=0;//暂停
			}else if((k=='r'||k=='R')){//如果 R 被按下
				//重启游戏
				slong=0,foodx=0,foody=0,foodsize=8,score=0;
				main_interface();
			}
		}
		Sleep(50);//暂停50ms
		running++;
	}
}

主函数

int main(){
	Initialize();//初始化
	enter();//启动界面
	main_interface();//主界面
	return 0;
}

完整代码

#include<bits/stdc++.h> 
#include<windows.h> 
#include<conio.h>
#define _for(i,a,b) for(int i=a;i<b;i++) //重定义for
#define Sbody 136 //蛇身体,136是颜色代码
#define Shead 119 //蛇头,119也是颜色代码
#define stdfood 8 //食物分数
#define handle GetStdHandle(STD_OUTPUT_HANDLE) //标准输出句柄
using namespace std;
//-----------数据定义----------
struct Snake{//蛇身体的结构体
	int direction;//方向,这一段身体运动的方向
	int x,y;//这一段身体在的位置
	int type;//身体的种类,就是之前的颜色(为了省事)
}snake[500];//蛇
int slong,foodx,foody,foodsize=stdfood,score; //蛇的长度,食物的X,Y坐标和大小(分数),玩家的得分
int pmap[30][30];//后面用来判断的数组
Snake setSnake(int x,int y,int type){//创建蛇的身体
	Snake t;//定义一个临时蛇
	t.x=x;//赋值X
	t.y=y;//赋值Y
	t.type=type;//赋值种类
	t.direction=0;//设置方向 
	return t;//回传
}
//----------函数定义---------
void gotoxy(double x,int y);
void color(int a);
void print(const char a[],int interval);
void main_interface();
void enter();
void Initialize();
void cursor(bool a);
void snake_move();
void game_print();
void snake_testing();
void gameover();
Snake setSnake(int x,int y,int type);
void createFood();
//----------函数书写---------
void cursor(bool a){
	CONSOLE_CURSOR_INFO CursorInfo;//创建一个光标变量
	GetConsoleCursorInfo(handle, &CursorInfo);//获取光标
	CursorInfo.bVisible = a;//将光标显示状态设为 a
	SetConsoleCursorInfo(handle, &CursorInfo);//设置光标
}
void Initialize(){
	cursor(0); //把光标隐藏(下面有解释)
	system("color f0"); //用cmd里的color指令把控制台设为白底黑字
	RECT rect; 
	HWND hwnd=GetForegroundWindow(); //返回前台窗回的句柄
	GetWindowRect(hwnd,&rect); //得到窗口的位置
	//修改窗口的位置,这行代码就是将窗口移动到左上角(不改变窗口大小)
	MoveWindow(hwnd,0,0,rect.right-rect.left,rect.bottom-rect.top,TRUE); 
	system("mode con cols=90 lines=30"); //将窗口大小设定为宽90字符高30字符
}
void gotoxy(double x,int y){//光标定位
	COORD pos; //定义一个坐标
	pos.X=2*x; //设置X,因为一个中文是两个字符,所以*2
	pos.Y=y; //设置Y
	SetConsoleCursorPosition(handle,pos); //设置坐标位置
}
void color(int a){//颜色
	SetConsoleTextAttribute(handle,a);//修改
}
void print(const char a[],int interval){ //传入文字和输出间隔(ms)
	int len=strlen(a); //获得文字长度
	_for(i,0,len){ //循环输出
		printf("%c",a[i]); 
		Sleep(interval); //暂停interval时间,Sleep来自于Window.h,可以把函数暂停一会儿(ms)
	}
}
void enter(){
	print("本游戏由 AichiStudio爱知工作室(http://aichistudio.space) 制作",50);
	Sleep(1000);
}
void snake_move(){
	for(int i=slong-1;i>=0;i--){//这里要倒着循环,这样就会先移动后面的在进行方向传递
		if(snake[i].direction!=0){//方向不等等于0,就是它不是停在原地不动的
			//首先清空之前输出的身体,这样直接定位到这身体的坐标再清空就不会导致闪屏
			gotoxy(snake[i].x,snake[i].y);color(0);printf("■");
			if(snake[i].direction==1){//方向为1,向左
				if(snake[i].x>0)snake[i].x--;//它不是贴着左边界的就直接x--
				else snake[i].x=24;//如果贴着左边界就传送,让它移动到右边界
			}else if(snake[i].direction==2){//方向为2,向右
				if(snake[i].x<24)snake[i].x++;//它不是贴着右边界的就x++
				else snake[i].x=0;//如果贴着右边界就传送,让它移动到左边界
			}else if(snake[i].direction==3){//方向为3,向上
				if(snake[i].y>0)snake[i].y--;//它不是贴着上边界的就y--
				else snake[i].y=24;//如果贴着上边界就传送,让它移动到下边界
			}else if(snake[i].direction==4){//方向为4,向下
				if(snake[i].y<24)snake[i].y++;//如果不是贴着下边界就y++
				else snake[i].y=0;//如果贴着下边界就传送,让它移动到上边界
			}
			//方向传递,这节身体的方向和下一节身体方向不同且自己不是最后一节身体时才进行传递
			if(snake[i].direction!=snake[i+1].direction&&i+1<slong){
				snake[i+1].direction=snake[i].direction;//将后面一节身体的方向设为自己的方向
			}
		}
	}
	snake_testing();//进行检测
}
void snake_testing(){
	memset(pmap,0,sizeof(pmap));//清空地图
	//绘制地图,蛇的身体所在的位置设为1,其余默认为0,注意:绘制时不要绘制蛇头
	_for(i,1,slong){
		pmap[snake[i].y][snake[i].x]=1;
	}
	if(pmap[snake[0].y][snake[0].x]==1)gameover();//如果蛇头的位置上有一节身体那么游戏结束
	if(snake[0].y==foody&&snake[0].x==foodx){//蛇头的坐标和食物坐标相同,吃到食物
		snake[slong]=setSnake(snake[slong-1].x,snake[slong-1].y,Sbody);//添加一节身体
		slong++;//蛇的长度增加
		createFood();//重新构造食物
		score+=foodsize;//分数增加
	}
	game_print();//绘制
}
void game_print(){
	color(4);//红色,食物的颜色
	gotoxy(foodx,foody);printf("●");//绘制食物
	_for(i,0,slong){//循环身体
		color(snake[i].type);//将颜色设为这节身体的颜色
		gotoxy(snake[i].x,snake[i].y);printf("■");//光标移动到身体的位置,绘制
	}
	color(240);//还原颜色,白底黑字
	gotoxy(28,5);printf("SCORE: %d",score);//打印分数
}
void createFood(){
	memset(pmap,0,sizeof(pmap));//清空地图
	_for(i,0,slong){//记录蛇的位置
		pmap[snake[i].y][snake[i].x]=1;
	}
	srand(time(NULL));//随机数种子
	foody=rand()%25;//生成X
	srand(rand());
	foodx=rand()%25;//生产Y
	while(pmap[foody][foodx]==1){//循环生成直到食物不在蛇身上
		//如果在,那么重新生成
		srand(rand());
		foody=rand()%25;
		srand(rand());
		foodx=rand()%25;
	}
	return;//生成成功就结束函数
}
void gameover(){
	system("cls&color f0");//清屏
	//制作界面
	gotoxy(19,10);printf("GAME OVER");
	if(score<100){gotoxy(19,12);printf("SCORE: %d",score);}//分数只有两位就放在中间输出
	else if(score<1000){gotoxy(19,12);printf("SCORE:%d",score);}//有三位就删去一个空格
	else if(score<10000){gotoxy(18,12);printf("SCORE: %d",score);}//如果有四位就...
	gotoxy(19,14);printf("按 R 重玩");
	//就是把分数放在中间
	while(_kbhit());//将积累的按键事件清空
	char k=getch();//暂停并读取一个键值(可以理解为请按任意键退出)
	if((k=='r'||k=='R')){//如果按的是 R 就重启游戏
		slong=0,foodx=0,foody=0,foodsize=8,score=0;
		main_interface();
	}
	exit(0);//退出
}
void main_interface(){
	system("cls&title 贪吃蛇c++版");//清屏并设置标题
	//构造界面,这里不多解释
	_for(i,0,7){gotoxy(27,i);printf("|");}
	gotoxy(27, 7);printf("|        W上");
	gotoxy(27, 8);printf("| ");
	gotoxy(27, 9);printf("|   A左  S下  D右");
	gotoxy(27,10);printf("| ");
	gotoxy(27,11);printf("|   Q暂停   R重玩");
	gotoxy(27,12);printf("| ");
	gotoxy(27,13);printf("|   F打开Aichi网站");
	_for(i,14,26){gotoxy(27,i);printf("|");}
	//构造地图
	color(0);
	_for(i,0,25){
		_for(j,0,25){
			gotoxy(j,i);printf("■"); 
		}
	}
	color(240);
	int running=0;//计时器
	bool started=0,turning=0;//是否开始和是否正在转向
	//定义头和两节身体
	snake[slong++]=setSnake(12,12,Shead);
	snake[slong++]=setSnake(12,12,Sbody);
	snake[slong++]=setSnake(12,12,Sbody);
	createFood();//构造食物
	game_print();
	while(_kbhit());//清空累积的键盘事件
	while(1){//循环
		if(running==3){//到150ms时
			if(started){//如果已开始
				snake_move();//移动蛇
				turning=0;//转向成功
			}
			running=0;//清空计时器(怕爆int)
		}
		if(_kbhit()){//如果键盘被按下
			char k=_getch();//获取键值
			if((k=='a'||k=='A')&&snake[0].direction!=2&&!turning){
				//如果 A 被按下并且不在转向,不是方向从右直接转到左,那么向左转
				snake[0].direction=1;//转向
				started=1;//表示已经开始
				turning=1;//正在转向
			}else if((k=='d'||k=='D')&&snake[0].direction!=1&&!turning){
				//如果 D 被按下并且不在转向,不是方向从左直接转到右,那么向右转
				snake[0].direction=2;//转向
				started=1;//表示已经开始
				turning=1;//正在转向
			}else if((k=='w'||k=='W')&&snake[0].direction!=4&&!turning){
				//如果 W 被按下并且不在转向,不是方向从下直接转到上,那么往上转
				snake[0].direction=3;//转向
				started=1;//表示已经开始
				turning=1;//正在转向
			}else if((k=='s'||k=='S')&&snake[0].direction!=3&&!turning){
				//如果 S 被按下并且不在转向,不是方向从上直接转到下,那么往下转
				snake[0].direction=4;//转向
				started=1;//表示已经开始
				turning=1;//正在转向
			}else if((k=='q'||k=='Q'))//如果 Q 被按下,那么 暂停 或 继续
				started=!started;//将状态切换
			else if((k=='f'||k=='F')){//如果 F 被按下
				system("explorer.exe http://aichistudio.space");//打开网站
				started=0;//暂停
			}else if((k=='r'||k=='R')){//如果 R 被按下
				//重启游戏
				slong=0,foodx=0,foody=0,foodsize=8,score=0;
				main_interface();
			}
		}
		Sleep(50);//暂停50ms
		running++;
	}
}
int main(){
	Initialize();//初始化
	enter();//启动界面
	main_interface();//主界面
	return 0;
}

From AichiStudio爱知工作室,
网站:http://aichistudio.space
QQ交流群:1032103456

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值