这里,我们有一个方块和一堵墙,我们希望能确保方块不能穿墙而过。为了能做到这一点,我们要检查方块和墙是否发生了碰撞。
在这里我们会讲一种简单的方法来检测两个物体之间的碰撞。
//墙 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() ); } }
这就是我们的主循环了。我们依次移动方块,填充背景,显示墙体,显示方块,最后更新屏幕并限制帧率。