碰撞检测教程C++.docx

上传人:b****7 文档编号:10071975 上传时间:2023-02-08 格式:DOCX 页数:27 大小:72.66KB
下载 相关 举报
碰撞检测教程C++.docx_第1页
第1页 / 共27页
碰撞检测教程C++.docx_第2页
第2页 / 共27页
碰撞检测教程C++.docx_第3页
第3页 / 共27页
碰撞检测教程C++.docx_第4页
第4页 / 共27页
碰撞检测教程C++.docx_第5页
第5页 / 共27页
点击查看更多>>
下载资源
资源描述

碰撞检测教程C++.docx

《碰撞检测教程C++.docx》由会员分享,可在线阅读,更多相关《碰撞检测教程C++.docx(27页珍藏版)》请在冰豆网上搜索。

碰撞检测教程C++.docx

碰撞检测教程C++

简介

本文是阐述如何在2D动作游戏中进行精确而高效的碰撞检测。

这里的碰撞是基于多边形而不是基于精灵的。

这两者之间在设计上会有不同。

基于精灵的碰撞检测是通过精灵之间的重叠的像素来完成的。

而多边形使用向量数学来精确计算交点,时间和碰撞方向。

虽然多边形仅仅是精灵的一个近似,但是它比精灵系统要高级。

∙可以精确模拟逼真的简单物理学,例如反弹,摩擦,斜面的滑行

∙碰撞检测可以更精确的用于高速精灵系统。

在基于精灵的系统中,如果物体移动过快就会在跳过另一个物体。

∙基于向量数学因此可以扩展到3D,然而精灵碰撞系统被严格限制在2D的情况下。

特性

本文使用的算法只适用于凸多边形,例如三角形,四边形,六边形,圆形。

对于非凸多边形,你可以将其分解为多个凸多边形,例如三角形。

算法可以用于快速移动或慢速移动的多边形。

不管物体移动多快,碰撞都不会丢失。

它也可以处理重叠的问题,并促使交叠物体分离。

演示也支持分割多边形交叉。

这可以用于子弹的建模。

同时提供了简单的物体系统,弹力,一些基本的摩擦和静摩擦力。

用于确保物体不会从斜面上滑落。

有一个刚体系统的例子,使用了ChrsiHecker的物理教程。

限制

有序碰撞。

就是说并不是有序的进行碰撞。

这对于快速移动的物体会出现一定的问题。

一旦碰撞被检测到,它就被直接处理了。

理想状态下你可能需要找到一个碰撞点并处理它,然后寻找更多的碰撞。

但是对于2D动作游戏,这通常是不必要的。

一、分离坐标轴方法

这个方法是碰撞检测的核心。

它的规则非常简单并且非常易于实现。

这个方法也非常快并且非常可靠,因为计算中没有使用除法操作,下面给出一个简单的基于两个BOX的碰撞检测的例子。

算法试图在两个物体之间找到一个合适平面,如果这个平面存在,那么物体就没有相交。

为了测试物体是否是分开的,简单的方法是投影这个物体到平面的法线上,并比较两者之间的间距看二者是否重叠。

显然有无数的平面可以用来分割两个物体。

但是已经经过证明的是:

你只需要使用一部分平面来进行测试,对于BOX从上图中可以看出平面的法线为BOXB的长轴。

对于BOX来说需要测试的分割平面是那些法线等于两个BOX的轴向的平面。

因此对于两个BOX来说,你只需要测试4个分割平面即可。

在这四个平面里,一旦发现一个分割平面可以分割BOX那么你就可以断定这两个BOX是不相交的。

如果四个平面都不能分割BOX,那么这两个BOX一定是相交的,也就是出现了碰撞。

可以扩展这个算法到普通的多边形,算法是相同的,只用需要测试的平面的数量改变了。

并且分割平面在每个多边形边的垂直方向上又有一个法线。

在下图中,你可以看到两个分割平面用于测试。

在红色的平面上你可以看到两个间隔是重叠的。

然而,在蓝色的平面上间隔是不重叠的,因此,蓝色的平面的是分割平面,因此物体是不相交的。

现在,我们有一个算法来检测两个多边形是否是相交的。

代码可以分为三个部分:

a)生成需要测试的分离轴

b)计算每一个多边形在分离轴法线上的投影

c)检测这些投影是否相交

boolIntersect(PolygonA,PolygonB)

{

for(I=0;I

{

VectorN=Vector(-A.EdgeDir[I].y,A.EdgeDir[I].x);

if(AxisSeparatePolygons(N,A,B))returnfalse;

}

for(I=0;I

{

VectorN=Vector(-B.EdgeDir[i].y,B.EdgeDir[I].x);

if(AxisSeparatePolygons(N,A,B))returnfalse;

}

returntrue;

}

voidCalculateInterval(VectorAxis,PolygonP,float&min,float&max)

{

floatd=AxisdotP.vertex[0];//从坐标原点开始计算向量

min=max=d;

for(I=0;I

{

floatd=P.vertex[I]dotAxis;

if(dmax)max=d;

}

}

算法检测2D多边形之间的碰撞,这个算法非常的快速和适用。

边的方向不需要单位化,因此你可以避免存贮边的方向,并通过多边形的顶点直接得到边的方向。

for(J=A.num_vertices-1,I=0;I

{

VectorE=A.vertex[I]-A.vertex[J];

VectorN=Vector(-E.y,E.x);

if(AxisSeparatePolygons(N,A,B))

returnfalse;

}

二、用于碰撞响应的扩展分离坐标轴方法

检测多边形相交是非常有用的方法,但是可以做更多的事情。

当多边形相交时,我想将他们移开以避免他们相交。

分离轴的方法可以非常好的用于这种情况,但是还需要作一些额外的工作。

必须返回相交的深度,和推开多边形将它们分离的方向。

相交的深度和方向的组合称为MTD,或者最小平移距离。

这是用于将物体分离的的最小向量。

为了计算MTD,我们可以使用分离坐标轴。

 

当物体相交时,我们可以计算两个物体在每一个分离轴上的投影间隔。

两个间隔交叠的部分提供了一个推动向量,你需要将其应用到其中一个物体上以便物体在轴上的投影停止交叠

“推动向量”你需要应用于A上将A推开,这样就可以使A和B分开。

显然,不能沿着一个随机的轴来推开物体。

候选轴是投影在该轴上两个间隔之间交叠最小的那个。

并且这个推动向量提供了最小平移距离。

boolIntersect(PolygonA,PolygonB,Vector&MTD) 

{

//电位分离轴。

他们被转换成推动    

vectorsVectorAxis[32];    

//每个多边形最大的16个顶点的    

intiNumAxis=0;    

for(J=A.num_vertices-1,I=0;I

{       

VectorE=A.vertex[I]-A.vertex[J];       

Axis[iNumAxis++]=Vector(-E.y,E.x);        

if(AxisSeparatePolygons(N,A,B))          

returnfalse;    

}    

for(J=B.num_vertices-1,I=0;I

{       

VectorE=B.vertex[I]-B.vertex[J];       

Axis[iNumAxis++]=Vector(-E.y,E.x);           

if(AxisSeparatePolygons(N,A,B))          

returnfalse;    

}        

//找到所有的分离向量之间的MTD    

MTD=FindMTD(Axis,iNumAxis);    

//确保将向量a推动远离b    

VectorD=A.Position-B.Position;    

if(DdotMTD<0.0f)       

MTD=-MTD;    

returntrue; }

 boolAxisSeparatePolygons(Vector&Axis,PolygonA,PolygonB) {    

floatmina,maxa;    

floatminb,maxb;    

CalculateInterval(Axis,A,mina,maxa);    

CalculateInterval(Axis,B,minb,maxb);    

if(mina>maxb||minb>maxa)       

returntrue;    

//查找间隔重叠    

floatd0=maxa-minb;    

floatd1=maxb-mina;    

floatdepth=(d0

d0:

d1;    

//将分离轴为推力矢量(重新恢复正常的轴乘区间重叠)    

floataxis_length_squared=AxisdotAxis;    

Axis*=depth/axis_length_squared;    

returnfalse; }

 VectorFindMTD(Vector*PushVectors,intiNumVectors) {    

VectorMTD=PushVector[0];    

floatmind2=PushVector[0]dotPushVector[0];    

for(intI=1;I

{       

floatd2=PushVector[I]*PushVector[I];       

if(d2

{          

mind2=d2;          

MTD=PushVector[I];       

}    

}    

returnMTD; 

}

 一旦得到了MTD向量,可以使用如下的方式将他们分开。

 

A.Postion+=MTD*0.5f;

B.Position-=MTD*0.5f;

显然,如果物体A是静态的,那么B将被完全的MTD推开(B.Position-=MTD)而A将不会被推开。

三、对快速移动的物体做进一步扩展

上述方法处理慢速移动物体时会取得非常好的效果。

但是当物体移动的非常快时,碰撞系统将失去准确性,丢失碰撞,或者允许物体之间相互穿越,这可不是我们所期望的。

 

这里我们还是使用分离坐标轴的方法,并进一步扩展,并使用该算法检测未来某时刻的碰撞和交叠。

 

原理还是相同的,可以使用下面的图片解释:

现在需要使用投影数学。

如果投影间隔没有相交,将速度投影到分离轴上,并计算两个间隔的碰撞时间。

 

相对于静态分离轴算法,我们需要测试一个扩展的轴。

显然这个是速度矢量轴。

 

那么我们现在有3个选择:

1. 间隔交叠

2. 间隔不相交,但是将在未来某个时刻发生碰撞

3. 间隔不相交,并且不会在未来发生碰撞

第三种可能性意味着物体不会在该帧处发生碰撞,而且分离轴真正分离了物体。

因此物体不会发生碰撞。

 

AxisSeparatePolygons()函数将反映这种现象,并返回重叠量或者碰撞时间。

为了区别两者,当检测到交叠时,将返回一个负值。

如果检测到未来的碰撞,将返回一个正值。

该函数看起来如下:

 

boolAxisSeparatePolygons(VectorAxis,PolygonA,PolygonB,VectorOffset,

VectorVel,float&t,floattmax);

这里Offset是多边形A和多边形B之间的相对距离,并且Vel是多边形A相对于多边形B的相对速度。

 

求解碰撞平面的算法与MTD非常相似。

只是碰撞将优于交叠,如果检测到未来的碰撞,将选择最新的一个。

 

如果没有发现碰撞并且只检测到了交叠,那么就像以前一样,选择交叠最小的那个轴。

 

碰撞检测函数将返回碰撞的法向,还有碰撞的深度(负值)和碰撞时间(正值)之一。

最后的伪代码如下…

boolCollide(constVector*A,intAnum,constVector*B,intBnum,constVector&xOffset,constVector&xVel,Vector&N,float&t) 

{    

if(!

A||!

B)

returnfalse;          

//Alltheseparationaxes    

//note:

amaximumof32verticesperpolyissupported    

VectorxAxis[64];

floattaxis[64];    

intiNumAxes=0;    

xAxis[iNumAxes]=Vector(-xVel.y,xVel.x);    

floatfVel2=xVel*xVel;    

if(fVel2>0.00001f)    

{       

if(!

IntervalIntersect(A,Anum,B,Bnum,xAxis[iNumAxes],xOffset,xVel,taxis[iNumAxes],t))           

returnfalse;       

iNumAxes++;    

}    

//测试分离轴A    

for(intj=Anum-1,i=0;i

{       

VectorE0=A[j];       

VectorE1=A[i];       

VectorE=E1-E0;       

xAxis[iNumAxes]=Vector(-E.y,E.x);              

if(!

IntervalIntersect(A,Anum,B,Bnum,xAxis[iNumAxes],xOffset,xVel,taxis[iNumAxes],t))          

returnfalse;      

iNumAxes++;    

}    

//测试分离轴B    

for(intj=Bnum-1,i=0;i

{       

VectorE0=B[j];       

VectorE1=B[i];       

VectorE=E1-E0;       

xAxis[iNumAxes]=Vector(-E.y,E.x);       

if(!

IntervalIntersect(A,Anum,B,Bnum,xAxis[iNumAxes],xOffset,xVel,taxis[iNumAxes],t))          

returnfalse;      

iNumAxes++;   

}       

if(!

FindMTD(xAxis,taxis,iNumAxes,N,t))       

returnfalse;    

//确保多边形被彼此推开。

    

if(N*xOffset<0.0f)       

N=-N;    

returntrue; 

}

boolAxisSeparatePolygons(VectorN,PolygonA,PolygonB,VectorOffset,VectorVel,

float&t,floattmax) 

{    

floatmin0,max0;    

floatmin1,max1;    

CalculateInterval(N,A,min0,max0);    

CalculateInterval(N,B,min1,max1);        

floath=OffsetdotN;    

min0+=h;    

max0+=h;    

floatd0=min0-max1;

//如果重叠,do<0    

floatd1=min1-max0;

//如果重叠,d1>0    

//分离,测试动态间隔    

if(d0>0.0f||d1>0.0f)    

{       

floatv=VeldotN;       

//速度很小,所以只能进行重叠测试。

      

if(fabs(v)<0.0000001f)          

returnfalse;       

floatt0=-d0/v;

//时间影响D0达到0       

floatt1=d1/v;

//时间影响D0达到1       

//排序时间。

       

if(t0>t1)       

{          

floattemp=t0;          

t0=t1;          

t1=temp;       

}       

//取最小值       

taxis=(t0>0.0f)?

t0:

t1;       

//交叉时间太晚或时间,没有碰撞       

if(taxis<0.0f||taxis>tmax)          

returntrue;       

returnfalse;    

}    

else    

{       

//重叠。

得到的区间,作为最小的|D0|和|D1|      

//返回负数以标记为重叠       

taxis=(d0>d1)?

d0:

d1;       

returnfalse;    

}

boolFindCollisionPlane(Vector*Axis,float*taxis,intiNumAxes,Vector&Ncoll,

float&tcoll) 

{    

//先找到碰撞    

intmini=-1;    

tcoll=0.0f;    

for(inti=0;i

{       

if(taxis[i]>0.0f)       

{

if(taxis[i]>tcoll)          

{             

mini=i;             

tcoll=taxis[i];             

Ncoll=Axis[i];             

Ncoll.Normalise();

//将轴          

}      

}   

}   

//发现了碰撞    

if(mini!

=-1)       

returntrue;    

//不,找到重叠    

mini=-1;    

for(inti=0;i

{       

floatn=Axis[i].Normalise();

//轴线长度       

taxis[i]/=n;

//正常区间重叠太       

//记住,这些数字是负的,所以采取最接近0       

if(mini==-1||taxis[i]>tcoll)       

{          

mini=i;          

tcoll=taxis[i];          

Ncoll=Axis[i];       

}    

}        

return(mini!

=-1); 

}

现在,你拥有了一个可以检测未来碰撞的的检测系统,或者当重叠的时候,返回碰撞平面和碰撞深度/时间

四、 基本弧碰撞响应

下面要作的是用给定的量将两个物体分离,并添加一点摩擦和一些静态摩擦,以便使物体静止在斜面上。

该部分使用简单的速度影响算法。

同样,为了使碰撞响应更加真实,物体被赋予了质量(更好的是质量的倒数)。

质量的倒数是比较常用的,该值为零意味着该物体具有无穷大的质量,并因此不能移动。

同时速度响应中使用质量的倒数具有更好的物理精确性。

现在我们知道多边形A在位置PA具有速度VA,与位置PB速度VB的多边形B发生碰撞。

Ncoll和tcoll定义了碰撞平面。

如果碰撞前是交叠的,首先分离两个物体,如下:

if(tcoll<0) 

if(A.InvMass==0) 

      PB+=Ncoll*tcoll; 

   else

if(B.InvMass==0) 

      PA-=Ncoll*tcoll; 

   else 

   { 

      PA-=Ncoll*(tcoll* 0.5f); 

      PB+=Ncoll*(tcoll* 0.5f); 

   } 

}

然后可以调用碰撞响应的代码,为了简化,我们可以考虑一个粒子碰到一个平面上

这里V表示粒子的进入速度,V’是粒子发生碰撞后的速度,N为平面的法向。

V’=V-(2*(V.N))*N

理想状态下,碰撞前后粒子的能量是相同的。

但是我们可以给粒子的碰撞加入弹性系数

V’=V-((1+elasticity)*(V.N))*N

弹性系数的范围为[0,1]如果为零意味着粒子将沿着平面滑动,如果为1,粒子将没有能量损耗的弹开。

同样我们可以加入一些摩擦。

如果

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 表格模板 > 合同协议

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1