#include "SDL/SDL.h" #include "SDL/SDL_image.h" #include <string> #include <vector> #include <cmath>
本节课将要使用距离公式,所以我们包含了math头文件。
//表示一个圆形的结构体
struct Circle
{
int x, y;
int r;
};
在这个程序中,我们需要创建我们的Circle结构体。"x" 和 "y"是圆心的坐标。"r"是半径。
//点
class Dot
{
private:
//点的区域
Circle c;
//点的速度
int xVel, yVel;
public:
//初始化变量
Dot();
//处理按键并调整点的速度
void handle_input();
//移动点的位置
void move( std::vector<SDL_Rect> &rects, Circle &circle );
//在屏幕上显示点
void show();
};
这里是另一个版本的Dot类。
所有内容和之前的一样,除了两处不同。这次我们使用Circle结构体替代了SDL_Rect的向量。并且,在move()函数中我们检测了SDL_Rect的vector与Circle之间的碰撞。
所有内容和之前的一样,除了两处不同。这次我们使用Circle结构体替代了SDL_Rect的向量。并且,在move()函数中我们检测了SDL_Rect的vector与Circle之间的碰撞。
译者注:关于C++标准库中的向量vector,上一课中编者已作简单说明(点此回看)。这里重申一下,vector相当于数组。
double distance( int x1, int y1, int x2, int y2 )
{
//返回两点间的距离
return sqrt( pow( x2 - x1, 2 ) + pow( y2 - y1, 2 ) );
}
我们写的这个函数告诉我们给定两点间的距离。这是此程序中用到的唯一的真正的数学。
对于使用visual studio的小伙伴们来说,他们也许需要将那些整数转换成double类型。
对于使用visual studio的小伙伴们来说,他们也许需要将那些整数转换成double类型。
bool check_collision( Circle &A, Circle &B )
{
//如果两圆圆心间的距离小于两圆半径的和
if( distance( A.x, A.y, B.x, B.y ) < ( A.r + B.r ) )
{
//两圆已经碰到了一起
return true;
}
//如果不是
return false;
}
检测两圆之间的碰撞是非常简单的。你只需要检查一下两圆圆心的距离是否小于它们的半径之和。
如果是小于的话,碰撞发生,否则没有碰撞。
如果是小于的话,碰撞发生,否则没有碰撞。
bool check_collision( Circle &A, std::vector<SDL_Rect> &B )
{
//碰撞盒子上距离圆心最近的点的坐标
int cX, cY;
//遍历所有盒子
for( int Bbox = 0; Bbox < B.size(); Bbox++ )
{
这个函数检测圆与矩形向量之间的碰撞。检测一个圆与一个矩形之间的碰撞有点复杂。为了检测一个圆与一个碰撞盒子的碰撞,你必须找到碰撞盒子上距离圆心最近的点。
//寻找最近的x坐标
if( A.x < B[ Bbox ].x )
{
cX = B[ Bbox ].x;
}
如果圆心在盒子的左侧,那么最近点的x坐标等于碰撞盒子的x坐标。
else if( A.x > B[ Bbox ].x + B[ Bbox ].w )
{
cX = B[ Bbox ].x + B[ Bbox ].w;
}
如果圆心在盒子的右侧,那么最近点的x坐标等于碰撞盒子的右侧边的x坐标。
else
{
cX = A.x;
}
如果圆心坐标既不在碰撞盒子的左侧,又不在盒子右侧,那么圆心的x坐标就在盒子的内部。
//寻找最近的y坐标
if( A.y < B[ Bbox ].y )
{
cY = B[ Bbox ].y;
}
else if( A.y > B[ Bbox ].y + B[ Bbox ].h )
{
cY = B[ Bbox ].y + B[ Bbox ].h;
}
else
{
cY = A.y;
}
我们采用和上面一样的方法寻找最近的y坐标。
//如果最近点在圆的内部
if( distance( A.x, A.y, cX, cY ) < A.r )
{
//这个盒子和圆已经发生碰撞
return true;
}
}
//如果没有与这些形状碰撞
return false;
}
如果碰撞盒子上距离圆心最近的点在圆的内部,那么圆和盒子发生重叠。这里我们一直遍历所有的碰撞盒子直到发现一个碰撞,或者所有的盒子都被检查过并且未发生任何碰撞。
//构建形状
std::vector<SDL_Rect> box( 1 );
Circle otherDot;
//Set the shapes' attributes
box[ 0 ].x = 60;
box[ 0 ].y = 60;
box[ 0 ].w = 40;
box[ 0 ].h = 40;
otherDot.x = 30;
otherDot.y = 30;
otherDot.r = DOT_WIDTH / 2;
In the main() function we create the 2 shapes the Dot is going to interact with.
在
main()函数中,我们创建了2个将与点交互的形状。
//当用户还未推出
while( quit == false )
{
//启动帧计时器
fps.start();
//当有事件需要处理
while( SDL_PollEvent( &event ) )
{
//为点处理事件
myDot.handle_input();
//如果用户叉掉了窗口
if( event.type == SDL_QUIT )
{
//退出程序
quit = true;
}
}
//移动点
myDot.move( box, otherDot );
//用白色填充屏幕
SDL_FillRect( screen, ≻reen->clip_rect, SDL_MapRGB( screen->format, 0xFF, 0xFF, 0xFF ) );
//显示盒子
SDL_FillRect( screen, &box[ 0 ], SDL_MapRGB( screen->format, 0x00, 0x00, 0x00 ) );
//显示其他的点
apply_surface( otherDot.x - otherDot.r, otherDot.y - otherDot.r, dot, screen );
//显示我们的点
myDot.show();
//更新屏幕
if( SDL_Flip( screen ) == -1 )
{
return 1;
}
//捕获帧率
if( fps.get_ticks() < 1000 / FRAMES_PER_SECOND )
{
SDL_Delay( ( 1000 / FRAMES_PER_SECOND ) - fps.get_ticks() );
}
}
这里是main循环,和前面的基本相同。但我想指出一点。
当我们显示其他点时,我们并不将图片blit到点的坐标。因为那个坐标是圆心的坐标,所以我们必须将点的图像blit到左上角的位置。我们可以通过将坐标减去半径来完成这件事。
当我们显示其他点时,我们并不将图片blit到点的坐标。因为那个坐标是圆心的坐标,所以我们必须将点的图像blit到左上角的位置。我们可以通过将坐标减去半径来完成这件事。
译者注:其实我想说,经过几番纠结,我还是将“X out the window”直译为“叉掉窗口”算了……土一点算了,没必要装高大上……后面的都这样喽。