标签:
这部分的知识实在是琐碎,涉及到了立体几何、数学、物理等方面许多内容,当然计算机方面的基础知识更重要。
感觉做东西越是用的心思多,越是没话说。这一部分真心不是菜鸟玩的转的。我就把自己写的碰撞检测函数和重力模仿框架帖一下。筒子们可以直接拿去用。但我更希望更多的初学者能好好看看代码中的核心思想。
想了好几天是在是想不出来怎么讲这一章怎么完成的,要是写的话把我累死了就。就写一下关于这部分的一些个人总结吧:
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 }
物理系统框架,需要注意,碰撞过程中,不能计算力与加速度。(动量定理的条件)
// 物理系统*************************** // 计算当前速度 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
标签:
原文地址:http://www.cnblogs.com/SecretMan001/p/4356663.html