贪吃蛇DIY


在这里插入图片描述
你觉得大学学校会决定未来的出路吗?

贪吃蛇

贪吃蛇是我们的经典游戏,所以让我们来做一条贪吃蛇吧
主要技术点:1,括号检测
2.
括号检测
1.这个会用到栈,我们遍历字符串,如果遇到左括号,其他忽略,我们就入栈;
2.继续遍历,如果遇到右括号,我们就把他们从出栈,然后判断一下是不是一对括号;
3.最后大总结判断,判断一下栈是否为空,如果为空代表成双成对,否则 里面还残留着单个括号 不匹配

#ifndef stack.h

#include<stdio.h>
#include<stdlib.h>
#include"stack.h"
/*
括号检测
1.这个会用到栈,我们遍历字符串,如果遇到左括号,其他忽略,我们就入栈;
2.继续遍历,如果遇到右括号,我们就把他们从出栈,然后判断一下是不是一对括号;
3.最后大总结判断,判断一下栈是否为空,如果为空代表成双成对,否则 里面还残留着单个括号 不匹配
*/
//判断是否为左括号 < [ ( “ ’
int isLeft(char ch)
{
switch (ch)
{
case ‘{’:
case ‘[’:
case ‘(’:
case ‘<’:
case ‘"’:
return 1;
default:
return 0;
}

}
//判断是否为右括号
int isRight(char ch)
{
switch (ch)
{
case ‘}’:
case ‘]’:
case ‘)’:
case ‘>’:
case ‘"’:
return 1;
default:
return 0;
}

}
//判断是否成对
int match(char left, char right)
{
switch (left)
{
case ‘{’: if (right == ‘}’)
return 1;
break;
case ‘[’:if (right == ‘]’)
return 1;
break;
case ‘(’:if (right == ‘)’)
return 1;
break;
case ‘<’:if (right == ‘>’)
return 1;
break;
case ‘"’:if (right == ‘"’)
return 1;
break;

}
return 0;

}
int judge(const char str)//const char str的意思是 无法改变指针指向空间的内容,但是可以改变指针。 char const str//确保这个指针不会改变,但是指针里面的内容可以改变
{
struct stack
sk = createStack();
while (*str)//如果没有遍历到\0 就继续遍历
{

	if (isLeft(*str))
	{//如果存在左括号,就入栈
		push(sk, *str);

	}
	//判断是否是右括号
	if (isRight(*str))
	{//如果是右括号,就判断是否成对
		if (!empty(sk) && match(stackTop(sk), *str))//完善更改 你会发现只完成一对是不行的,因为要继续变量 so 判断NULL
		{
			printf("%c 括号匹配成功", stackTop(sk));
			pop(sk);//弹出栈顶元素
			printf("ok\n");

		}
		else
		{
			return 0;
		}
	}
	str++;
}//可是出现了新的问题,如果全部都是左括号,就会跳出 自动中断
if (!empty(sk))
{
	while (!empty(sk))
	{
		printf("还剩下%c", stackTop(sk));//一定要出栈,否则就会一直打印第一个
		pop(sk);
	}
	return 0;
}
return 1;

}
int main()
{
char *str = “#include<stdio.h>#include<stdl{}ib.h>[(;”;//str是这个字符串指针,str=&字符串 反正就是指向地址首位, *一把钥匙 &一把锁 *str=内容 是指向位置空间的一个变量
int isok = judge(str);//接收返回值
if (isok)
{
printf(“匹配成功”);

}
else
{
	printf("没有成双成对,或者你的程序有错误");
}

}
#endif
太可怕了,出现了问题,我滴天呐,我第一次写程序把我自己的文件作为头文件。然后出
现了问题,
在这里插入图片描述
注意点与学习到的知识:
1 原来。h文件必须要放到 项目目录下 而且让我写程序要规范,
2 必须带上防重复 ifndef 确实这是个好习惯
既然提出来了 我就要认真执行。
3 头文件里面还必须取掉main函数,一个项目里面只能有一个main函数。
4.我又犯了一个错误,如果突然出现很多现实未声明的错误,那么就是顺序编译的原因,尤其是定义的结构体类型要放在最前面。
5.enum 枚举 每个元素后面加“,”。 然后 DIR::RIGHT “::”表示 域运算符号 从属关系
接下来我们来做
在这里插入图片描述
我们首先控制了蛇头的移动,现在我们来控制蛇身的移动,那么他只需要每一个都移动到前一个位置的坐标就行了。
重点问题
1.刚开始因为移动速度过快,我们延时整个程序,sleep(500) #延时半秒 使我们能看到小蛇
2. 控制小蛇方向时,我们发现反应迟钝,这时我们该怎么处理呢?其实使用sleep,会让全部程序都在进行下一次循环之前暂停。而我们的目的就只是让snakeMove这个函数进行延时,来让我们看见小蛇,控制不灵敏就是因为对keyCtrol 也进行了延时。
initgraph(width, height);
gameInt();
DWORD t1 , t2;//typedef unsigned long DWORD; 无符号长整型
t1 = t2 = GetTickCount();
//获得系统从开机到现在经过的毫秒数 GetTickCount是一种函数。GetTickCount返回(retrieve)从操作系统启动所经过(elapsed)的毫秒数,它的返回值是DWORD。

while (1)
{
	GameDraw();
	
	if (t2-t1> 20)
	{
		snakeMove();
		t1=t2;
	}
	t2 = GetTickCount();
	keyCtr();
	已被注释/Sleep(500);//使整个程序 延时 500=半秒,但这里我们需要的是移动延时

其实这样子的话,就能使snakeMove这个函数循环3,4次才能被执行一次,达到专项延时效果。
在蛇吃掉食物这个功能实现时,出现了问题,
void eatFood()
{
if (food.x ==snake.coor[0].x&&food.y == snake.coor[0].y)//判断是否相等,一定记得两个等号 ==
{
food.flag = 0;
snake.size++;
}
}
这样太绝对化,只有蛇头圆心与食物圆心重合,才能使身体变长。但其实我们知道了半径
在这里插入图片描述
只要蛇头圆心在以食物为圆心,的半径范围内就可以了

@1._getch() 和 getch() 有什么区别?

1,带下划bai线_的函数一般是函数库内部的du函数,而不带下划线的一般是提zhi供给用户使用的函数。带dao下划线的目的是***为了防止用户定义的函数和函数库的函数重名冲突,所以直接使用也是可以的***。

@ 2.为啥要加一个close

原来是这样,你把close 如果关掉的话,你会发现在吃第一个食物的时候,响一声,后面都不响。因为这个音乐就只有几秒是有声的,你一直开着,后面自然是无声。
现在我们来梳理一下 新学到的陌生函数:
在这里插入图片描述

1.initgraph(width, height)

是函数名 ,功能为初始化图形系统。pathtodriver表示存放图形驱动文件的路径。头文件是graphics.h。

2.初始化数据一定要在操作前

gameint —》gamedraw

3.//播放背景音乐

mciSendString(L"open  ./ress/snake_bgm.mp3 alias BGM",0,0,0);
mciSendString(L"play BGM repeat", 0, 0, 0);
A. 在字符串前加一个L作用: unicode字符集是两个字节组成的。L告示编译器使用两个字节的 unicode 字符集。

如 L"我的字符串" 表示将ANSI字符串转换成unicode的字符串,就是每个字符占用两个字节。
在这里插入图片描述
使用函数前必须应该包含头文件:#include<mmsystem.h>

并导入库:#pragma comment<lib.“winmm.lib”>

mciSendString是用来播放多媒体文件的API指令,可以播放MPEG,AVI,WAV,MP3,等等

函数原型如下:

MCIERROR mciSendString(
LPCTSTR lpszCommand, //命令字符串:如 open、play 、close等
LPTSTR lpszReturnString, //接受返回信息的字符串 一般为NULL
UINT cchReturn, //返回字符串的大小
HANDLE hwndCallback //回调窗口句柄
);
最后,注意
在这里插入图片描述
改变属性:
在这里插入图片描述
在这里插入图片描述

@4.alias,repeat

alias就是换一个别名
repeat 重复
在这里插入图片描述

@5.随机数种子

srand(GetTickCount());
srand跟rand很像,就是弄随机数的,里面是随机值范围,然后srand(100)就是100->0xFFFFFFFF
srand(G...)就是下图
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200814152630211.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N1bmJveTMyMw==,size_16,color_FFFFFF,t_70#pic_center)

在这里插入图片描述
在这里插入图片描述

@6.rand() % 256 随机值

food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
后面是范围 表示0-255 是256以内

@延时 GetTickCount()时间差

DWORD t1 , t2;
//typedef unsigned long DWORD;
t1 = t2 = GetTickCount();
//获得系统从开机到现在经过的毫秒数 GetTickCount是一种函数。GetTickCount返回(retrieve)从操作系统启动所经过(elapsed)的毫秒数,它的返回值是DWORD。
while (1)
{
GameDraw();

	if (t2-t1> 20)
	{
		snakeMove();
		t1=t2;
	}
	t2 = GetTickCount();
	eatFood();
	keyCtr();
	stop();
	die();
}

@7. MessageBox(GetHWnd(), “you are fail”, “warning…”, MB_YESNO) 信息窗口

MB_OK 就是 你调用MessageBox 后, 弹出的对话框里 有个确定按钮。

如果是 MB_YESNO 的话 就是有两个按钮 是 和 否*/
在这里插入图片描述
(获取句柄,中间语句,表头语句,类型)

@8.暂停运行

void stop()
{
if (_kbhit())//检测到有按键
{
while (_getch() == ’ ')//getch 是需要你输入的 所以会卡在这里 你输入了 空格 才会进入while里面 会在下一个getch卡住,只有再次输入空格才会有用
{
if (_getch() == ’ ‘)//while(_getch()!=’ ')
{
break;
}
}
}
}

@9.分数设置

void outtextxy_score(int x, int y, const char *text)
{
char str[20] = “”;
sprintf(str, “%s:%d”, text, snake.score);
settextcolor(RED);
settextstyle(35, 0, “楷体”);
outtextxy(x, y, str);
}

@10.去warning

#define _CRT_SECURE_NO_WARNINGS

程序

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<graphics.h>
#include<conio.h>//_kbhit 是判断是否有键盘按下
#include<string.h>
#include<mmsystem.h>//播放音乐
#pragma comment(lib,“winmm.lib”)//动静态库 /#pragma 是一条编译器指令,是给告诉编译器你要链接一些东西,然后在后面的comment里面指明是什么东西 导入winmm.lib库,有了它才可以支持对windows 多媒体的编程./
#define width 800
#define height 600
首先 我们需要分析功能实现
解析:1.创建窗口 绘制蛇的身体
2.蛇的移动
// 3.蛇的穿墙 吃食
// 4.判断输赢 分数
// 5食物的绘制
//蛇的属性: 长度,坐标,速度,分数,方向,是否死亡
#define SNAKE_NUM 500//蛇的节数最大值是500
//定义食物的信息结构
struct FOOD
{
int x;
int y;
int r;//半径
int flag;//判断是否被吃掉
COLORREF color;

}food;//定义这些模板后,一定要初始化

void stop()
{
if (_kbhit())//检测到有按键
{
while (_getch() == ’ ')//getch 是需要你输入的 所以会卡在这里 你输入了 空格 才会进入while里面 会在下一个getch卡住,只有再次输入空格才会有用
{
if (_getch() == ’ ‘)//while(_getch()!=’ ')
{
break;
}
}
}
}
struct SNAKE
{
int size;//几节身体
POINT coor[SNAKE_NUM];//每一节蛇的坐标信息都会储存在这个数组里面 int x;//int y;可以换成POINT

int speed;//我自己感觉speed要与半径一样
int dir;
int score;

}snake;
//先尝试使用结构体数组
//cpp使用顺序编译 然后我们来枚举
enum DIR
{
UP,
DOWN,
LEFT,
RIGHT,//每个后面都要加,
};

/*
函数:控制蛇的运动
分析:我们只需要控制蛇头的运动就可以,头会带着身子走
控制运动实际就是在控制坐标
*/
void snakeMove()
{//控制蛇的身体的移动
for (int i = snake.size - 1; i > 0; i–)//这里竟出现了错误。是因为前面我初始化的是i,但是后面写的是常量snake.size
{
snake.coor[i] = snake.coor[i - 1];
}
//控制蛇头移动
switch (snake.dir)
{
case UP:
snake.coor[0].y -= snake.speed;
//是否到边界 穿墙判断
if (snake.coor[0].y < 0)
{
snake.coor[0].y = height;

	}
	break;
case DOWN:
	snake.coor[0].y += snake.speed;
	//是否到边界 穿墙判断
	if (snake.coor[0].y >height)
	{
		snake.coor[0].y = 0;
		
	}
	break;
case LEFT:
	snake.coor[0].x -= snake.speed;
	//是否到边界 穿墙判断
	if (snake.coor[0].x < 0)
	{
		snake.coor[0].x = width;
		
	}
	break;
case RIGHT:
	snake.coor[0].x += snake.speed;
	//是否到边界 穿墙判断
	if (snake.coor[0].x > width)
	{
		snake.coor[0].x = 0;
		
	}
	break;
default:
	break;
}

}

void outtextxy_score(int x, int y, const char *text)
{
char str[20] = “”;
sprintf(str, “%s:%d”, text, snake.score);
settextcolor(RED);
settextstyle(35, 0, “楷体”);
outtextxy(x, y, str);
}

//绘制游戏
void GameDraw()
{

BeginBatchDraw();//开始批量绘制,直到出现flushbatchdraw 或者endbatchdraw, 才会展示。  消除闪屏
setbkcolor(RGB(141, 173, 226));//设置背景颜色
cleardevice();//清楚图形屏幕窗口   在计算机术语中,device是屏幕的意思。
outtextxy_score(20, 50, "分数");
for (int i = 0; i < snake.size; i++)
{
	setfillcolor(YELLOW);
	solidcircle(snake.coor[i].x, snake.coor[i].y, 3);
}
//绘制食物
if (food.flag)
{
	setfillcolor(food.color);//只有设置了颜色,初始化才管用
	solidcircle(food.x, food.y, food.r);
}



EndBatchDraw();

}
//接触死亡
void die()
{
for (int i = 3; i < snake.size; i++)
{
if (snake.coor[0].x == snake.coor[i].x&&snake.coor[0].y == snake.coor[i].y)
{
MessageBox(GetHWnd(), “you are fail”, “warning…”, MB_YESNO);/*1.在字符串前加一个L作用: unicode字符集是两个字节组成的。L告示编译器使用两个字节的 unicode 字符集。
如 L"我的字符串" 表示将ANSI字符串转换成unicode的字符串,就是每个字符占用两个字节。
strlen(“asd”) = 3;
strlen(L"asd") = 6;
2.MB_OK 就是 你调用MessageBox 后, 弹出的对话框里 有个确定按钮。

如果是 MB_YESNO 的话 就是有两个按钮 是 和 否*/
exit(0);

	}
}

}
//吃食物效果
void eatFood()
{

if (snake.coor[0].x >= food.x - food.r&&snake.coor[0].x <= food.x + food.r&&snake.coor[0].y >= food.y - food.r&&snake.coor[0].y <= food.y + food.r&&food.flag==1)//判断是否相等,一定记得两个等号 ==
{
	food.flag = 0;
	snake.score += 10;		//吃食物分数改变
	snake.size++;
	mciSendString("close EAT ", 0, 0, 0);//这里还是不懂
	mciSendString("open  ./ress/eatfood.mp3 alias EAT", 0, 0, 0);
	mciSendString("play EAT", 0, 0, 0);
}
if (!food.flag)
{
	food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
	food.flag = 1;
	food.r = rand() % 10 + 3;
	food.x = rand() % width;
	food.y = rand() % height;
}

}
/*
函数:控制方向 使用键盘
*/
void keyCtr()//GetAsyncKeyState是一个用来判断函数调用时指定虚拟键的状态,确定用户当前是否按下了键盘上的一个键的函数。如果按下,则返回值最高位为1。
{
//在这里,我们可以用 getch的方法,但还有另外一种。目前有两种
if (GetAsyncKeyState(‘W’) || GetAsyncKeyState(VK_UP)&&snake.dir!=DOWN)/*1 注意里面只能用大写
2 并且不能直接掉头,所以我们要进行限制
3. 加穿墙效果 调整蛇头功能
*/

{
	snake.dir = DIR::UP;
}
if (GetAsyncKeyState('D') || GetAsyncKeyState(VK_RIGHT) && snake.dir != LEFT)
{
	snake.dir = DIR::RIGHT;
}
if (GetAsyncKeyState('S') || GetAsyncKeyState(VK_DOWN) && snake.dir != UP)
{
	snake.dir = DIR::DOWN;
}
if (GetAsyncKeyState('A') || GetAsyncKeyState(VK_LEFT) && snake.dir != RIGHT)
{
	snake.dir = DIR::LEFT;
}

}
//那么我们定义好蛇的属性之后,就需要来初始化游戏
void gameInt()
{//播放背景音乐
mciSendString(“open ./ress/snake_bgm.mp3 alias BGM”,0,0,0);
mciSendString(“play BGM repeat”, 0, 0, 0);
//随机数种子
srand(GetTickCount());
snake.dir = DIR::RIGHT; //怎么初始化方向也是非常令人值得注意的
snake.size = 3;// 哦 原来如此 这里其实是struct snake.size
snake.speed = 6;
snake.score = 0;
snake.coor[2].x=10; //初始化坐标比较值得注意
snake.coor[2].y=10;
snake.coor[1].x = 30; //初始化坐标比较值得注意
snake.coor[1].y = 10;
snake.coor[0].x = 50; //初始化坐标比较值得注意
snake.coor[0].y = 10;
//初始化食物 不懂 代问
food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
food.flag = 1;
food.r = rand() % 10 + 3;
food.x = rand() % width;
food.y = rand() % height;
}

int main()
{
initgraph(width, height);//画出图框
gameInt();
DWORD t1 , t2;//typedef unsigned long DWORD;
t1 = t2 = GetTickCount();//获得系统从开机到现在经过的毫秒数 GetTickCount是一种函数。GetTickCount返回(retrieve)从操作系统启动所经过(elapsed)的毫秒数,它的返回值是DWORD。

while (1)
{
	GameDraw();

	if (t2-t1> 20)
	{
		snakeMove();
		t1=t2;
	}
	t2 = GetTickCount();
	eatFood();
	keyCtr();
	stop();
	die();
	//Sleep(500);//使整个程序 延时 500=半秒,但这里我们需要的是移动延时
}


system("pause");
return 0;

}
在这里插入图片描述
显示分数。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值