碰撞检测经典解决方案

               




        碰撞检测在3D游戏中至关重要,好的碰撞检测要求人物在场景中可以平滑移动,遇到一定高度内的台阶可以自动上去,而过高的台阶则把人挡住,遇到斜率较小的斜坡可以上去,斜率过大则把人挡住,在各种前进方向被挡住的情况下都要尽可能地让人物沿合理的方向滑动而不是被迫停下。在满足这些要求的同时还要做到足够精确和稳定,防止人物在特殊情况下穿墙而掉出场景。


       碰撞检测做得好了是应该的,不易被人注意到,因为这符合我们日常生活中的常识。做得差了却很容易让人发现,人物经常被卡住不能前进或者人物穿越了障碍。所以大部分人都觉得写碰撞检测代码是件吃力不讨好的事情,算法复杂、容易出bug、不容易出彩。下面还是回到正题,看看我们该如何解决这个难题。

       早期3D游戏的碰撞检测多数基于格子或者BSP树,基于格子的系统实现简单但精度不够,不属于严格意义的3D碰撞检测。基于BSP树的碰撞检测一度十分流行,算法基本已经成熟定型,但它的固有缺点却使它不太适合现在的游戏。BSP树需要很长的预处理时间不适合加载时计算,BSP划分经常会产生原多边形数三到四倍的多边形,考虑到不用保存法线、颜色、uv等信息也要增加将近一倍的资源容量,在一个大的游戏中将模型资源的容量从200M增加到400M相信是大部分人都不愿接受的。目前对于任意复杂三角形集合(mesh)的碰撞检测多数基于BVTree(bounding volume tree),具体可以是aabb tree,obb tree或者K-dop tree,这也是当今各种物理引擎和碰撞检测引擎流行的做法。

       上面是碰撞检测按数据结构不同的分类,按检测方式又可以分为离散点的碰撞检测和连续碰撞检测(CCD continuous collisiondetection)。离散点的碰撞检测是指定某一时刻T的两个静态碰撞体,看它们之间是否交迭,如果没有交迭则返回它们最近点的距离,如果交迭则返回交迭深度,交迭方向等。连续碰撞检测则是分别指定在T1、T2两个时刻两个碰撞体的位置,看它们在由T1运动到T2时刻的过程中是否发生碰撞,如果碰撞则返回第一碰撞点的位置和法线。连续碰撞检测是最为自然的碰撞检测,可以大大方便碰撞响应逻辑的编写,可以很容易避免物体发生交迭或者穿越。离散点的碰撞检测则没有那么友好,当检测到碰撞时两个物体已经发生了交迭,如果其中有三角形网格对象那么已经有许多三角形发生了交迭,如何将两个交迭的对象分开并按合理的方式运动是一个挑战。虽然连续碰撞检测是最自然的方式,但它的实现非常复杂,运算开销也很大,所以目前大部分成熟的物理引擎和碰撞检测引擎还是采用了基于离散点的碰撞检测,为了避免物体交迭过深或者彼此穿越,它们都要采用比较小的模拟步长。

       由于碰撞检测引擎的复杂性和对效率的高要求,我们应该尽量使用目前成熟的完整引擎,而不是自己去开发。经过评估,我决定采用Opcode碰撞检测引擎来做游戏中人物和场景的碰撞检测。Opcode的主要功能是用aabb tree管理复杂三角形集合来和射线、球体,立方体,另一个三角形集合等进行离散点上的碰撞检测,如果检测到交迭则返回所有发生交迭的三角形。Opcode的特点是高度的内存使用优化和极好的效率,ODE物理引擎底层就采用它来做复杂三角形mesh的碰撞检测,Opcode的作者也是NovodeX(PhysX前身)物理引擎的核心开发人员,据说NovodeX采用了Opcode的一个更优化版本。由此可见Opcode的成熟与效率。

       确定了要使用的引擎,下面要讨论的算法就和具体引擎无关了,适合于任何离散点的碰撞检测引擎。我们用AABB包围盒来代表场景中的人物,看看如何实现文章开头所提出的效果。

        首先解释一下检测地面的方式,沿人物包围盒的四条竖边向下投四条射线,射线的终点略低于人物的脚底(也就是说射线的长度是有限的),如果与场景发生碰撞并且碰撞平面的斜率小于某一值则取得碰撞点的高度,否则视为这条射线没有检测到地面。将所有射线检测到的地面高度最大值作为最后的地面高度,如果四条射线都没有检测到地面则认为人物悬空。

 vD = 当前帧人物位移

p0 = 人物包围盒中心当前位置

bOnGroundP1; // 人物是否站在地面

bOnGroundP3; // 人物是否站在地面

bOnGround; // 人物是否站在地面

 

p1 = p0 + vD

在p1位置检测地面

if( 检测到地面 )

{

     将包围盒下放到地面得到位置p2

    bOnGroundP1 = true;

}

else

{

     p2 = p1;

    bOnGroundP1 = false;

}

测试p2点的包围盒是否与场景交迭

if( 交迭 )

{

     取得所有交迭三角形的法线,将它们相加后取平均值,得到法线normal

     将法线与向上的向量叉乘得到切线方向tangent

     // 计算人物沿切线滑动后的位置,注意这里用p0做计算。

     // 如果要使滑动更平滑可以把p0向法线方向推出一些

     // p3 =p0 + normal * 0.1f + vD.Dot(tangent);

     p3 = p0 +vD.Dot(tangent); 

 

     在p3位置检测地面

     if( 检测到地面 )

     {

        将包围盒下放到地面得到位置p4

         bOnGroundP3 = true;

     }

     Else

     {

        p4 = p3;

         bOnGroundP3 = false;

     }

 

     测试p4点的包围盒是否与场景交迭

     if( 交迭 )

     {

        测试p1点的包围盒是否与场景交迭

        if( 交迭 )

        {

             // 无法得到合理的移动位置,人物位置保持不变

             // 等待下一帧玩家调整前进方向再做测试

             将p0作为人物的新位置

             bOnGround = true;

             return;

        }

        else

        {

             将p1作为人物的新位置

             bOnGround = bOnGroundP1;

             return;

        }

     }

     Else

     {

        将p4作为人物的新位置

        bOnGround = bOnGroundP3;

         return;

     }

}

else

{

     将p2作为人物的新位置

    bOnGround = bOnGroundP1;

     return;

}

 

上面的算法基本达到了文章开头所提到的效果,在某些复杂情况下人物移动还有些不流畅,但没有发现人物有穿越障碍物的现象。在大部分情况下人物的新坐标都会由p2点返回,最坏情况是人物被卡住返回p0点。在P4 3.0G的机器上可以支持120个人物在最坏情况下保持30帧的游戏帧数。

 

 

 

在使用广义碰撞阶段迅速排除了大量物体以后,将会使用精确到多边形级别的精确碰撞,比如两个凸包之间的碰撞,

内容简介  《实时碰撞检测算法技术》详细阐述了与碰撞检测问题相关的高效解决方案及相应的数据结构和算法,主要包括:碰撞检测系统中的设计问题、数学和几何学入门、包围体、基本图元测试、层次包围体技术、空间划分、BSP树层次结构、凸体算法、基于GPU的碰撞检测、数值健壮性、几何健壮性以及优化操作。另外,《实时碰撞检测算法技术》还提供了相应的算法、代码以及伪代码,以帮助读者进一步理解计算方案的实现过程。  《实时碰撞检测算法技术》适合作为高等院校计算机及相关专业的教材和教学参考书,也可作为相关开发人员的自学教材和参考手册。第1章 概述1.1 内容概览1.2 关于本书的代码第2章 碰撞检测系统中的设计问题2.1 撞算法的设计因素2.2 应用程序中对象的表达方式2.3 查询类型2.4 环境模拟参数2.5 性能2.6 健壮性2.7 实现与使用的简洁性2.8 小结第3章 数学和几何学入门3.1 矩阵3.2 坐标系统和顶点3.3 向量3.4 质心坐标3.5 直线、光线和线段3.6 平面和半空间3.7 多边形3.8 多面体3.9 凸包计算3.10 域3.11 Minkowski和与Minkowski差3.12 小结第4章 包围体4.1 BV期望特征4.2 轴对齐包围盒4.3 Spheres体4.4 方向包围盒4.5 扫掠体4.6 半空间相交体4.7 其他类型的包围体4.8 小结第5章 基本图元测试5.1 最近点计算5.2 图元测试5.3 直线、光线和有向线段的相交测试5.4 其他类型的测试5.5 动态相交测试5.6 小结第6章 层次包围体技术6.1 层次结构设计问题6.2 层次结构的构建策略6.3 层次结构的遍历6.4 包围体层次结构示例6.5 合并包围体6.6 高效的树型表达方式及遍历6.7 通过缓存机制改善查询6.8 小结第7章 空间划分第8章 BSP树层次结构第9章 凸体算法第10章 基于GPU的碰撞检测第11章 数值健壮性第12章 几何健壮性第13章 优化操作参考文献
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值