分享
 
 
 

向量几何在游戏编程中的使用2

王朝other·作者佚名  2006-07-07
窄屏简体版  字體: |||超大  

<2>2-D物体任意角度的反弹

-Twinsen编写

-本人水平有限,疏忽错误在所难免,还请各位数学高手、编程高手不吝赐教

-我的Email-address: popyy@netease.com

第一次我说了一下向量知识的基础内容和一点使用技巧,浅显的展示了它在游戏编程中的作用。这次深入一些,充分利用向量的性质模仿一个物理现象。

首先,我要介绍一下将要使用的两个基本但非常重要的技巧。

一、求与某个向量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) = (-b'x, -b'y) = -(b'x, b'y)

(b'x, b'y) = (-bx, -by) = -(bx, by)

所以

a.b =(ax, ay) . (bx, by) = (ax, ay) . -(b'x, b'y) = a.-b'= -(a.b')

a.b'= (ax, ay) . (b'x, b'y) = (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(ax^2 + ay^2),所以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 = c'

c = ( b . a1 ) * a1 = (-b'. a1) * a1

c'= ( 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为n'

float n0x = Nx / lengthN ; // n0x就是n'的x分量

float n0y = Ny / lengthN ; // n0y就是n'的y分量

// 计算n,就是S在N方向上的投影向量

// 根据b'= (-b.a1').a1',有n = (-S.n').n'

float nx = -(Svx*n0x+Svy*n0y)*n0x ; // n的x分量

float ny = -(Svx*n0x+Svy*n0y)*n0y ; // n的y分量

// 计算T

// T = S + n

float Tx = Svx + nx ; // T的x分量

float Ty = Svy + ny ; // T的y分量

// 有了T,有了F = 2*T - S,好了,你现在拥有一切了

// 计算F

float 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!

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有