OpenGL矩形碰撞检测存异常间隙 checkCollisionRect()bug排查
矩形碰撞检测物体间隙问题
问题表现
- 自研碰撞检测系统在运行阻挡穿透逻辑时,发生碰撞的两个矩形对象间会出现无规律细小间隙,正常碰撞接触状态下两物体不应存在空隙
- 已定位问题出在矩形碰撞处理函数
checkCollisionRect()
问题复现代码
#include <stdio.h> #include <stdlib.h> #include <GL/glut.h> #define or || #define and && #define pCharacter "%c \n" #define pInteger "%d \n" #define pFloat "%f \n" #define pDouble "%lf \n" #define pUnsighned "%u \n" #define pString "%s \n" /* format string:| input type: %c | character %d | digit (integer) %f | float %lf | double %u | unsigned %s | string */ int width=1000; //Screen width int height=500; //Screen height int px=200; //Player pos x int py=100; //Player pos y float playerSpeed=5.0; void drawRect(int tlx,int tly,int boxWidth,int boxHeight){ glBegin(GL_POLYGON); glVertex2f(tlx,tly+boxHeight); //Bottom Left glVertex2f(tlx,tly); //Top Left glVertex2f(tlx+boxWidth,tly); //Top Right glVertex2f(tlx+boxWidth,tly+boxHeight); //Bottom Right glEnd(); } void checkCollisionRect(x1,y1,w1,h1,x2,y2,w2,h2){ if(x1>x2+w2 or x1+w1<x2 or y1>y2+h2 or y1+h1<y2){ glColor3f(0,1,0); } else{ glColor3f(1,0,0); if(x1<x2+w2 and x1+w1>x2 and y1<y2+h2){ py-=playerSpeed; printf("top\n"); } if(y1+h1>y2 and y1<y2+h2 and x1<x2+w2){ px-=playerSpeed; printf("left\n"); } if(x1<x2+w2 and x1+w1>x2 and y1+h1>y2){ py+=playerSpeed; printf("bottom\n"); } if(y1+h1>y2 and y1<y2+h2 and x1+w1>x2){ px+=playerSpeed; printf("right\n"); } } } void drawSquare(px,py,size){ glPointSize(size); glBegin(GL_POINTS); glVertex2i(px,py); glEnd(); } void Buttons(unsigned char key,int x,int y){ if(key=='a'){px-=playerSpeed;} if(key=='d'){px+=playerSpeed;} if(key=='w'){py-=playerSpeed;} if(key=='s'){py+=playerSpeed;} } void display(){ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //-----------------------Draw---------------------- checkCollisionRect(px,py,10,10,200,200,100,100); drawRect(200,200,100,100); //rect drawRect(px,py,10,10); //player //------------------------------------------------- glutSwapBuffers(); glutPostRedisplay(); } void init(){ glClearColor(0.3,0.3,0.3,0); gluOrtho2D(0,width,height,0); } void main(int argc, char** argv){ glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); glutInitWindowSize(width,height); glutCreateWindow("OpenGL"); init(); glutDisplayFunc(display); glutKeyboardFunc(Buttons); glutMainLoop(); }
问题根因
现有checkCollisionRect()逻辑存在三个核心错误,直接导致间隙出现:
- 多方向同时响应错误:只要检测到碰撞,就会同时满足多个方向的判断条件,同时触发x、y轴多个方向的位置回退,多余的位移会把物体推离接触面
- 修正距离不合理:不管实际碰撞穿透深度是多少,统一固定回退5像素(
playerSpeed的数值),当实际穿透深度小于5像素时,多推的距离就会形成可见间隙 - 代码规范问题:
checkCollisionRect、drawSquare两个函数的形参未声明类型,C语言默认按int处理,后续如果改用浮点坐标会直接出现计算错误
修复方法
标准AABB碰撞响应的正确逻辑是:碰撞发生后只选重叠深度最小的轴做位置修正,修正距离刚好等于该轴的重叠深度,把物体推到刚好和障碍物贴合的位置即可。
修复后的碰撞检测函数参考:
// 补全形参类型,用浮点类型避免整数截断误差 void checkCollisionRect(float x1,float y1,float w1,float h1,float x2,float y2,float w2,float h2){ // 无碰撞情况 if(x1>x2+w2 or x1+w1<x2 or y1>y2+h2 or y1+h1<y2){ glColor3f(0,1,0); return; } // 发生碰撞 glColor3f(1,0,0); // 计算两个矩形的中心坐标 float pCenterX = x1 + w1/2; float pCenterY = y1 + h1/2; float oCenterX = x2 + w2/2; float oCenterY = y2 + h2/2; // 计算x、y轴的重叠深度 float overlapX = (w1 + w2)/2 - abs(pCenterX - oCenterX); float overlapY = (h1 + h2)/2 - abs(pCenterY - oCenterY); // 只修正重叠深度更小的轴 if(overlapX < overlapY){ // x轴方向修正 if(pCenterX < oCenterX){ px -= overlapX; // 从左侧碰撞,往左推刚好贴合 }else{ px += overlapX; // 从右侧碰撞,往右推刚好贴合 } }else{ // y轴方向修正 if(pCenterY < oCenterY){ py -= overlapY; // 从上方碰撞,往上推刚好贴合 }else{ py += overlapY; // 从下方碰撞,往下推刚好贴合 } } }
替换原有函数后,碰撞时物体就会刚好贴在障碍物表面,不会出现间隙,也不会出现多方向同时推物体的逻辑错误。
内容的提问来源于stack exchange,提问作者Saw
相关产品推荐
相关产品推荐

