码迷,mamicode.com
首页 > 其他好文 > 详细

DX10引擎计划0322——物理系统

时间:2015-03-22 06:53:25      阅读:234      评论:0      收藏:0      [点我收藏+]

标签:

这部分的知识实在是琐碎,涉及到了立体几何、数学、物理等方面许多内容,当然计算机方面的基础知识更重要。

感觉做东西越是用的心思多,越是没话说。这一部分真心不是菜鸟玩的转的。我就把自己写的碰撞检测函数和重力模仿框架帖一下。筒子们可以直接拿去用。但我更希望更多的初学者能好好看看代码中的核心思想。

想了好几天是在是想不出来怎么讲这一章怎么完成的,要是写的话把我累死了就。就写一下关于这部分的一些个人总结吧:

1.游戏编程最重要的是数据结构和算法,以及底层知识,计组、操作系统是必修课。这个不是我乱说的,我接触到的一些所谓的DX程序员,在这几方面几乎是空白(甚至计算机专业的本科也很差劲)。这几部分非常枯燥,学习的过程是痛苦的,但是这些是真功夫,是内功,历久弥香。

2.做游戏“引擎”,数学一定要好。做游戏引擎和游戏编程是不一样的,需要的数学知识是以质来算的。这里的数学包括的方面很广,不只是一本《计算机图形学》搞的定的(数学系学引擎编程的确有很大的先天优势)。我曾经遇到几位想涉足3D游戏的童鞋,刚上高二而已,被我劝了回去。现在看来非常明智,因为那部分数学太重要了,是游戏开发的最基础。如果筒子要开发引擎,首先看看高中数学学的扎实不扎实吧。(我知道这样说有点残酷,但是这一部分的内容是拼硬功夫的地方)

3.回到物理系统的话题上,物理系统,物理可定得过得去。高中的知识足够了(但是一定要扎实)。也没什么好说的。吐槽一下:自然科学家真的是“时代的开创者”,你用他们的公式“创造世界”的时候,就发现,人家那些定理、公式,都是对现实世界的高度概括,不得不服。(咱们这部分主要接触的是牛顿)

**********************************************

下面这部分与技术关系比较大

**********************************************

4.很早很早以前,我就有一个想法,就是碰撞检测的时候,用二维数组、三维数组作为碰撞层,类似于深度测试,是用空间换取时间。这样每次检查碰撞的时候仅需要查找数组就可以了。然后现在我进行了试算,发现这个想法很失败。这种方法在2D游戏中勉强可行。但在3D空间中,几乎不可能实现。2D空间中,假设碰撞数组是 bool a[1024][1024],这样的碰撞层精确度是1024像素,但是数组有点太大了,内存翻页的时候一页是4K,但是我们这个数组一页盛不下来。好在我们可以利用字节优化,就是把 1字节是 8 个碰撞层像素位,这样来写,数组缩小了8倍,a[1024][128],依然有点大,我们可以损失一部分分辨率,把碰撞检测的精确度降低点——a[512][64],或者a[256][128],这样也算说得过去。

但是到了3维空间——a[1024][1024][1024],这个数组所占空间的大小,口算都知道是12位,把内存条全吃掉也不够,字节优化、分页完全无力回天...

那就只能使用碰撞检测链表了,心知当今自己老菜鸟的功力不再是三年前的小小菜鸟了,就自己试着写了写,果然发现了好多可以优化的地方,优化完之后,运行速度完全无压力。所以如果筒子们的碰撞检测运行效率不高,那说句实在的话,不是路不对,是你代码写的有问题。

这个想法一直在我心里埋藏了很久,这段时间找到了答案,就顺手分享给大家吧。

5.能不用所谓的优化求距离函数尽量不要用。这个纯属我的个人看法了。泰勒公式确实可以优化算法,但是对于某些模型较小游戏来说(不超过1.0f大小的模型),三项、四项多项式的泰勒公式误差率太过大了,而过多的多项式,反而达不到优化的目的。

———————————————————————————————

不多说了,介绍一下这个物理系统的主要功能吧:

我目前仅实现了发起碰撞方是球体的碰撞检测。如果是矩形、圆锥截体的话,需要考虑空间旋转的问题,这部分很好解决,类似于拾取,变换到局部坐标系空间进行运算就可以了。但是这些形状的碰撞,并不是所有游戏都用到了的,比如大部分的角色扮演、射击类游戏,矩形或者圆锥截体形的物体都是固定Y轴、不发生旋转的,这样大大简化了碰撞检测算法。用到了空间旋转内容的,是一些以物理系统为看点的游戏。因为具体这套引擎怎么用,选用什么玩法我还没定下来,所以这部分我没有写出来。其他的相关特性筒子们可以自行研究一下,这个碰撞检测函数已经足够应付大多数情况了。希望对看到文章的你有所帮助。(关于碰撞后的旋转方向我并没有做严格的测试,希望筒子们自行参悟,如果要使用,自行修改)

 

  1 bool Mesh::Crash(GameObj* obj)    // 碰撞检测
  2 {
  3     // 该函数用于判断两个物体有没有碰撞
  4     Mesh* mesh = (Mesh*)obj;
  5 
  6 
  7     // 排除掉距离过远的点
  8     static D3DXVECTOR3 position;
  9     position = mesh->GetPosition();
 10 
 11     // 不要求绝对值,增加代码适应性
 12     static float dty, dtx, dtz;
 13     dtx = positionX - position.x;
 14     dty = positionY - position.y;
 15     dtz = positionZ - position.z;
 16 
 17 
 18     if( dtx > MAX_DISTANT_OF_TWO_OBJ || dtx < -MAX_DISTANT_OF_TWO_OBJ)
 19         return false;
 20     if( dty > MAX_DISTANT_OF_TWO_OBJ || dty < -MAX_DISTANT_OF_TWO_OBJ)
 21         return false;
 22     if( dtz > MAX_DISTANT_OF_TWO_OBJ || dtz < -MAX_DISTANT_OF_TWO_OBJ)
 23         return false;
 24 
 25     static int shapeType;
 26     shapeType = mesh->GetShapeType();
 27 
 28     static D3DXVECTOR3 norm;    // 法线,方便计算碰撞后的速度方向
 29     static D3DXVECTOR3 verh;    // 垂直法线的单位向量
 30 
 31     static D3DXMATRIX normMtx;    // 法线变换矩阵
 32     normMtx = mesh->GetWorldMtx();
 33 
 34     static D3DXMATRIX inverseW;
 35     D3DXMatrixInverse(&inverseW, 0, &normMtx);
 36 
 37     norm = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
 38 
 39     static float hx, hy, hz, dx, dy, dz, disX, disY, disZ;;
 40     hy = mesh->GetHalfY();
 41     hx = mesh->GetHalfX(), hz = mesh->GetHalfZ();
 42     dy = hy + halfY;
 43     dx = halfX + hx, dz = halfZ + hz;
 44 
 45     // 还需要分这几种情况:
 46     // 1.球形与球形
 47     // 2.球形与圆锥截体
 48     // 3.球形与长方体
 49     // 4.长方体与长方体
 50     // 5.长方体与圆锥截体
 51     // 6.圆锥截体与圆锥截体
 52     // 我们只考虑球形与球形,球形(发起碰撞方)与长方体
 53     // 其他类型的碰撞检测需要与地形系统、玩法结合起来
 54     // 先计算碰撞面法线,然后根据法线计算碰撞速度,这样分步过滤的执行流水消耗相对较少
 55 
 56     // 计算法线*************************************************
 57     if(shapeType == BOX )
 58     {
 59         // 对方是长方体
 60         // 
 61         // X,Y,Z坐标的 “精细化” 判断都需要转化到长方体的局部坐标系中
 62         // 
 63         // 转换开始***************************
 64 
 65         static D3DXVECTOR3 pos;
 66         pos = D3DXVECTOR3(positionX, positionY, positionZ);
 67 
 68         D3DXVec3TransformCoord(&pos, &pos, &inverseW);
 69 
 70         // 转换完毕****************************
 71         // 
 72         //
 73         // 求绝对值,简化运算
 74         dtx = fabs(pos.x);
 75         dty = fabs(pos.y);
 76         dtz = fabs(pos.z);
 77 
 78         // 检测有无碰撞
 79         if( dtx > dx+CRASH_GAP )
 80             return false;
 81         if( dty > dy+CRASH_GAP )
 82             return false;
 83         if( dtz > dz+CRASH_GAP )
 84             return false;
 85 
 86 
 87         // Y轴对应的两个平面
 88         if( dtx < hx && dtz < hz )
 89         {
 90             // 在XZ面上碰撞到了
 91             if( pos.y > 0.0f )    // 上表面
 92                 norm = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
 93             else    // 下表面
 94                 norm = D3DXVECTOR3(0.0f, -1.0f, 0.0f);;
 95         }
 96         else if( dty < hy && dtz < hz )
 97         {
 98             // 在YZ面上碰撞到了
 99             if( pos.x > 0.0f )    // 右表面
100                 norm = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
101             else    // 左表面
102                 norm = D3DXVECTOR3(-1.0f, 0.0f, 0.0f);
103         }
104         else if( dty < hy && dtx < hx )
105         {
106             // 在XY面上碰撞到了
107             if( pos.z > 0.0f )    // 后表面
108                 norm = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
109             else    // 前表面
110                 norm = D3DXVECTOR3(0.0f, 0.0f, -1.0f);
111         }
112         
113         // 检测是否碰撞到边,两个对立平面即可,选XZ平面
114         // 示意图:
115         //         +z
116         //     ___________
117         //    |           |
118         //    |           _
119         //-x  |      球 ( o )  +x
120         //    |           -
121         //    |           |
122         //    -------------
123         //         -z
124 
125         else if( (dtx > hx) && (dtx < hx + halfX) )
126         {
127             // +x
128             // 需要判定是否碰撞
129             float dt1 = dtx - hx, dt2 = dty - hy; 
130             disX = dt1*dt1 + dt2*dt2;
131             float radius2 = halfY*halfY;    // 球体的半径 = halfX = halfY = halfZ
132             if(disX > radius2+CRASH_GAP)
133                 return false;
134             else
135             {
136                 // 碰到边了
137                 if(pos.x > 0.0f)
138                 {
139                     //碰到了+x
140                     if(pos.y > 0.0f)
141                         // +y方向上
142                         norm = D3DXVECTOR3(0.7071f, 0.7071f, 0.0f);
143                     else
144                         // -y方向
145                         norm = D3DXVECTOR3(0.7071f, -0.7071f, 0.0f);
146                 }
147                 else
148                 {
149                     //碰到了-x
150                     if(pos.y > 0.0f)
151                         // +y方向
152                         norm = D3DXVECTOR3(-0.7071f, 0.7071f, 0.0f);
153                     else
154                         // -y方向
155                         norm = D3DXVECTOR3(-0.7071f, -0.7071f, 0.0f);
156                 }
157             }
158         }
159         else if( (dtz > hz) && (dtz < hz + halfZ) )
160         {
161             // +z
162             // 需要判定是否碰撞
163             float dt1 = dtz - hz, dt2 = dty - hy; 
164             disX = dt1*dt1 + dt2*dt2;
165             float radius2 = halfY*halfY;    // 球体的半径 = halfX = halfY = halfZ
166             if(disX > radius2 + CRASH_GAP)
167                 return false;
168             else
169             {
170                 if(pos.z > 0.0f)
171                 {
172                     //碰到了+z
173                     if(pos.y > 0.0f)
174                         // +y方向上
175                         norm = D3DXVECTOR3(0.0f, 0.7071f, 0.7071f);
176                     else
177                         // -y方向
178                         norm = D3DXVECTOR3(0.0f, -0.7071f, 0.7071f);
179                 }
180                 else
181                 {
182                     //碰到了-z
183                     if(pos.y > 0.0f)
184                         // +y方向上
185                         norm = D3DXVECTOR3(0.0f, 0.7071f, -0.7071f);
186                     else
187                         // -y方向
188                         norm = D3DXVECTOR3(0.0f, -0.7071f, -0.7071f);
189                 }
190             }
191         }
192         // 检测是否碰到角
193         // 
194         else
195         {
196             float dt1 = dtz - hz, dt2 = dty - hy, dt3 = dtx - hx;
197             disX = dt1*dt1 + dt2*dt2 + dt3*dt3;
198             float radius2 = halfY*halfY;    // 球体的半径 radius = halfX = halfY = halfZ
199 
200             if(disX >  radius2)
201                 return false;
202             else
203             {
204                 // 碰到角了
205                 if(pos.y > 0.0f )
206                 {
207                     if(pos.x>0.0f)
208                     {
209                         if(pos.z > 0.0f)
210                             // 碰到( 1, 1, 1)角
211                             norm = D3DXVECTOR3(0.57735f, 0.57735f, 0.57735f);
212                         else
213                             // 碰到(1, 1, -1)
214                             norm = D3DXVECTOR3(0.57735f, 0.57735f, -0.57735f);
215                     }
216                     else
217                     {
218                         if(pos.z>0.0f)
219                             // 碰到( -1, 1, 1)角
220                             norm = D3DXVECTOR3(-0.57735f, 0.57735f, 0.57735f);
221                         else
222                             // 碰到(-1, 1, -1)
223                             norm = D3DXVECTOR3(-0.57735f, 0.57735f, -0.57735f);
224                     }
225                 }
226                 else
227                 {
228                     if(pos.x>0.0f)
229                     {
230                         if(pos.z>0.0f)
231                             // 碰到( 1, -1, 1)角
232                             norm = D3DXVECTOR3(0.57735f, -0.57735f, 0.57735f);
233                         else
234                             // 碰到(1, -1, -1)
235                             norm = D3DXVECTOR3(0.57735f, -0.57735f, -0.57735f);
236                     }
237                     else
238                     {
239                         if(pos.z>0.0f)
240                             // 碰到( -1, 1, 1)角
241                             norm = D3DXVECTOR3(-0.57735f, 0.57735f, 0.57735f);
242                         else
243                             // 碰到(-1, 1, -1)
244                             norm = D3DXVECTOR3(-0.57735f, 0.57735f, -0.57735f);
245                     }
246                 }
247             }
248         }
249 
250         // 将norm转换到世界坐标系中
251         D3DXVec3TransformNormal(&norm, &norm, &normMtx);
252         
253         // 单位化法向量,简化计算
254         D3DXVec3Normalize( &norm, &norm);
255 
256     }    
257     else if(shapeType == CYLINDER)
258     {
259         // 对方是圆锥截体,待补充
260         return false;
261     }
262     else if(shapeType == QUAD)
263     {
264         // 对方是平面,平面也可能有旋转角度,也需要转换到局部坐标系中判断有无碰撞
265         static D3DXVECTOR3 pos;
266         pos = D3DXVECTOR3(positionX, positionY, positionZ);
267 
268         D3DXVec3TransformCoord(&pos, &pos, &inverseW);
269         // 转换完毕****************************
270 
271         // 
272         //
273         // 求绝对值,简化运算
274 
275         // 设定只在正面产生碰撞,否则强制转移到正面
276         // 判断有无碰撞
277         if( pos.x > dx+CRASH_GAP || pos.x < -dx-CRASH_GAP)
278             return false;
279         if( pos.y > dy+CRASH_GAP || pos.y < 0.0f )
280             return false;
281         if( pos.z > dz+CRASH_GAP || pos.z < -dz-CRASH_GAP )
282             return false;
283 
284         // 计算norm
285         norm = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
286         D3DXVec3TransformNormal(&norm, &norm, &normMtx);
287         // 单位化法向量,简化计算
288         D3DXVec3Normalize( &norm, &norm);
289         
290     }
291     else    // 不属于长方体和圆锥截体的形状全部转为默认——球形
292     {
293         // 对方是球
294 
295         // 求法线
296         norm = D3DXVECTOR3( positionX - position.x, positionY - position.y, positionZ - position.z );
297         
298         // 判断有无碰撞
299         static float dist;
300         dist = D3DXVec3Length( &norm);
301 
302         if(dist > halfX + hx + CRASH_GAP)
303         {
304             // 没碰到,返回
305             return false;
306         }
307         // 单位化法向量,简化计算
308         D3DXVec3Normalize( &norm, &norm);
309     }
310     // 筛选向量*************************************************
311     static float mass, crashMtr;
312     mass = mesh->GetMass();
313     crashMtr = mesh->GetCrashMtr();
314 
315     // 己方
316     static D3DXVECTOR3 oriSpeed1,    // 原速度
317         nSpeed1,    // norm负方向上的速度
318         vSpeed1;    // 垂直于norm方向的速度分量
319     oriSpeed1 = D3DXVECTOR3(speedX, speedY, speedZ);
320 
321     float len = D3DXVec3Length( &oriSpeed1 );
322     // 速度太小,归0
323     if( len <= MIN_SPEED )
324     {
325         speedX = speedY = speedZ = 0.0f;
326         return true;
327     }        
328 
329     // 判断物体是否符合发起方条件
330     float r = D3DXVec3Dot( &oriSpeed1, &norm );
331     if( r > 0 )
332         return true;    // 只要两个物体还接触,并且碰撞速度不为0,作用力就不能参与碰撞,只有垂直于法线的速度可以参与力的运算(摩擦力、重力等)
333     else if(r <= 0.001f && r > -0.001f)
334     {
335         // 浮点数中没有准确值,只能靠范围确定
336         // 转到摩擦力系统,待补充
337         return true;
338     }
339     
340         
341     // 计算速度*************************************************
342     // 对方
343     static D3DXVECTOR3 oriSpeed2,
344         nSpeed2,
345         vSpeed2;
346     static D3DXVECTOR3 destSpeed1, rotateSpeed;
347 
348     oriSpeed2 = mesh->GetSpeed();
349 
350     static float mtr;
351     mtr = mesh->GetCrashMtr();
352     // 
353     // 计算速度
354 
355     if(mass <= 0.001f )
356     {
357         // 对方是无限大质量时
358 
359         // 计算反射向量
360         destSpeed1 = ComputeReflectLineToLine( &oriSpeed1, &norm);
361         
362         rotateSpeed = destSpeed1 + oriSpeed1;
363 
364         rotateSpeedZ = -rotateSpeed.x;
365         rotateSpeedX = -rotateSpeed.y;
366         rotateSpeedY = -rotateSpeed.z;
367 
368         destSpeed1 = destSpeed1 *mtr * mCrashMtr;
369 
370         // 速度系数
371         speedX = destSpeed1.x;
372         speedY = destSpeed1.y;
373         speedZ = destSpeed1.z;
374         return true;
375     }
376     else
377     {
378         // 动量定理系数
379         // 注意,这里出现了除法,一定要避免除数为0的情况
380         static float m1, m2, m3, m4, mt;
381         mt = (mMass + mass);
382         if(mt < 0.0001f || mt > -0.0001f)
383             // 除数为0,直接返回
384             return false;
385         m1 = (mMass - mass) / mt;
386         m2 = mass * 2.0f / mt;
387         m3 = -m1;
388         m4 = mMass * 2.0f / mt;
389 
390         // 计算速度在norm方向上的分量
391         // 取正方向为norm方向
392         
393         // 己方
394         // norm分量
395         float valueNSpeed1 = D3DXVec3Dot(&oriSpeed1, &norm);
396         nSpeed1 = valueNSpeed1 * norm;
397         // 垂直norm的分量
398         vSpeed1 = oriSpeed1 - nSpeed1;
399         
400         float valueVSpeed1 = D3DXVec3Length( &vSpeed1 );
401         // 对方
402         // norm分量
403         float valueNSpeed2 = D3DXVec3Dot(&oriSpeed2, &norm);
404         nSpeed2 = valueNSpeed2 * norm;
405         // 垂直norm分量
406         vSpeed2 = oriSpeed2 - nSpeed2;
407         float valueVSpeed2 = D3DXVec3Length( &vSpeed2 );
408         D3DXVec3Normalize( &verh, &vSpeed2);
409 
410         // 碰撞后的速度值
411         float valueNSpeed3 = (m1*valueNSpeed1 + m2*valueNSpeed2);    // 己方
412         float valueNSpeed4 = (m3*valueNSpeed2 + m4*valueNSpeed1);    // 对方
413 
414         float valueVSpeed3 = (m1*valueVSpeed1 + m2*valueVSpeed2);    // 己方
415         float valueVSpeed4 = (m3*valueVSpeed2 + m4*valueVSpeed1);    // 对方
416 
417         destSpeed1 = valueNSpeed3*norm + valueVSpeed3*verh;
418 
419         rotateSpeed = destSpeed1 + oriSpeed1;
420         rotateSpeedZ = -rotateSpeed.x;
421         rotateSpeedX = -rotateSpeed.y;
422         rotateSpeedY = -rotateSpeed.z;
423 
424         oriSpeed1 = (destSpeed1) *mtr * mCrashMtr;    // 己方
425         oriSpeed2 = (valueNSpeed4*norm + valueVSpeed4*verh) *mtr * mCrashMtr;    // 对方
426     }
427 
428     SetSpeed(destSpeed1);
429     mesh->SetSpeed(oriSpeed2);
430     return true;
431 }

以及一些我自己写的工具函数:

这个函数是计算一条向量关于另一条向量对称的反向向量的(光的反射向量,主要使用了立体几何知识)

技术分享
 1 // 传入的向量不需要单位化
 2 D3DXVECTOR3 ComputeReflectLineToLine(D3DXVECTOR3* pIn, D3DXVECTOR3* pNorm)
 3 {
 4     // 单位化法向量,简化计算
 5     D3DXVec3Normalize( pNorm, pNorm);
 6 
 7     // 对应于输入向量的法线的模
 8     static float valueN;
 9     valueN = fabs( D3DXVec3Dot( pIn, pNorm) );
10 
11     *pNorm = (*pNorm) * valueN;
12 
13     return (*pIn) + (*pNorm) + (*pNorm);
14 }
View Code

物理系统框架,需要注意,碰撞过程中,不能计算力与加速度。(动量定理的条件)

// 物理系统***************************
    // 计算当前速度
    if(mSupported)
        // 位置固定的物体
        speedX = speedY = speedZ = 0.0f;
    else
    {
        // 碰撞检测的过程中必须忽视力的存在,否则不符合自然规律
        if(enableCrash)
        {
            // 主动检测碰撞的物体
            if(gCrashList->Crash(this));
            else
            {
                if(speedY + accSpeedY > MAX_SPEED)
                    speedY = MAX_SPEED;
                if(speedY + accSpeedY < -MAX_SPEED)
                    speedY = -MAX_SPEED;
                else
                    speedY += accSpeedY;
            }
            // 只有主动检测碰撞的物体才会旋转
            Rotate(rotateSpeedX, rotateSpeedY, rotateSpeedZ);
        }
        // 计算当前位置
        // 待补充
        if(positionY + speedY + halfY >=  SKY_TOP)
            SetPosition( positionX, SKY_TOP - halfY, positionZ);
        else if(positionY + speedY - halfY <= HORIZON)
            SetPosition( positionX, HORIZON + halfY, positionZ);
        else
            Translate( speedX, speedY, speedZ);

    }


下面是程序截图:

我设置动能损失率为0时,物体将会永远碰撞下去。 

技术分享

技术分享

技术分享

2015-03-2202:53:10

DX10引擎计划0322——物理系统

标签:

原文地址:http://www.cnblogs.com/SecretMan001/p/4356663.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!