[C/C++学习] 6.弹跳小球(B)
参考文献: 童晶. C和C++游戏趣味编程[M].人民邮电出版社.2021.
上节课的程序:
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
int main(){int Width=600,High=600;//窗口尺寸float x=100; //小球x坐标float y=20; //小球y坐标float vy = 10; //小球y轴速度vint r = 20;//小球半径rfloat a = 2;//重力加速度设置为2initgraph(Width,High); //生成600X600像素的窗口cleardevice();//清空画布fillcircle(x,y,r);//在坐标(100,90)处画半径20像素的圆while( 1 ){y = y +vy;cleardevice();//清空画布fillcircle(x,y,r);//在坐标(100,90)处画半径20像素的圆Sleep(100);//系统睡眠100msvy = vy + a; //重力加速度施加时 注意y轴方向是朝下的 if( (y+r)>=High ){ vy = -vy ; }}getch();closegraph();return 0;
}
一.实现无反弹的小球
检测小球位置,当小球到达窗口下边界时,强制将y轴速度设置为0;
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
int main(){int Width=600,High=600;//窗口尺寸float x=100; //小球x坐标float y=20; //小球y坐标float vy = 10; //小球y轴速度vint r = 20;//小球半径rfloat a = 2;//重力加速度设置为2initgraph(Width,High); //生成600X600像素的窗口cleardevice();//清空画布fillcircle(x,y,r);//在坐标(100,90)处画半径20像素的圆while( 1 ){y = y +vy;cleardevice();//清空画布fillcircle(x,y,r);//在坐标(100,90)处画半径20像素的圆Sleep(10);//系统睡眠100msvy = vy + a; //重力加速度施加时 注意y轴方向是朝下的 if( (y+r)>=High ){ vy = 0 ; y = High-r; }}getch();closegraph();return 0;
}
二.键盘按键控制小球弹跳
当有键盘按键按下时,kbhit( )返回1,否则返回0;
1.1 使用kbhit( )检测按键 实现小球弹跳
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
int main(){int Width=600,High=600;//窗口尺寸float x=100; //小球x坐标float y=20; //小球y坐标float vy = 10; //小球y轴速度vint r = 20;//小球半径rfloat a = 10;//重力加速度设置为10initgraph(Width,High); //生成600X600像素的窗口cleardevice();//清空画布fillcircle(x,y,r);//在坐标(100,90)处画半径20像素的圆while( 1 ){ if( kbhit() ){ /* 检测按键 */getch(); /* 等待输入按键 */vy =vy -50; /* vy速度-50 即向上施加一个速度 */}y = y +vy;cleardevice();//清空画布fillcircle(x,y,r);//在坐标(100,90)处画半径20像素的圆Sleep(50);//系统睡眠100msvy = vy + a; //重力加速度施加时 注意y轴方向是朝下的 if( (y+r)>=High ){ vy = 0 ; y = High-r; }//到达边界重置y值和y速度}getch();closegraph();return 0;
}
思考:如何设置弹跳按键为固定的某个按键呢?
1.2 字符与编码
使用<conio.h>的函数kbhit( )检测键盘按键输入;
每个键盘按钮输入的字符不同;
通过getch( )函数读取缓冲区字符。
示例
#include <conio.h>
#include <stdio.h>
int main(){char n = 0;while(1){if( kbhit( ) == 1 ){//判断按键是否按下n = getch( );//获取缓冲区输入的按键 存储到n中printf("The Key is %c\n",n);//以字符格式打印nprintf("The Key is %d\n",n);//以整数格式打印n}}return 0;
}
按下键盘上的a按键时,输出结果:
上述程序中,变量n是一个字符型变量( char )。
字符型变量也属于整数类型变量,由于char类型变量只占用1个字节的存储空间,它所能存储的数据范围比int小得多( 64位系统中 int 为 4 字节),只能表示 -128~127 范围的数值。
在计算机中,字符是通过特定的编码表示的。
英文字符的编码中使用较多的属于ASCII码,它们的对应关系如下:
https://c.biancheng.net/c/ascii/
可以看到,a对应的acsii码为97,这正好与上述程序中用%d(整数)格式输出变量n的值对应。
编码数值与字符的等效
示例1:赋值整数 输出字符 则字符是整数对应的ACSII编码的字符
我们可以将上述ACSII码表中的值拿来使用,例如表示字符’b’,对应的ACSII编码值为98;
建立一个char(字符型)变量x,给其初值98,然后用字符型格式(%c)打印x
#include <stdio.h>
int main(){char x = 98;printf("%c\n", x);//以字符格式输出xreturn 0;
}
输出结果为: b
示例2:赋值字符 输出字符
给变量x赋值字符b ;
注: 字符常量使用单引号 ’ ’ 表示。
#include <stdio.h>
int main(){char x = 'b';printf("%c\n", x);//以字符格式输出xreturn 0;
}
输出结果为: b
示例3: 赋值字符 输出整数 整数是对应ACSII编码表字符的整数
给变量x赋值字符b ;
#include <stdio.h>
int main(){char x = 'b';printf("%d\n", x);//以整数格式输出xreturn 0;
}
输出结果为: 98
示例4: 赋值整数 输出整数 - char变量也可以作为整数型变量使用
给变量x赋值字符b ;
注: 字符常量使用单引号 ’ ’ 表示。
#include <stdio.h>
int main(){char x = 98;printf("%d\n", x);//以字符格式输出xreturn 0;
}
输出结果为: 98
1.3 使用键盘按键w控制小球弹跳
使用if条件语句检查getch( )的值,如果是字符’w’,则控制小球弹跳。
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
int main(){int Width=600,High=600;//窗口尺寸float x=100; //小球x坐标float y=20; //小球y坐标float vy = 10; //小球y轴速度vint r = 20;//小球半径rfloat a = 10;//重力加速度设置为10initgraph(Width,High); //生成600X600像素的窗口cleardevice();//清空画布fillcircle(x,y,r);//在坐标(100,90)处画半径20像素的圆char input;while( 1 ){ if( kbhit() ){ /* 检测按键 */input = getch(); /* 等待输入按键 */if( input = 'w' ){/* 如果输入字符为w */vy =vy -50; /* vy速度-50 即向上施加一个速度 */}}y = y +vy;cleardevice();//清空画布fillcircle(x,y,r);//在坐标(100,90)处画半径20像素的圆Sleep(50);//系统睡眠100msvy = vy + a; //重力加速度施加时 注意y轴方向是朝下的 if( (y+r)>=High ){ vy = 0 ; y = High-r; }//到达边界重置y值和y速度}getch();closegraph();return 0;
}
三. 生成移动的方块
使用easyx库函数fillrectangle生成方块。
fillrectangle( )函数的输入参数如下:
fillrectangle(<矩形左上角的点的X坐标>,<矩形左上角点的Y坐标>,
矩形右下角点的X坐标,矩形右下角点的Y坐标 )
3.1 在窗口右下角绘制方块
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
int main(){int Width=600,High=600;//窗口尺寸int Rectangle_W = 20,Rectangle_H = 30;/* 矩形宽度、高度 */float Rectangle_X=Width-Rectangle_W;float Rectangle_Y = High - Rectangle_H; /* 矩形的x、y坐标设定为矩形左上角的坐标 */float x=100; //小球x坐标float y=20; //小球y坐标float vy = 10; //小球y轴速度vint r = 20;//小球半径rfloat a = 10;//重力加速度设置为10initgraph(Width,High); //生成600X600像素的窗口cleardevice();//清空画布fillcircle(x,y,r);//在坐标(100,90)处画半径20像素的圆char input;while( 1 ){ if( kbhit() ){ /* 检测按键 */input = getch(); /* 等待输入按键 */if( input = 'w' ){/* 如果输入字符为w */vy =vy -50; /* vy速度-50 即向上施加一个速度 */}}y = y +vy;cleardevice();//清空画布fillcircle(x,y,r);//在坐标(100,90)处画半径20像素的圆fillrectangle(Rectangle_X , Rectangle_Y,Rectangle_X+Rectangle_W, Rectangle_Y+Rectangle_H);/* 绘制矩形: */Sleep(50);//系统睡眠100msvy = vy + a; //重力加速度施加时 注意y轴方向是朝下的 if( (y+r)>=High ){ vy = 0 ; y = High-r; }//到达边界重置y值和y速度}getch();closegraph();return 0;
}
3.2 移动的方块
设定矩形的速度 float RVx = -10 ;
并在每次循环时更新矩形的x位置;
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
int main(){int Width=600,High=600;//窗口尺寸int Rectangle_W = 20,Rectangle_H = 30;/* 矩形宽度、高度 */float Rectangle_X=Width-Rectangle_W;float Rectangle_Y = High - Rectangle_H; /* 矩形的x、y坐标设定为矩形左上角的坐标 */float RVx = -10; /*矩形速度*/float x=100; //小球x坐标float y=20; //小球y坐标float vy = 10; //小球y轴速度vint r = 20;//小球半径rfloat a = 10;//重力加速度设置为10initgraph(Width,High); //生成600X600像素的窗口cleardevice();//清空画布fillcircle(x,y,r);//在坐标(100,90)处画半径20像素的圆char input;while( 1 ){ if( kbhit() ){ /* 检测按键 */input = getch(); /* 等待输入按键 */if( input = 'w' ){/* 如果输入字符为w */vy =vy -50; /* vy速度-50 即向上施加一个速度 */}}y = y +vy;Rectangle_X = Rectangle_X + RVx;cleardevice();//清空画布fillcircle(x,y,r);//在坐标(100,90)处画半径20像素的圆fillrectangle(Rectangle_X , Rectangle_Y,Rectangle_X+Rectangle_W, Rectangle_Y+Rectangle_H);/* 绘制矩形: */Sleep(50);//系统睡眠100msvy = vy + a; //重力加速度施加时 注意y轴方向是朝下的 if( (y+r)>=High ){ vy = 0 ; y = High-r; }//到达边界重置y值和y速度}getch();closegraph();return 0;
}
3.3 重置方块位置
当方块的x坐标到达窗口最左侧时(即小于0),重置方块位置到窗口最右侧。
if( Rectangle_X < 0 ){ Rectangle_X = Width;}
完整代码:
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
int main(){int Width=600,High=600;//窗口尺寸int Rectangle_W = 20,Rectangle_H = 30;/* 矩形宽度、高度 */float Rectangle_X=Width-Rectangle_W;float Rectangle_Y = High - Rectangle_H; /* 矩形的x、y坐标设定为矩形左上角的坐标 */float RVx = -10; /*矩形速度*/float x=100; //小球x坐标float y=20; //小球y坐标float vy = 10; //小球y轴速度vint r = 20;//小球半径rfloat a = 10;//重力加速度设置为10initgraph(Width,High); //生成600X600像素的窗口cleardevice();//清空画布fillcircle(x,y,r);//在坐标(100,90)处画半径20像素的圆char input;while( 1 ){ if( kbhit() ){ /* 检测按键 */input = getch(); /* 等待输入按键 */if( input = 'w' ){/* 如果输入字符为w */vy =vy -50; /* vy速度-50 即向上施加一个速度 */}}y = y +vy;Rectangle_X = Rectangle_X + RVx;if( Rectangle_X < 0 ){ Rectangle_X = Width;}cleardevice();//清空画布fillcircle(x,y,r);//在坐标(100,90)处画半径20像素的圆fillrectangle(Rectangle_X , Rectangle_Y,Rectangle_X+Rectangle_W, Rectangle_Y+Rectangle_H);/* 绘制矩形: */Sleep(50);//系统睡眠100msvy = vy + a; //重力加速度施加时 注意y轴方向是朝下的 if( (y+r)>=High ){ vy = 0 ; y = High-r; }//到达边界重置y值和y速度}getch();closegraph();return 0;
}
四.碰撞检测
碰撞检测本质是判断小球与方块的区域是否存在重叠。
什么情况下小球与方块位置发生重叠呢?
我们先来关注一下什么时候不重叠:(注意y轴正方向朝下)
此处用(x,y)表示小球中心点坐标,用(Rx,Ry)表示矩形左上角的点的坐标;
r表示小球半径;RW、RH分别表示矩形宽度、高度
4.1 不重叠的情况
(1)小球的右边界<方块的左边界 这时不相交
(x+r)<(Rx)
(2)小球左边界>矩形右边界时 不相交
(x-r)>(Rx+RW)
(3)小球下边界>矩形上边界时 不相交
(y+r)<(Ry)
(4)小球上边界<矩形下边界时 不相交
由于小球不可能移动到窗口最下端以下,所以情况(4)一般不存在。但是仍然可以表示。
(y-r)>(Ry+RH)
4.2 不重叠情况的集合
(x+r)<(Rx)
(x-r)>(Rx+RW)
(y+r)<(Ry)
(y-r)>(Ry+RH)
满足上述任意一条关系运算表达式为真(true),即可判断不发生碰撞。
故此处可以用逻辑运算"或"表示不发生碰撞的情况:
((x+r)<(Rx)) || ((x-r)>(Rx+RW)) || ((y+r)<(Ry)) || ((y-r)>(Ry+RH))
其中"||"表示"逻辑或"运算。
其它的几种逻辑运算符如下:
运算符 | 含义 |
---|---|
&& | 与 |
|| | 或 |
! | 非(取反) |
可以再加上一个"非"运算,表示"发生碰撞的情况":
!(((x+r)<(Rx)) || ((x-r)>(Rx+RW)) || ((y+r)<(Ry)) || ((y-r)>(Ry+RH)))
故判断碰撞的语句可以这样表示:
if( !(((x+r)<(Rx)) || ((x-r)>(Rx+RW)) || ((y+r)<(Ry)) || ((y-r)>(Ry+RH))) )
{//发生碰撞时的程序
}
4.3 碰撞的检测
最终代码如下所示: 在碰撞发生时,终端输出字符串 Crash!
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
int main(){int Width=600,High=600;//窗口尺寸int Rectangle_W = 20,Rectangle_H = 30;/* 矩形宽度、高度 */float Rectangle_X=Width-Rectangle_W;float Rectangle_Y = High - Rectangle_H; /* 矩形的x、y坐标设定为矩形左上角的坐标 */float RVx = -10; /*矩形速度*/float x=100; //小球x坐标float y=20; //小球y坐标float vy = 10; //小球y轴速度vint r = 20;//小球半径rfloat a = 10;//重力加速度设置为10initgraph(Width,High); //生成600X600像素的窗口cleardevice();//清空画布fillcircle(x,y,r);//在坐标(100,90)处画半径20像素的圆char input;while( 1 ){ if( kbhit() ){ /* 检测按键 */input = getch(); /* 等待输入按键 */if( input = 'w' ){/* 如果输入字符为w */vy =vy -50; /* vy速度-50 即向上施加一个速度 */}}y = y +vy;Rectangle_X = Rectangle_X + RVx;if( !(((x+r)<(Rectangle_X)) || ((x-r)>(Rectangle_X+Rectangle_W)) || ((y+r)<(Rectangle_Y)) || ((y-r)>(Rectangle_Y+Rectangle_H))) ){printf(" Crash! ");}if( Rectangle_X < 0 ){ Rectangle_X = Width;}cleardevice();//清空画布fillcircle(x,y,r);//在坐标(100,90)处画半径20像素的圆fillrectangle(Rectangle_X , Rectangle_Y,Rectangle_X+Rectangle_W, Rectangle_Y+Rectangle_H);/* 绘制矩形: */Sleep(50);//系统睡眠100msvy = vy + a; //重力加速度施加时 注意y轴方向是朝下的 if( (y+r)>=High ){ vy = 0 ; y = High-r; }//到达边界重置y值和y速度}getch();closegraph();return 0;
}
五. 方块大小的随机控制
使用rand( )函数,获取0~32767之间的伪随机数。
知识点: 求余数 %
使用%(求余数运算符)对一个整数求余。
例如42除10 余 2,则 42%10 结果为2。
使用求余运算符%获取指定范围的随机数
rand( ) / 10 可以获得0~9之间的随机数;
rand( ) / 33 可以获得0~32之间的随机数;