




已阅读5页,还剩77页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
向量几何在游戏编程中的使用 分类: DirectX 2008-06-01 20:14 584人阅读 评论(0) 收藏 举报 简单的2-D追踪-Twinsen编写-本人水平有限,疏忽错误在所难免,还请各位数学高手、编程高手不吝赐教-我的Email-address: Andre Lamothe说:“向量几何是游戏程序员最好的朋友”。一点不假,向量几何在游戏编程中的地位不容忽视,因为在游戏程序员的眼中,显示屏幕就是一个坐标 系,运动物体的轨迹就是物体在这个坐标系曲线运动结果,而描述这些曲线运动的,就是向量。使用向量可以很好的模拟物理现象以及基本的AI。现在,先来点轻松的,复习一下中学知识。向量v(用粗体字母表示向量)也叫矢量,是一个有大小有方向的量。长度为1的向量称为单位向量,也叫幺矢,这里记为E。长度为0的向量叫做零向量,记为0,零向量没有确定方向,换句话说,它的方向是任意的。一、向量的基本运算 1、向量加法:a+b等于使b的始点与a的终点重合时,以a的始点为始点,以b的终点为终点的向量。2、向量减法:a-b等于使b的始点与a的始点重合时,以b的终点为始点,以a的终点为终点的向量。3、 数量乘向量:k*a,k0时,等于a的长度扩大k倍;k=0时,等于0向量;k0时,等于a的长度扩大|k|倍然后反向。4、向量的内积(数量积、点积): a.b=|a|*|b|*cosA 等于向量a的长度乘上b的长度再乘上a与b之间夹角的余弦。它的几何意义就是a的长度与b在a上的投影长度的乘积,或者是b的长度与a在b上投影长的乘积,它是一个标量,而且可正可负。因此互相垂直的向量的内积为0。5、向量的矢积(叉积): a x b = |a|*|b|*sinA*v = c, |a|是a的长度,|b|是b的长度,A是a和b之间的不大于180的夹角,v是与a,b所决定的平面垂直的幺矢,即axb与a、b都垂直。在右手坐标系下,a,b,c构成右手系,即右手拇指伸直,其余四指按由a到b的不大于180度的角卷曲,此时拇指所指方向就是c的方向。因此axb!=bxa。如果是左手系,那么上图中a x b = -c ,即a,b和-c构成左手系。a x b的行列式计算公式如上图右边所示。两个向量的矢积是一个向量。6、正交向量的内积:互相垂直的两个向量是正交的,正交向量的内积为零。a.b = |a|.|b|*cos(PI/2) = |a|.|b|*0 = 0。二、向量的性质没有下面的这些性质做基础,我们后面向量技巧的推导将无法进行。 1) a + b = b + a2) (a + b) + c = a + (b + c)3) a + 0 = 0 + a = a4) a + (-a) = 05) k*(l*a) = (k*l)*a = a*(k*l)6) k*(a + b) = k*a + k*b7) (k + l)*a = k*a + l*a8) 1*a = a9) a.b = b.a10)a.(b + c) = a.b + a.c11)k*(a.b) = (k*a).b = a.(k*b)12)0.a = 013)a.a = |a|2三、自由向量的代数(分量)表示1、向量在直角坐标中的代数表示方法:a=(x,y) 其中x,y分别是向量在x轴和y轴上的分量。任何一个在直角坐标轴上的分量为(x,y)的向量都相等。比如上图中的每个向量都表示为(-2,1)。或者写成a=x*i+y*j,即i和j的线性组合,这里i是x轴方向的单位向量(1,0),j是y轴方向的单位向量(0,1),因此i正交于j。任意一个2-D向量都可以表成i与j的线性组合。|i| = |j| = 1 2、向量的代数(分量)表示的运算:向量加法分量表示:a+b=(xa,ya)+(xb,yb)=(xa+xb,ya+yb)向量减法分量表示:a-b=(xa,ya)-(xb,yb)=(xa-xb,ya-yb)向量的内积(数量积、点积)分量表示:a.b=(xa * i + ya * j).(xb * i + yb * j)= xa * i * xb * i + xa * i * yb * j + ya * j * xb * i + ya * j * yb * j=(xa * xb) * (i * i) + (xa * yb) * (i * j) + (xb * ya) * (i * j) + (ya * yb) * (j * j)= xa * xb + ya * yb |ab|*cosA=x2|ab|sinA3、向量长度(模)的计算以及单位化(归一化):设a=(x,y),则|a| = |(x,y)| = |x*i + y*j| = sqrt(x2*i2 + y2*j2) = sqrt(x2 + y2),这里sqrt是开平方符号。a的单位向量为a/|a|,即(x,y)/sqrt(x2 + y2)。四、简单的2-D追踪现在,有了向量的基本知识,我们就可以分析一个常见的问题-屏幕上一点到另一点的追踪,其实这一问题也可理解为画线 问题,画线的算法有很多:DDA画线法、中点画线法以及高效的Bresenham算法。但这些算法一般只是画一些两端固定的线段时所使用的方法,再做一些 动态的点与点之间的跟踪时显得不很灵活。使用向量的方法可以很好的解决此类问题。现在假设你正在编写一个飞行射击游戏,你的敌人需要一种 很厉害的武器-跟踪导弹,这种武器在行进的同时不断的修正自己与目标之间的位置关系,使得指向的方向总是玩家,而不论玩家的位置在哪里,这对一个水平不高 的玩家(我?)来说可能将是灭顶之灾,玩家可能很诧异敌人会拥有这么先进的秘密武器,但对于你来说只需要再程序循环中加入几行代码,它们的原理是向量的单位化和基本向量运算。首先我们要知道玩家的位置(x_player, y_player),然后,我们的导弹就可以通过计算得到一个有初始方向的速度,速度的方向根据玩家的位置不断修正,它的实质是一个向量减法的计算过程。 速度的大小我们自己来设置,它可快可慢,视游戏难易度而定,它的实质就是向量单位化和数乘向量的过程。具体算法是:导弹的更新速度 (vx_missile, vy_missile) = 玩家的位置(x_player, y_player) - 导弹的位置(x_missile, y_missile),然后再对(vx_missile, vy_missile)做缩小处理,导弹移动,判断是否追到玩家,重新更新速度,缩小.看一下这个简单算法的代码:/ 假设x_player,y_player是玩家位置分量/ x_missile,y_missile是导弹位置分量/ xv_missile,yv_missile是导弹的速度分量/ 让我们开始吧!float n_missile ; / 这是玩家位置与导弹位置之间向量的长度 float v_rate ; / 这是导弹的速率缩放比率 / 计算一下玩家与导弹之间的位置向量 xv_missile = x_player-x_missile ; / 向量减法,方向由导弹指向玩家,x分量 yv_missile = y_player-y_missile ; / y分量/ 计算一下它的长度n_missile = sqrt( xv_missile*xv_missile + yv_missile*yv_missile ) ;/ 归一化导弹的速度向量:xv_missile /= n_missile ;yv_missile /= n_missile ;/ 此时导弹的速率为1,注意这里用速率。/ 导弹的速度分量满足xv_missile2+yv_missile2=1/ 好!现在导弹的速度方向已经被修正,它指向玩家。/ 由于现在的导弹速度太快,为了缓解一下紧张的气氛,我要给导弹减速v_rate = 0.2f ; / 减速比率xv_missile *= v_rate ; / 这里的速率缩放比率,你可以任意调整大小yv_missile *= v_rate ; / 可以加速:v_rate大于1;减速v_rate大于0小于1,这里就这么做!/ 导弹行进!导弹勇敢的冲向玩家!x_missile += xv_missile ;y_missile += yv_missile ;/ 然后判断是否攻击成功现在,你编写的敌人可以用跟踪导弹攻击玩家了。你也可以稍加修改,变为直线攻击武器。这样比较普遍。基本的跟踪效果用向量可以很好的模拟。此时,我们只用到了所述向量知识的很少的一部分。其他的知识会慢慢用到游戏中。这次先介绍到这里。下次我将说说利用向量模拟2-D物体任意角度返弹的技巧:)但是!别忘了复习一下向量的基础知识,我们要用到它们。2-D物体任意角度的反弹-Twinsen编写 -本人水平有限,疏忽错误在所难免,还请各位数学高手、编程高手不吝赐教-我的Email-address: 第一次我说了一下向量知识的基础内容和一点使用技巧,浅显的展示了它在游戏编程中的作用。这次深入一些,充分利用向量的性质模仿一个物理现象。首先,我要介绍一下将要使用的两个基本但非常重要的技巧。一、求与某个向量a正交的向量b 根据向量内积的性质以及正交向量之间的关系,有:设a=(xa,ya),b=(xb,yb)a.b = 0= xa*xb + ya*yb = 0= xa*xb = -ya*yb= xa/-ya = yb/xb= xb = -ya , yb = xa 或 xb = ya , yb = -xa则向量(xa,ya)的正交向量为(xb,yb)=(-ya,xa)比如上图中,向量(2,3)的逆时针旋转90度的正交向量是(-3,2),顺时针旋转90度的正交向量为(3,-2)。这样,任给一个非零向量(x,y),则它相对坐标轴逆时针转90度的正交向量为(-y,x),顺时针转90度的正交向量为(y,-x)。二、计算一个向量b与另一向量a共线的两个相反的投影向量我们看一下上面的图,很明显,cosA(A=X)关于y轴对称,是偶函数,因此cosA = cos(-A),又因为cosA是周期函数,且周期是2*PI,则有cos(A+2*PI) = cosA = cos(-A) = cos(-A+2*PI),则根据cosA = cos(2*PI-A)以及a.b = |a|*|b|*cosA,有a.b = |a|*|b|*cosA = |a|*|b|*cos(2*PI-A)现在,根据上图,就有a.b = |a|*|b|*cosA = |a|*|b|*cos(2*PI-A) = ax*bx + ay*by按照这个规则,当上面的b与c的模相等时,有|a|*|b| = |a|*|c|,进一步的,当它们与a的夹角A = B时,就有a.b = |a|*|b|*cosA = |a|*|c|*cosB = a.c ,相应的有a.b = |a|*|b|*cosA = |a|*|b|*cos(2*PI-A) = |a|*|c|*cosB = |a|*|c|*cos(2*PI-B) = a.c 也就是ax*bx + ay*by = ax*cx + ay*cy我们还注意到在一个周期内,比如在0,2*PI中,cosA有正负两种情况,分别是:在(0,PI/2)&(3*PI/2, 2*PI)为正,在(PI/2,3/2*PI)为负。好,知道了这件事情之后,再看a.b = |a|*|b|*cosA,|a|和|b|都为正,所以a.b的正负性就由cosA决定,换句话说,a.b与它们夹角A的余弦cos有相同的符号。所以,还看上面的图,我们就有:1)当A在(0, PI/2)&(3*PI/2, 2*PI)中,此时2*PI-A在(-PI/2,0)&(0, PI/2)中,a.b为正2)当A在(PI/2, 3*PI/2)中,此时2*PI-A也在(PI/2, 3*PI/2)中,a.b为负现在我们再来看一下同模相反(夹角为PI)向量b和b与同一个向量a的两个内积之间有什么关系。首先B + B= 2*PI - PI = PI,所以有b = -b, b = -b,即(bx, by) = (-bx, -by) = -(bx, by)(bx, by) = (-bx, -by) = -(bx, by)所以 a.b =(ax, ay) . (bx, by) = (ax, ay) . -(bx, by) = a.-b= -(a.b) a.b= (ax, ay) . (bx, by) = (ax, ay) . -(bx, by) = a.-b = -(a.b)我们看到,一个向量b的同模相反向量b与向量a的内积a.b,等于b与a的内积的相反数-(a.b)。好,有了上面的基础,我们就可以求一个向量b与另一向量a共线的两个相反的投影向量c和c了。要求b在a上的投影向量c,我们可以用一个数乘上一个单位向量,这个单位向量要和a方向一至,我们记为a1。而这个数就是b在a上的投影长。先来求单位向量a1,我们知道它就是向量a乘上它自身长度的倒数(数乘向量),它的长度我们可以求出,就是m = sqrt(ax2 + ay2),所以a1就是(ax/m, ay/m),记为(a1x, a1y)。再求投影长/c/(注意/与|的区别,前者是投影长,可正可负也可为零,后者是实际的长度,衡为非负)。 根据内积的几何意义:一个向量b点乘另一个向量a1,等于b在a1上投影长与a1的长的乘积。那我们要求b在a上的投影长,就用它点乘a的单位向量a1就可以了,因为单位向量的长度为1,b的投影长/c/乘上1还等于投影长自身,即:/c/ = b.a1 = (bx, by) . (a1x, a1y) = bx * a1x + by * a1y好,我们得到了c的投影长,现在就可以求出c:c = /c/*a1 = ( (bx * a1x + by * a1y)*a1x, (bx * a1x + by * a1y)*a1y )总结一下,就是c = (b.a1)*a1。我们看到,b与a1的夹角在(0, PI/2)之间,因此它们的点积/c/是个正值。因此当它乘a1之后,得到向量的方向就是a1的方向。现在来看b,它是b的同模相反向量,它和a1的夹角在(PI/2, 3*PI/2)之间,因此b点乘a1之后得到/c/是个负值,它再乘a1,得到向量的方向和a1相反。我们知道,一个向量b的同模相反向量b与向量a的内积a.b,等于b与a的内积的相反数-(a.b)。因此,/c/ = -/c/,也就是说,它们的绝对值相等,符号相反。因此它们同乘一个a1,得到的的两个模相等向量c与c共线。让我们把它完成:(b.a1) = -(b.a1) = -(b.a1) = (b.a1), 好,代入c = (b.a1)*a1,得到 c = -(b.a1)*a1 = (b.a1)*a1 = -c = cc = ( b . a1 ) * a1 = (-b. a1) * a1c= ( b. a1 ) * a1 = (-b . a1) * a1至此为止,我们得出结论:当一个向量b与另一个向量a的夹角在(0, PI/2)&(3*PI/2, 2*PI)之间,它在a方向上的投影向量c就是c = ( b . a1 ) * a1,其中a1是a的单位向量;它在a相反方向的投影向量c是c= ( b. a1 ) * a1,其中向量b是b的同模相反向量。相反的,也可以这样说:当一个向量b与另一个向量a的夹角在(PI/2, 3*PI/2)之间,它在a相反方向上的投影向量c是c= ( b. a1 ) * a1,其中 a1是a的单位向量;它在a方向上的投影向量c是c = ( b . a1 ) * a1。其中向量b是b的同模相反向量。特别的,点乘两个单位向量,得到它们夹角的余弦值:E.E = |E|*|E|*cosA = 1*1*cosA = cosA 好了,可完了。 现在就可以看一下三、使用向量模拟任意角度反弹的原理根据初等物理,相互接触的物体在受到外力具有接触面相对方向相对运动趋势的时候,接触面会发生形变从而产生相互作用的弹力。弹力使物体形变或形变同时运动形式发生改变。在知道了这件事情之后,我们开始具体讨论下面这种情况:矩形框和小球碰撞,碰撞时间极短,墙面无限光滑从而碰撞过程没有摩擦,碰撞时间极短,没有能量损失.总之是一个理想的物理环境。我们在这种理想环境下讨论,小球与墙面发生了完全弹性碰撞,且入射角和反射角相等:A=A,B=B,C=C,.。虚线是法线,它和墙面垂直。小球将在矩形框中永无休止的碰撞下去,且每次碰撞过程中入射角和反射角都相等。 我 们再具体点,现在假设上面那个矩形墙壁的上下面平行于x轴,左右面平行于y轴。这样太好了,我们在编写程序的时候只要判断当球碰到上下表面的时候将y方向 速度值取返,碰到左右表面时将x方向速度值取返就行了,这种方法常常用在简单物理模型和规则边界框的游戏编程上,这样可以简化很多编程步骤,编写简单游戏 时可以这样处理。可事实不总是像想向中的那么好。如果情况像下面这样:虽然在碰撞过程中入射角仍然等于反射角,但是边界的角度可没那么“纯”了,它们的角度是任意的,这样就不能简单的将x方向或者y方向的速度取返了,我们要另找解决办法。我们现在的任务是:已知物体的速度向量S和边界向量b,求它的反射向量F。我们先来看一下在碰撞过程中都有哪些向量关系:设b是障碍向量,S是入射速度向量,F是反射速度向量,也就是我们要计算的向量。A是入射角度,A是反射角度,A=A。N是b的法向量,即N垂直于b。n是与N共线的向量,n是N方向的单位向量。T是垂直于N的向量。根据向量加法,现在有关系:(1) S + n = T(2) n + T = F合并,得F = 2*T - S 我们已经找到了计算F的公式了。这里S是已知的,我们要计算一下T,看(1)式:T = S + n要计算T,S是已知的,就要计算一下n。我们知道,n是S在N方向上投影得到的,S已知所以要得到n就要再计算一下N,而N又是和b垂直的。还记得刚才我们导出的使用向量的两个技巧吧,这里我们都要用到:1、任给一个非零向量(x,y),则它相对坐标轴逆时针转90度的垂直向量为(-y,x),顺时针转90度垂直向量为(y,-x)。2、当一个向量b与另一个向量a的夹角在(0, PI/2)&(3*PI/2, 2*PI)之间,它在a方向上的投影向量c就是c = ( b . a1 ) * a1,其中a1是a的单位向量;它在a相反方向的投影向量c是c= ( b. a1 ) * a1,其中向量b是b的同模相反向量。我们知道了b,用技巧1可以计算出N。然后归一化N计算出n,再用技巧2,这里S和n之间的夹角在(PI/2, 3*PI/2)中,因此要想用c = ( b. a1 ) * a1,必须要使b = -S,a1=n。这样就计算出了n。然后根据上面的(1)式计算出T,好了,有了T和F = 2*T - S ,你就拥有了一切!计算出的F就是物体碰撞后的速度向量,在2-D中它有两个分量x和y,3-D中有x,y,z三个分量。这里也证明了使用向量的一个好处就是在一些类似这样关系推导过程中不用去考虑坐标问题,而直接的用简单的向量就可以进行。这里注意我们的障碍向量b在实际的编程中是用障碍的两个端点坐标相减计算出的,计算的时候不需要考虑相减的顺序问题。因为虽然用不同的相减顺序得到b的方向相反,且计算得到的单位法向量n方向也相反(看上图的虚线部分),但是当用-S去点乘单位法向量n之后得到的值也是相反的,它有一个自动的调节功能:现在假设以b为界,S一侧为正方向。则如果单位法向量n是正方向,与-S点积值也是正,正的n再乘正点积得正的n;如果单位法向量为负方向,与-S点积值也为负值,负的n再乘负的点积得到的n为正方向。总之n的方向是不变的,算出的F当然也是不变的。四、编码实现它 现 在我想编码实现它,但之前有一点我想说一下,可能读者已经想到了,在反弹之前我们要先判断什么时候开始反弹,也就是什么时候碰撞,这是一个碰撞检测问题, 本来这是我们应该先要解决的问题,但我想把它放到下一次在具体说,所以这里的编码省略碰撞检测的一步,直接计算反弹速度向量!目的是把上述理论迅速用到算 法中去。/ 在游戏循环中/ 移动的物体简化为质点,位置是x=0.0f,y=0.0f/ 质点速度向量的分量是Svx=4.0f,Svy=2.0f/ 障碍向量是bx=14.0f-6.0f=8.0f,by=4.0f-12.0f=-8.0f/ 则障碍向量的垂直向量是Nx=-8.0f,Ny=-8.0f/ 这里可以加入碰撞检测/ 现在假设已经碰撞完毕,开始反弹计算!/ 计算N的长度float lengthN = sqrt( Nx*Nx + Ny*Ny ) ;/ 归一化N为nfloat n0x = Nx / lengthN ; / n0x就是n的x分量float n0y = Ny / lengthN ; / n0y就是n的y分量/ 计算n,就是S在N方向上的投影向量/ 根据b= (-b.a1).a1,有n = (-S.n).nfloat nx = -(Svx*n0x+Svy*n0y)*n0x ; / n的x分量float ny = -(Svx*n0x+Svy*n0y)*n0y ; / n的y分量/ 计算T/ T = S + nfloat Tx = Svx + nx ; / T的x分量float Ty = Svy + ny ; / T的y分量/ 有了T,有了F = 2*T - S,好了,你现在拥有一切了/ 计算Ffloat Fx = 2*Tx - Svx ; / F的x分量float Fy = 2*Ty - Svy ; / F的y分量/ 现在已经计算出了反弹后的速度向量了/ 更新速度向量Svx = Fx ;Svy = Fy ;/ 质点移动x+=Svx ;y+=Svy ;/ 现在你就可以看到质点被无情的反弹回去了/ 而且是按照物理法则在理想环境下模拟就是这么简单,一个物理现象就可以模拟出来,但是还不完善,只是针对直线障碍,且没有碰撞检测,下次分析一下后者,还是用向量的知识。这次先到这,See u next time!2-D边界碰撞检测-Twinsen编写-本人水平有限,疏忽错误在所难免,还请各位数学高手、编程高手不吝赐教-我的Email-address: 一、使用向量进行障碍检测的原理上次说了使用向量模拟任意角度的反弹,这次谈谈它的前提-障碍碰撞。在游戏中进行障碍碰撞检 测,基本思路是这样的:给定一个障碍范围,判断物体在这次移动后会不会进入这个范围,如果会,就发生碰撞,否则不发生碰撞。在实际操作中,是用物体的边界 来判断还是其他部位判断完全取决于编程者。这时候,就可以从这个部位沿着速度的方向引出一条速度向量线,判断一下这条线段(从检测部位到速度向量终点)和 障碍边界线有没有交点,如果有,这个交点就是碰撞点。上面物体A,在通过速度向量移动之后将到达B位置。但是,这次移动将不会顺利进行,因为我们发现,碰撞发生了。碰撞点就在那个红色区域中,也就是速度向量和边界线的交点。 我们接下来的工作就是要计算这个交点,这是一个解线性方程组的过程,那么我们将要用到一样工具.二、一个解线性方程组的有力工具-克兰姆(Cramer)法则 首先要说明一下的是,这个法则是有局限性的,它必须在一个线性方程组的系数行列式非零的时候才能够使用。别紧张,我会好好谈谈它们的。首先让我来叙述一下这个法则(我会试着让你感觉到这不是一堂数学课):如果线性方程组:A11*X1 + A12*X2 + . + A1n*Xn = b1A21*X1 + A22*X2 + . + A2n*Xn = b2.An1*X1 + An2*X2 + . + Ann*Xn = bn的系数矩阵 A =_| A11 A12 . A1n | A21 A22 . A2n | . | An1 An2 . Ann |- 的行列式 |A| != 0 线性方程组有解,且解是唯一的,并且解可以表示为:X1 = d1/d , X2 = d2/d , . , Xn = dn/d (这就是/A/=d为什么不能为零的原因)这里d就是行列式/A/的值,dn(n=1,2,3.)是用线性方程组的常数项b1,b2,.,bn替换系数矩阵中的第n列的值得到的矩阵的行列式的值,即:| b1 A12 . A1n |d1 = | b2 A22 . A2n | . | bn An2 . Ann | A11 b1 . A1n |d2 = | A21 b2 . A2n | . | An1 bn . Ann | .| A11 A12 . b1 |dn = | A21 A22 . b2 | . | An1 An2 . bn | 别去点击关闭窗口按钮!我现在就举个例子,由于我们现在暂时只讨论2-D游戏(3-D以后会循序渐进的谈到),就来个2-D线性方程组:(1) 4.0*X1 + 2.0*X2 = 5.0(2) 3.0*X1 + 3.0*X2 = 6.0这里有两个方程,两个未知量,则根据上面的Cramer法则:| 4.0 2.0 |d = | 3.0 3.0 | = 4.0*3.0 - 2.0*3.0 = 6.0 (2阶行列式的解法,/对角线相乘减去/对角线相乘) | 5.0 2.0 |d1 = | 6.0 3.0 | = 5.0*3.0 - 2.0*6.0 = 3.0 | 4.0 5.0 |d2 = | 3.0 6.0 | = 4.0*6.0 - 5.0*3.0 = 9.0则 X1 = d1/d = 3.0/6.0 = 0.5X2 = d2/d = 9.0/6.0 = 1.5 好了,现在就得到了方程组的唯一一组解。 是不是已经掌握了用Cramer法则解2-D线性方程组了?如果是的话,我们继续。三、深入研究这里的2-D障碍碰撞检测的实质就是判断两条线段是否有交点,注意不是直线,是线段,两直线有交点不一定直线上的线段也有交点。现在我们从向量的角度,写出两条线段的方程。现在有v1和v2两条线段,则根据向量加法:v1e = v1b + s*v1v2e = v2b + t*v2v1b和v2b分别是两线段的一端。s,t是两个参数,它们的范围是0.0,1.0,当s,t=0.0时,v1e=v1b,v2e=v2b;当s,t=1.0时,v1e和v2e分别是两线段的另一端。s,t取遍0.0,1.0则v1e和v2e取遍两线段的每一点。那么我们要判断v1和v2有没有交点,就让v1e=v2e,看解出的s,t是不是在范围内就可以了:v1e = v2e= v1b + s*v1 = v2b + t*v2= s*v1 - t*v2 = v2b - v1b写成分量形式:s*x_v1 - t*x_v2 = x_v2b - x_v1bs*y_v1 - t*y_v2 = y_v2b - y_v1b现在是两个方程式,两个未知数,则根据Cramer法则: | x_v1 -x_v2 | | 4.0 -2.0 |d = | y_v1 -y_v2 | = | 1.0 -3.0 | = -10.0 | x_v2b-x_v1b -x_v2 | | 5.0 -2.0 |d1 = | y_v2b-y_v1b -y_v2 | = | 2.0 -3.0 | = -11.0 s = d1/d = -11.0/-10.0 = 1.1 1.0现在s已经计算出来,没有在0.0,1.0内,所以两线段没有交点,从图上看很直观。t没有必要再计算了。所以是物体与障碍没有发生碰撞。如果计算出的s,t都在0.0,1.0内,则把它们带入原方程组,计算出v1e或者v2e,它的分量就是碰撞点的分量。四、理论上的东西已经够多的了,开始写程序我现在要写一个用于处理障碍碰撞检测的函数,为了测试它,我还准备安排一些障碍:这是一个凸多边形,我让一个质点在初始位置(10,8),然后给它一个随机速度,这个随机速度的两个分速度在区间1.0,4.0内,同时检测是否与边界发生碰撞。当碰撞发生时,就让它回到初始位置,重新给一个随机速度。/ 首先我要记下凸多边形的边界坐标float poly28 = 6.0f , 2.0f , 4.0f , 8.0f , 14.0f , 18.0f , 14.0f , 6.0f , / 所有点的x分量,最后一个点和第一个点重合 2.0f , 6.0f , 10.0f , 14.0f , 12.0f , 8.0f , 4.0f , 2.0f / 所有点的y分量 ;/ 定义一些变量float x,y ; / 这是质点的位置变量float vx , vy ; / 质点的速度向量分量/ 好,开始编写碰撞检测函数bool CollisionTest() / 当发生碰撞时返回true,否则返回falsefloat s , t ; / 线段方程的两个参数/ 各个参量float x_v1 , x_v2 , y_v1 , y_v2 ; float x_v2b , x_v1b , y_v2b , y_v1b ; for( int i = 0 ; i 8-1 ; +i ) / 循环到倒数第二个点/ 障碍线段x_v1 = poly0i+1-poly0i ;y_v1 = poly1i+1-poly1i ; / 物体速度向量x_v2 = vx ;y_v2 = vy ;/ 障碍向量初始点x_v1b = poly0i ;y_v1b = poly1i ;/ 物体位置x_v2b = x ;y_v2b = y ;/ 计算d,d1和d2/ | x_v1 -x_v2 | /d = | y_v1 -y_v2 | / | x_v2b-x_v1b -x_v2 |/d1 = | y_v2b-y_v1b -y_v2 |/| x_v1 x_v2b-x_v1b |/d2 = | y_v1 y_v2b-y_v1b |d = (x_v1*(-y_v2)-(-x_v2)*y_v1) ;d1 = (x_v2b-x_v1b)*(-y_v2)-(-x_v2)*(y_v2b-y_v1b) ;d2 = (x_v1*(y_v2b-y_v1b)-(x_v2b-x_v1b)*y_v1) ;/ 判断d是否为零if( abs(d) 0.001f ) / 如果等于零做近似处理,abs()用于求绝对值d = 0.001f ; / 计算参量s,ts = d1/d ;t = d2/d ;/ 判断是否发生碰撞/ 如果发生了就返回trueif( 0.0f = s & 0.0f = t ) return true ; / for( int i = 0 ; i 8-1 ; +i )/ 没有发生碰撞,返回falsereturn false ; / end of function/ 现在对函数做测试/ 初始化质点x = 10.0f , y = 8.0f ;vx = vy = (float)(rand()%4+1) ; / 进入主循环中/ 假设现在已经在主循环中 if( CollisionTest() ) / 如果物体与质点发生碰撞x = 10.0f , y = 8.0f ;vx = vy = (float)(rand()%4+1) ;/ 质点移动x+=vx ;y+=vy ;现在你就可以结合上次的讨论模拟一个完整的理想物理情景:一个物体在不规则障碍中移动、反弹,永不停息.除非.至此为止我们讨论了2-D游戏的障碍碰撞检测以及它的编程实现,在此过程中涉及到了线性代数学的知识,以后随着深入还会不断的加入更多的数学、物理知识。下次我们继续讨论,BYE! 2-D物体间的碰撞响应-Twinsen编写 -本人水平有限,疏忽错误在所难免,还请各位数学高手、编程高手不吝赐教-我的Email-address: 这次我要分析两个球体之间的碰撞响应,这样我们就可以结合以前的知识来编写一款最基本的2-D台球游戏了,虽然粗糙了点,但却是个很好的开始,对吗?一、初步分析中学时候上物理课能够认真听讲的人(我?哦,不包括我)应该很熟悉的记得:当两个球体在一个理想环境下相撞之后,它们的总动量保持不变,它们的总机械能也守恒。但这个理想环境是什么样的呢?理想环境会不会影响游戏的真实性?对于前者我们做出在碰撞过程中理想环境的假设:1)首先我们要排除两个碰撞球相互作用之外的力,也就是假设没有外力作用于碰撞系统。2)假设碰撞系统与外界没有能量交换。3)两个球体相互作用的时间极短,且相互作用的内力很大。有了这样的假设,我们就可以使用动量守恒和动能守恒定律来处理它们之间的速度关系了,因为1)确保没有外力参与,碰 撞系统内部动量守恒,我们就可以使用动量守恒定律。2)保证了我们的碰撞系统的总能量不会改变,我们就可以使用动能守恒定律。3)两球发生完全弹性碰撞, 不会粘在一起,没有动量、能量损失。而对于刚才的第二个问题,我的回答是不会,经验告诉我们,理想环境的模拟看起来也是很真实的。除非你是在进行科学研究,否则完全可以这样理想的去模拟。现在,我们可以通过方程来观察碰撞前后两球的速度关系。当两球球心移动方向共线(1-D处理)时的速度,或不共线(2-D处理)时共线方向的速度分量满足:(1)m1 * v1 + m2 * v2 = m1 * v1 + m2 * v2 (动量守恒定律)(2)1/2 * m1 * v12 + 1/2 * m2 * v22 = 1/2 * m1 * v12 + 1/2 * m2 * v22 (动能守恒定律)这里m1和m2是两球的质量,是给定的,v1和v2是两球的初速度也是我们已知的,v1和v2是两球的末速度,是我们要求的。好,现在我们要推导出v1和v2的表达式:由(1),得到v1 = (m1 * v1 + m2 * v2 - m2 * v2) / m1,代入(2),得1/2 * m1 * v12 + 1/2 * m2 * v22 = 1/2 * m1 * (m1 * v1 + m2 * v2 - m2 * v2)2 + 1/2 * m2 * v22= v2 = (2 * m2 * v1 + v2 * (m1 - m2) / (m1 + m2),则= v1 = (2 * m1 * v2 + v1 * (m1 - m2) / (m1 + m2)我们现在得到的公式可以用于处理当两球球心移动方向共线(1-D处理)时的速度关系,或者不共线(2-D处理)时共线方向的速度分量的关系。不管是前者还是后者,我们都需要把它们的速度分解到同一个轴上才能应用上述公式进行处理。二、深入分析首先我要说明一件事情:当两球碰撞时,它们的速度可以分解为球心连线方向的分速度和碰撞点切线方向的分速度。而由于 它们之间相互作用的力只是在切点上,也就是球心连线方向上,因此我们只用处理这个方向上的力。而在切线方向上,它们不存在相互作用的力,而且在理想环境下 也没有外力,因此这个方向上的力在碰撞前后都不变,因此不处理。好,知道了这件事情之后,我们就知道该如何把两球的速度分解到同一个轴上进行处理。现在看上面的分析图,s和t是我们根据两个相碰球m1和m2的位置建立的辅助轴,我们一会就将把速度投影到它们上面。v1和v2分别是m1和m2的初速度,v1和v2是它们碰撞后的末速度,也就是我们要求的。s是两球球心的位置向量,t是它的逆时针正交向量。s1是s的单位向量,t1是t的单位向量。我们的思路是这样的:首先我们假设两球已经相碰(在程序中可以通过计算两球球心之间的距离来判断)。接下来我们计算一下s和t,注意s和t的方向正反无所谓(一会将解释),现在设m1球心为(m1x, m1y),m2球心为(m2x, m2y),则s为(m1x-m2x, m1y-m2y),t为(m2y-m1y, m1x-m2x)(第一篇的知识)。则设sM = sqrt(m1x-m2x)2+(m1y-m2y)2),tM = sqrt(m2y-m1y)2+(m1x-m2x)2),有s1 = (m1x-m2x)/sM, (m1y-m2y)/sM) = (s1x, s1y)t1 = (m2y-m1y)/tM, (m1x-m2x)/tM) = (t1x, t1y)现在s和t轴的单位向量已经求出了,我们根据向量点乘的几何意义,计算v1和v2在s1和t1方向上的投影值,然后将s轴上投影值代入公式来计算s方向碰撞后的速度。注意,根据刚才的说明,t方向的速度不计算,因为没有相互作用的力,因此,t方向的分速度不变。所以我们要做的就是:把v1投影到s和t方向上,再把v2投影到s和t方向上,用公式分别计算v1和v2在s方向上的投影的末速度,然后把得到的末速度在和原来v1和v2在t方向上的投影速度再合成,从而算出v1和v2。好,我们接着这个思路做下去:先算v1(v1x, v1y)在s和t轴的投影值,分别设为v1s和v1t:v1s = v1.s1= v1s = v1x * s1x + v1y * s1y v1t = v1.t1= v1t = v1x * t1x + v1y * t1y再算v2(v2x, v2y)在s和t轴的投影值,分别设为v2s和v2t:v2s = v2.s1= v2s = v2x * s1x + v2y * s1yv2t = v2.t1= v2t = v2x * t1x + v2y * t1y接下来用公式v1 = (2 * m1 * v2 + v1 * (m1 - m2) / (m1 + m2) v2 = (2 * m2 * v1 + v2 * (m1 - m2) / (m1 + m2) 计算v1s和v2s的末值v1s和v2s,重申v1t和v2t不改变:假设m1 = m2 = 1v1s = (2 * 1 * v2s + v1s * (1 - 1) / (1 + 1)v2s = (2 * 1 * v1s + v2s * (1 - 1) / (1 + 1)= v1s = v2s= v2s = v1s好,下一步,将v1s和v1t再合成得到v1,将v2s和v2t再合成得到v2,我们用向量和来做:首先求出v1t和v2t在t轴的向量v1t和v2t(将数值变为向量)v1t = v1t * t1 = (v1t * t1x, v1t * t1y)v2t = v2t * t1 = (v2t * t1x, v2t * t1y)再求出v1s和v2s在s轴的向量v1s和v2s(将数值变为向量)v1s= v1s * s1 = (v1s * s1x, v1s * s1y) v2s= v2s * s1 = (v2s * s2x, v2s * s2y)最后,合成,得v1 = v1t + v1s = (v1t * t1x + v1s * s1x, v1t * t1y + v1s * s1y)v2 = v2t + v2s = (v2t * t1x + v2s * s2x, v2t * t1y + v2s * s2y)从而就求出了v
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 健康蔬菜菌类有营养课件
- 健康绳子课件下载手机版
- 国一高考数学试卷
- 健康素养知识讲座课件
- 健康精灵村绘本课件
- 贵州联考文科数学试卷
- 2025年中国燃气轮机整体行业市场前景预测及投资战略咨询报告
- 2024-2030年中国大连市房地产行业市场全景分析及投资策略研究报告
- 中国何首乌提取物行业市场调研及未来发展趋势预测报告
- 矫形器可行性研究报告
- 自动化仪表维修与保养2025培训
- (一模)2025届安徽省“江南十校”高三联考英语试卷(含官方答案)
- 物业纠纷调解培训
- 2025年中国移动通信集团浙江限公司春季校园招聘高频重点提升(共500题)附带答案详解
- 辅导员评职称述职报告范本
- 《有限空间安全管理制度》
- 共同借款人合同借条3篇
- DB23-T 3627-2023 演出经纪机构信用等级划分与评价规范
- T-GDNAS 043-2024 成人静脉中等长度导管置管技术
- DB21T 3496-2021 机电类特种设备检验记录编写指南
- 节约集约建设用地标准 DG-TJ08-2422-2023
评论
0/150
提交评论