这里,我们有一个方块和一堵墙,我们希望能确保方块不能穿墙而过。为了能做到这一点,我们要检查方块和墙是否发生了碰撞。
在这里我们会讲一种简单的方法来检测两个物体之间的碰撞。
//墙 SDL_Rect wall;
这是我们要拿来给方块撞的墙,没什么好多解释的了吧?
//方块
class Square
{
private:
//方块的碰撞检测矩形
SDL_Rect box;
//方块的速度
int xVel, yVel;
public:
//初始化方块
Square();
//按键时调整方块速度的函数
void handle_input();
//移动方块
void move();
//在屏幕上显示方块
void show();
};
这便是我们要拿来撞墙的方块类了。你多半已经注意到,这和我们先前在运动一课中使用的圆点类颇为类似。
唯一显著的区别是,方块的X,Y坐标都被储存在一个SDL_Rect结构体中,这样方块的大小也能储存其中。除此之外,别的都一样。
唯一显著的区别是,方块的X,Y坐标都被储存在一个SDL_Rect结构体中,这样方块的大小也能储存其中。除此之外,别的都一样。
bool check_collision( SDL_Rect A, SDL_Rect B )
{
//矩形的各边
int leftA, leftB;
int rightA, rightB;
int topA, topB;
int bottomA, bottomB;
//计算矩形A的各边
leftA = A.x;
rightA = A.x + A.w;
topA = A.y;
bottomA = A.y + A.h;
//计算矩形B的各边
leftB = B.x;
rightB = B.x + B.w;
topB = B.y;
bottomB = B.y + B.h;
现在我们开始写碰撞检测的函数代码了。
首先我们载入
首先我们载入
SDL_Rect并计算它们各边的位置。
//如果矩形A的任意一条边都在矩形B外
if( bottomA <= topB )
{
return false;
}
if( topA >= bottomB )
{
return false;
}
if( rightA <= leftB )
{
return false;
}
if( leftA >= rightB )
{
return false;
}
//如果没有一条边在矩形B外
return true;
}
在此我们检测碰撞。
矩形碰撞检测的基本原理是,检查一个矩形的四条边是否都在另一个矩形的外侧。
好好想想便会发现,如果矩形的四条边都在另一个矩形的外侧的话,它们之间就不可能发生碰撞,就像这样。
不管是哪种情况,B的每条边都在A外面。
接下来,我们要考虑另一种情况:
矩形B至少有一条边再矩形A中的情况。
这样我们能确定矩形B中没有一条边再矩形A内,如果没有碰撞发生我们返回false,如果有则返回true。
注意我使用的是大于等于和小于等于。也就是说只有当两个矩形重合的时候才会被判定碰撞。如果你使用的是大于和小于,那么图中的两个矩形:
即便只是靠在一起也会被判定为碰撞。
究竟该使用哪种模式要视实际情况而定。
矩形碰撞检测的基本原理是,检查一个矩形的四条边是否都在另一个矩形的外侧。
好好想想便会发现,如果矩形的四条边都在另一个矩形的外侧的话,它们之间就不可能发生碰撞,就像这样。
接下来,我们要考虑另一种情况:
这样我们能确定矩形B中没有一条边再矩形A内,如果没有碰撞发生我们返回false,如果有则返回true。
注意我使用的是大于等于和小于等于。也就是说只有当两个矩形重合的时候才会被判定碰撞。如果你使用的是大于和小于,那么图中的两个矩形:
究竟该使用哪种模式要视实际情况而定。
Square::Square()
{
//初始化坐标
box.x = 0;
box.y = 0;
//设置矩形的大小
box.w = SQUARE_WIDTH;
box.h = SQUARE_HEIGHT;
//初始化速度
xVel = 0;
yVel = 0;
}
在方块的构造函数中我们对方块的坐标,大小,和速度像以前一样进行了初始化。
void Square::move()
{
//将方块左右移动
box.x += xVel;
//当方块移出屏幕或是与墙发生了碰撞
if( ( box.x < 0 ) || ( box.x + SQUARE_WIDTH > SCREEN_WIDTH ) || ( check_collision( box, wall ) ) )
{
//退回去
box.x -= xVel;
}
//将方块上下移动
box.y += yVel;
//当方块移出屏幕或是与墙发生了碰撞
if( ( box.y < 0 ) || ( box.y + SQUARE_HEIGHT > SCREEN_HEIGHT ) || ( check_collision( box, wall ) ) )
{
//退回去
box.y -= yVel;
}
}
这便是我们用来移动方块的
move()函数。每次移动都要检测方块是否移出窗口或是撞上了墙,如果方块走到了不该走的地方,我们便要撤销刚才的移动。 //设置墙壁
wall.x = 300;
wall.y = 40;
wall.w = 40;
wall.h = 400;
在我们的主函数中,在初始化并加载了所有的东西后,我们便要设置墙体的参数。
//当用户还未退出
while( quit == false )
{
//启动帧计时器
fps.start();
//当有事件需要处理
while( SDL_PollEvent( &event ) )
{
//处理方块的事件
mySquare.handle_input();
//当用户关闭窗口
if( event.type == SDL_QUIT )
{
//退出程序
quit = true;
}
}
//移动方块
mySquare.move();
//填充白色背景
SDL_FillRect( screen, ≻reen->clip_rect, SDL_MapRGB( screen->format, 0xFF, 0xFF, 0xFF ) );
//显示墙体
SDL_FillRect( screen, &wall, SDL_MapRGB( screen->format, 0x77, 0x77, 0x77 ) );
//将方块显示在屏幕上
mySquare.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() );
}
}
这就是我们的主循环了。我们依次移动方块,填充背景,显示墙体,显示方块,最后更新屏幕并限制帧率。