目录
一、让小球蹦跶起来——键盘输入控制小球
在操作小游戏的时候,我们首先得让程序收到我们按键信息,首先,让我们的程序接收字符吧。
键盘输入—— _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;
}
运行结果: