C/C++ 趣味编程——别去撞墙(飞行躲避障碍类游戏)

目录

一、让小球蹦跶起来——键盘输入控制小球

       键盘输入—— _getch()函数

         数据类型char

         键盘输入—— kbhit()函数                       

二、虽然不想撞南墙但是南墙向我冲过来——“墙体”的制作工艺

1、总之先建墙起来

        fillrectangle绘制矩形  

2、让墙也动起来吧

3、小球和墙的碰撞判断

        三种逻辑运算符:       

4、墙体随机一点才有挑战性

       rand()生成随机整数

浮点数除法、整数除法、取余运算符控制随机数的范围

类型转换

生成随机墙体

三、让他看起来有模有样——得分的计算与显示

完整代码:

四、好像还有什么问题没解决


一、让小球蹦跶起来——键盘输入控制小球

          在操作小游戏的时候,我们首先得让程序收到我们按键信息,首先,让我们的程序接收字符吧。

       键盘输入—— _getch()函数

        _getch()——其作用是等待用户输入一个字符,当用户按下键盘的任意键之后,程序结束。

         数据类型char

         char定义字符型数据,printf()函数利用%c格式进行控制字符型常量变量的输出,%d输出整数,%f输出浮点数

#include<graphics.h>
#include<conio.h>
#include<stdio.h>

int main()
{
	while(1)
	{
		char c;//定义字符型变量
		c = _getch();
		printf("输入的字符为:%c\n", c);//printf函数用%c控制字符型变量常量的输出
	}
	

	return 0;

}

        在while循环下,可以重复输入字符

         接下来想程序实现,我不按键你就别识别,按键了再分析

         键盘输入—— kbhit()函数                       

         kbhit()函数——当有键盘输入时返回1,否则返回0

#include<graphics.h>
#include<conio.h>
#include<stdio.h>

int main()
{
	while(1)
	{

		if (_kbhit( ))
		{
			char input = _getch();//定义字符型变量
			if (input == ' ')//此处表示空格是单引号
				printf("按下了空格键.\n");
			else if (input != ' ')
				printf("你输入的是:%c\n", input);

		}
	}
	

	return 0;

ps:在使用函数的时候会发生这种情况

                表示你正在使用的函数或特性在 Visual Studio 中已经被标记为弃用(deprecated)。在这种情况下,编译器告诉你 kbhit 这个函数的名字是 POSIX 风格的,现在更推荐使用 ISO C 和 C++ 标准的名字 _kbhit

        要解决这个问题,你需要将代码中的 kbhit 替换为 _kbhit。这样,你的代码就会使用推荐的、与标准更一致的函数。

运行结果如下:

       结合上面的准备,加上之前”玩的就是真实“的技巧,我们就可以做出来一个空格控制起跳的小球了

#include<graphics.h>
#include<conio.h>
#include<stdio.h>

int main()
{

	float w, h, g;//窗口宽高、重力加速度
	float x, y, vy, r;//小球圆心坐标\初速度和半径

	w = 600;
	h = 400;
	g = 0.6;
	initgraph(w, h);//创建一个新窗口

	r = 20;
	x = w / 4;
	y = h - r;
	vy = 0;

	while(1)
	{

		if (_kbhit( ))//按键时
		{
			char input = _getch();// 获得输入字符
			if (input == ' ')
			{
				vy = -16;//给小球向上初速度
			}
		}

		//老样子,小球运动的规范
		vy = vy + g;
		y = y + vy;
		//重力加速给小球带来的的位置改变
		if (y >= h - r)
		{
			vy = 0;
			y = h - r;//规范坐标避免落到地面下
		}
		//如果落地,速度为0

		cleardevice();//清空屏幕
		fillcircle(x, y, r);//绘制小球
		Sleep(10);//暂停十毫秒

	}//循环结束
	closegraph();
	return 0;

	return 0;

}

 运行结果如图:

二、虽然不想撞南墙但是南墙向我冲过来——“墙体”的制作工艺

1、总之先建墙起来

            小球准备完毕,接下来考虑障碍物——墙。

        fillrectangle绘制矩形  

        fillrectangle(left,top,right,bottom)——可以绘制矩形,其中,(left,top)是矩形左上角的坐标,(right,bottom)是矩形右下角坐标

float c_x, c_y, c_w, c_h;//方块坐标和宽高

    c_w = 20;
	c_h = 100;
	c_x = w * 3 / 4;
	c_y = h - c_h;
	//注意变量们的先后顺序,先出现的才能在后面调用

	fillrectangle(c_x, h-c_h, c_x + c_w, h);

 带上之前小球起跳的一起:

#include<graphics.h>
#include<conio.h>
#include<stdio.h>

int main()
{

	float w, h, g;//窗口宽高、重力加速度
	float x, y, vy, r;//小球圆心坐标\初速度和半径
	float c_x, c_y, c_w, c_h;//方块坐标和宽高
	w = 600;
	h = 400;
	g = 0.6;
	initgraph(w, h);//创建一个新窗口

	r = 20;
	x = w / 4;
	y = h - r;
	vy = 0;
	
	c_w = 20;
	c_h = 100;
	c_x = w * 3 / 4;
	c_y = h - c_h;
	//注意变量们的先后顺序,先出现的才能在后面调用


	while(1)
	{

		if (_kbhit( ))//按键时
		{
			char input = _getch();// 获得输入字符
			if (input == ' ')
			{
				vy = -16;//给小球向上初速度
			}
		}

		//老样子,小球运动的规范
		vy = vy + g;
		y = y + vy;
		//重力加速给小球带来的的位置改变
		if (y >= h - r)
		{
			vy = 0;
			y = h - r;//规范坐标避免落到地面下
		}
		//如果落地,速度为0

		cleardevice();//清空屏幕
		fillcircle(x, y, r);//绘制小球
		fillrectangle(c_x, h-c_h, c_x + c_w, h);//注意他的地方,因为每次执行while会清空屏幕,所以放到while里面
		Sleep(10);//暂停十毫秒

	}//循环结束
	closegraph();
	return 0;

	return 0;

}

 结果如图:

 ps:需要注意的

        1、对于坐标,窗口的坐标是左上边沿为0,下右边沿为设置的高h和宽w;

        2、这里需要注意因为每次while循环让小球起跳时会清除屏幕,所以方块必须放在while循环里,否则屏幕一清就看不到方块在哪儿了;
        3、对于 小球坐标的运算,c_x=变量*3/4  与 c_x= 3/4*变量结果不同,后者运行结果如图:

后者c_x运算之后会直接变成0;

  因为变量是整数且非零,那么这两个表达式的计算顺序可能会影响结果,因为整数除法会向下取整

2、让墙也动起来吧

        虽然视觉上看起来像是小球蹦蹦跳跳去撞墙,但实际上,对于静止的窗口,小球待在那儿没动,实际上是一堵堵墙冲向小球

        所以参照赋予小球运动能力的魔法,让墙也动起来

float c_x, c_y, c_w, c_h,c_vx;//方块坐标和宽高和初速度

	c_w = 20;
	c_h = 100;
	c_x = w * 3 / 4;
	c_y = h - c_h;
	c_vx = -3;

c_x = c_x + c_vx;//方块运动
		if (c_x + c_w <= 0)
		{
			c_x = w;

		}//如果运动到最左,重新从最右边出现


fillrectangle(c_x, h-c_h, c_x + c_w, h);

加上去: 

#include<graphics.h>
#include<conio.h>
#include<stdio.h>

int main()
{

	float w, h, g;//窗口宽高、重力加速度
	float x, y, vy, r;//小球圆心坐标\初速度和半径
	float c_x, c_y, c_w, c_h,c_vx;//方块坐标和宽高和初速度
	w = 600;
	h = 400;
	g = 0.6;
	initgraph(w, h);//创建一个新窗口

	r = 20;
	x = w / 4;
	y = h - r;
	vy = 0;
	
	c_w = 20;
	c_h = 100;
	c_x = w * 3 / 4;
	c_y = h - c_h;
	c_vx = -3;
	//注意变量们的先后顺序,先出现的才能在后面调用


	while(1)
	{

		if (_kbhit( ))//按键时
		{
			char input = _getch();// 获得输入字符
			if (input == ' ')
			{
				vy = -16;//给小球向上初速度
			}
		}

		//老样子,小球运动的规范
		vy = vy + g;
		y = y + vy;
		//重力加速给小球带来的的位置改变
		if (y >= h - r)
		{
			vy = 0;
			y = h - r;//规范坐标避免落到地面下
		}
		//如果落地,速度为0


		c_x = c_x + c_vx;//方块运动
		if (c_x + c_w <= 0)
		{
			c_x = w;

		}//如果运动到最左,重新从最右边出现

		cleardevice();//清空屏幕
		fillcircle(x, y, r);//绘制小球
		fillrectangle(c_x, h-c_h, c_x + c_w, h);//注意他的地方,因为每次执行while会清空屏幕,所以放到while里面
		Sleep(10);//暂停十毫秒

	}//循环结束
	closegraph();
	return 0;

	return 0;

}

        重复让一面墙跑起来,感觉就像有很多面墙一样,看起来还算是像模像样的了,但是很明显,撞墙了没有反馈,我们还需要更多不同高度的墙

3、小球和墙的碰撞判断

        在之前的代码中我们成功让小球能够起跳,墙能够移动,但是小球与墙体的碰撞还需要进一步的判断。

        小球和墙体的碰撞有三种情况:

1、墙的左边和小球右边碰撞

2、墙的上边和小球下边碰撞

3、墙的右边和小球左边碰撞

        三种逻辑运算符:       

        符合三条逻辑判断的结果即可判断“小球撞上墙了”,而进行逻辑判断需要用到逻辑运算符——运用C语言提供的三种逻辑运算符,实现多种逻辑条件的组合:

!(非)输出相反
&&(与)全真为真,有假即假
||(或)全假为假,有真即真

        现在我们可以实现小球碰撞判断,和撞上去之后我们想要的效果了

if ((c_x <= x + r) && (c_x + c_w >= x - r) && (h - c_h <= y + r))
			/*
			如果(墙的左边撞上小球右边)
			&& (墙的右边装上小球的左边)
			&& (墙的上边撞上小球下边 ) 
            ps:因为窗口下边沿为h上边沿为0,所以这里小球下边不是惯性思维的y - r
			*/
		{
			Sleep(1000);//慢动作效果

		}

4、墙体随机一点才有挑战性

        虽然现在满足了碰撞效果,也有了墙体移动,但是墙体应该随机一点才有挑战性,因此,我们需要让墙体的高度随机起来。

       rand()生成随机整数

         按下空格时输出一个随机整数:

        但是明显这个随机整数的范围很大,我们不能让自己的墙随机的高度有好几个都超过窗口大小 ,所以下一步

浮点数除法、整数除法、取余运算符控制随机数的范围

        为了得到设定范围内的随机数,我们需要利用浮点数除法、整数除法、取余运算符

#include<stdio.h>
int main()
{
	float a=5.0/2;
	int a1 = 5.0 / 2;
	printf("浮点型接收浮点除法:%f\n", a);
	printf("整型接收浮点除法:%d\n", a1);
	printf("整型接收浮点除法但是最后我想转回浮点型:%f\n", a1);
	/*
	编译时,很多编译器会发出警告,指出类型不匹配。
	在运行时,由于printf期待一个浮点数(通常是双精度浮点数double),
	但它接收到一个整数,所以它会从内存中错误地解释整数后面的位作为浮点数,
	这可能导致输出一个随机的、无意义的浮点数。
	*/
	int b = 5 / 2;
	float b1= 5 / 2;
	printf("\n");
	printf("整型接收整数除法%d\n", b);
	printf("浮点型接收整型除法:%f\n", b1);
	printf("浮点型接收整型除法但是最后我想转回整型:%d\n", b1);


	int c=10%3 ;
	printf("\n");
	printf("取余运算结果:%d\n", c);



	return 0;
}

运行结果: 

        除号“/”两边数字或变量其中有一个为浮点数时,实行浮点数除法;

        除号“/”两边数字或变量均为整型,实行整数除法;

        取余号“%”输出左右两整数相除得到的余数;

eg.利用rand()%10可以生成一个0~9的随机数

      5  在上述编译时,很多编译器会发出警告,指出类型不匹配。在运行时,由于printf期待一个浮点数(通常是双精度浮点数double),但它接收到一个整数,所以它会从内存中错误地解释整数后面的位作为浮点数,这可能导致输出一个随机的、无意义的浮点数。

这就牵扯到了

类型转换

int a=1.1 ——把浮点数1.1自动转换为整数1,赋值给a;

float b=2——把整数2自动转换成浮点数2.0,赋值给b;这种转换方式称为自动转换。

float(),int()形式称为强制类型转换。

float(6.3/2)等于3,int(6.3)把6.3转换成6

生成随机墙体

现在我们可以开始生产我们随机的墙体了。

if (c_x + c_w <= 0)
		{
			c_x = w;
			c_h = rand() % int(h / 4) + h / 4;//设置随机墙高重新给墙体h赋值,但是不能小于h/4
			c_vx = rand() / float(RAND_MAX) * 4 - 7;//设置墙体随机速度

		}//如果运动到最左,重新从最右边出现

ps.RAND_MAX存储了rand()函数所能生成的最大整数,rand()/float(RAND_MAX)可以生成0~1的随机小数 

三、让他看起来有模有样——得分的计算与显示

        现在我们有了墙,也有了蹦跶的小球,该让他显示得分之类的了

        ps.为了方便处理越来越多的代码,我们用“工具”——>“文本编辑器”——>“所有语言”——>“显示”里的“行号”来令编辑器中显示代码行号方便阅读理解代码

1、定义整型变量记录得分:

int score=0;//定义得分
/*记得给他初始值,别int score;就结束了,
上面几句时先定义再赋值,实际上还是有初始值的
*/

2、墙成功跑到最左得分+1

        if (c_x + c_w <= 0)
		{
			score += 1;//得分+1
			c_x = w;
			c_h = rand() % int(h / 4) + h / 4;//重新给墙体h赋值,但是不能小于h/4
			c_vx = rand() / float(RAND_MAX) * 4 - 7;//设置墙体随机速度

		}//如果运动到最左,重新从最右边出现

3、撞墙得分清零

if ((c_x <= x + r) && (c_x + c_w >= x - r) && (h - c_h <= y + r))
		
		{	
			score = 0;//得分清零
			Sleep(100);//慢动作效果
		}

4、利用EasyX的文字输出功能,输出score得分:

		TCHAR s[20];//定义字符串数组
		_stprintf_s(s, _T("得分:% d"), score);//将score转换为字符串
		settextstyle(40, 0, _T("黑体"));//设置文字大小字体
		outtextxy(50, 30, s);//输出得分文字

完整代码:
#include<graphics.h>
#include<conio.h>
#include<stdio.h>

int main()
{

	float w, h, g;//窗口宽高、重力加速度
	float x, y, vy, r;//小球圆心坐标\初速度和半径
	float c_x, c_y, c_w, c_h,c_vx;//方块坐标和宽高和初速度
	w = 600;
	h = 400;
	g = 0.6;
	initgraph(w, h);//创建一个新窗口

	r = 20;
	x = w / 4;
	y = h - r;
	vy = 0;
	
	c_w = 20;
	c_h = 100;
	c_x = w * 3 / 4;
	c_y = h - c_h;
	c_vx = -3;
	//注意变量们的先后顺序,先出现的才能在后面调用
	int score=0;//定义得分,记得给他初始值,别int score;就结束了
	while(1)
	{

		if (_kbhit( ))//按键时
		{
			char input = _getch();// 获得输入字符
			if (input == ' ')
			{
				vy = -16;//给小球向上初速度
			}
		}

		//老样子,小球运动的规范
		vy = vy + g;
		y = y + vy;
		//重力加速给小球带来的的位置改变
		if (y >= h - r)
		{
			vy = 0;
			y = h - r;//规范坐标避免落到地面下
		}
		//如果落地,速度为0


		c_x = c_x + c_vx;//方块运动
		if (c_x + c_w <= 0)
		{
			score += 1;//得分+1
			c_x = w;
			c_h = rand() % int(h / 4) + h / 4;//重新给墙体h赋值,但是不能小于h/4
			c_vx = rand() / float(RAND_MAX) * 4 - 7;//设置墙体随机速度

		}//如果运动到最左,重新从最右边出现


		if ((c_x <= x + r) && (c_x + c_w >= x - r) && (h - c_h <= y + r))
			/*
			如果(墙的左边撞上小球右边)
			&& (墙的右边装上小球的左边)
			&& (墙的上边撞上小球下边 ) ps:因为窗口下边沿为h上边沿为0,所以这里不是惯性思维的y - r
			*/
		{	
			score = 0;
			Sleep(100);//慢动作效果

		}
		cleardevice();//清空屏幕
		fillcircle(x, y, r);//绘制小球
		fillrectangle(c_x, h-c_h, c_x + c_w, h);//注意他的地方,因为每次执行while会清空屏幕,所以放到while里面
		
		TCHAR s[20];//定义字符串数组
		_stprintf_s(s, _T("得分:% d"), score);//将score转换为字符串
		settextstyle(40, 0, _T("黑体"));//设置文字大小字体
		outtextxy(50, 30, s);//输出得分文字
		
		
		
		
		Sleep(10);//暂停十毫秒

	}//循环结束
	closegraph();
	return 0;

	return 0;

}

 运行结果如图:

四、好像还有什么问题没解决

        在最后的示例里有个问题还没解决——可以一直起跳,如果一直高高地跳在天上,那么就不会撞到墙。所以我们进一步限制小球“只能在地上起跳”

增加“在地面”的判定:

int ballonfloor = 1;//判断小球是否在地面,1在,0不在

按下空格时,只有在地面才能起跳:

		if (_kbhit( ))//按键时
		{
			char input = _getch();// 获得输入字符
			if (input == ' ' && ballonfloor == 1)//如果小球在地面,输入空格
			{
				vy = -16;//给小球向上初速度
				ballonfloor = 0;//小球离开地面
			}
		}

回到地面 时:

	if (y >= h - r)
		{
			vy = 0;
			y = h - r;//规范坐标避免落到地面下
			ballonfloor = 1;//小球回到地面
		}
		//如果落地,速度为0,可重起跳

最后完整的全部代码:

#include<graphics.h>
#include<conio.h>
#include<stdio.h>

int main()
{

	float w, h, g;//窗口宽高、重力加速度
	float x, y, vy, r;//小球圆心坐标\初速度和半径
	float c_x, c_y, c_w, c_h,c_vx;//方块坐标和宽高和初速度
	w = 600;
	h = 400;
	g = 0.6;
	initgraph(w, h);//创建一个新窗口

	r = 20;
	x = w / 4;
	y = h - r;
	vy = 0;
	
	c_w = 20;
	c_h = 100;
	c_x = w * 3 / 4;
	c_y = h - c_h;
	c_vx = -3;
	//注意变量们的先后顺序,先出现的才能在后面调用


	int score=0;//定义得分,记得给他初始值,别int score;就结束了,上面几句时先定义再赋值,实际上还是有初始值的
	
	int ballonfloor = 1;//判断小球是否在地面,1在,0不在
	
	
	while(1)
	{

		if (_kbhit( ))//按键时
		{
			char input = _getch();// 获得输入字符
			if (input == ' ' && ballonfloor == 1)//如果小球在地面,输入空格
			{
				vy = -16;//给小球向上初速度
				ballonfloor = 0;//小球离开地面
			}
		}

		//老样子,小球运动的规范
		vy = vy + g;
		y = y + vy;
		//重力加速给小球带来的的位置改变
		if (y >= h - r)
		{
			vy = 0;
			y = h - r;//规范坐标避免落到地面下
			ballonfloor = 1;//小球回到地面
		}
		//如果落地,速度为0,可重起跳


		c_x = c_x + c_vx;//方块运动
		if (c_x + c_w <= 0)
		{
			score += 1;//得分+1
			c_x = w;
			c_h = rand() % int(h / 4) + h / 4;//重新给墙体h赋值,但是不能小于h/4
			c_vx = rand() / float(RAND_MAX) * 4 - 7;//设置墙体随机速度

		}//如果运动到最左,重新从最右边出现


		if ((c_x <= x + r) && (c_x + c_w >= x - r) && (h - c_h <= y + r))
			/*
			如果(墙的左边撞上小球右边)
			&& (墙的右边装上小球的左边)
			&& (墙的上边撞上小球下边 ) ps:因为窗口下边沿为h上边沿为0,所以这里不是惯性思维的y - r
			*/
		{	
			score = 0;
			Sleep(100);//慢动作效果

		}
		cleardevice();//清空屏幕
		fillcircle(x, y, r);//绘制小球
		fillrectangle(c_x, h-c_h, c_x + c_w, h);//注意他的地方,因为每次执行while会清空屏幕,所以放到while里面
		
		TCHAR s[20];//定义字符串数组
		_stprintf_s(s, _T("得分:% d"), score);//将score转换为字符串
		settextstyle(40, 0, _T("黑体"));//设置文字大小字体
		outtextxy(50, 30, s);//输出得分文字
		
		
		
		
		Sleep(10);//暂停十毫秒

	}//循环结束
	closegraph();
	return 0;

	return 0;

}

运行结果:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值