控制物体的运动-第九作坊物理运动引擎介绍

 
 
游戏引擎的基本功能除了渲染以外最重要的就是物理运算,因为引擎不光要把物体“画”出来,还要在指定的位置,指定的大小,按照指定的姿态将物体正确的画出来。这些任务都是由物理运算完成的。引擎的物理运算都是在物理层完成的,物理对象决定渲染对象的物理属性。下面首先从最基本的刚体运动开始。
1.  刚体运动
刚体运动将所有物体看作不会发生形变的实体,对其进行的各种运算只需要考虑位移和旋转即可。所有刚体运动的内容都在Phyical_FRU中实现,除了位移和旋转之外该类也提供了缩放的功能,以便于调整物体的大小。“FRU”指的是该物理对象采用了三根基准轴:Front,Right,Up。这三根轴是执行位移变换的基准,旋转变换会直接应用到这三根轴上,再由这三根轴生成旋转矩阵。最终的世界矩阵由旋转矩阵*缩放矩阵*位移矩阵获得。

下面列出基础性的代码片段。

D3DXMatrixTranslation(&MatTranslation,NowState.position.x,NowState.position.y,NowState.position.z);
D3DXMatrixScaling(&MatScale,NowState.Scale.x,NowState.Scale.y,NowState.Scale.z);
MatWorld=MatRotation*MatScale*MatTranslation ;

     生成位移变换矩阵
     生成缩放变换矩阵。
     将旋转、位移、缩放组合在一起。
 
下面具体说明如何使用基准轴生成旋转矩阵:
 
这里4行代码用于使用基准轴构造渲染矩阵。旋转矩阵的“格式”如下:
 

front.x, right.x, up.x , 0
front.y, right.y, up.y , 0
front.z, right.z, up.z, 0
0,     0,     0,   1

 

D3DXMATRIX mat;
mat._11=Front.x;mat._21=Front.y;mat._31=Front.z;mat._41=0;
mat._12=Right.x;mat._22=Right.y;mat._32=Right.z;mat._42=0;
mat._13=Up.x;mat._23=Up.y;mat._33=Up.z;mat._43=0;
mat._14=0;mat._24=0;mat._34=0;mat._44=1;
D3DXMatrixTranspose(&MatRotation,&mat);

     按照这种方式构造旋转矩阵将使得物体“面向”Front所指的方向。

② 转置,以便和其他变换矩阵相乘。

D3DXMatrixRotationAxis(&mat,&Front,NowState.FrontAngle);
MatRotation*=mat;
D3DXVec3TransformCoord(&Right,&Right,&mat);
D3DXVec3TransformCoord(&Up,&Up,&mat);
D3DXVec3TransformCoord(&Front,&Front,&mat);

只要每侦都执行上面的代码就可以保证旋转矩阵和基准轴保持同步。这样在发生旋转变换时
只要变换基准轴就可以了。下面的代码对基准轴进行变换,实现指定角度的翻滚。 
 
     D3DXMatrixRotationAxis可以绕任意轴旋转的矩阵,第一个参数输出结果;第二个参数为旋转轴,这里直接使用基准轴Front,绕Front轴旋转的结果就是翻滚;第三个参数为旋转角度。
     将新的旋转矩阵叠加到当前的旋转矩阵上。
     使用新矩阵变换现有的基准轴,D3DXVec3TransformCoord用于变换3D向量。
图2-16显示了一个FRU物体进行俯仰、偏转、翻滚变换的情况。
到此为止,物体状态的设置就可以完成了。但是,这样还不够,因为游戏最终需要的是一个运动过程。为了对动态的过程进行描述和操作,在Phyical_FRU加入了两个状态的集合:NowState,NewState,它们都是FRUState结构的实例,FRUState结构包含了足以描述各种刚体运动状态的数据。NowState记录了当前时刻的运动状态,NewState记录了预计在一定时间内将要达到的运动状态。程序每一帧都会在两个状态之间插值以决定当前时刻的运动状态。当需要物体做出某种运动时,只要修改NewState的相关数值,并设定预计完成该动作的时间即可。
 
上图分别是一个游戏实体做出俯仰、偏转、翻滚变换的情况。
 
2 . 碰撞与反弹
游戏实体在运动中应该受到物理定律的约束,这样才能带来真实的游戏体验。但是,由于时间关系,本课题只实现了最基本的物理现象模拟:碰撞。
2.1碰撞检测
在若干个多边形之间检测有没有发生碰撞绝对不像“看”起来那么简单。因为游戏实体的形状是没有特定规律可寻的,而且每个游戏实体都可能由成千上万个多边形组成,在这成千上万个多边形之间执行“严格”的检测绝对不是现在的家用计算机能够胜任的。另一方面,游戏过程中也没有必要进行如此精确的碰撞检测。所以使用包围盒进行碰撞检测就是一种比较“合算”的方法。但是只进行包围盒对包围盒的碰撞检测也是不够的,因为这样会忽略复杂物体表面的细节,产生很不自然的结果。所以,在编写碰撞检测时使用多种碰撞检测方式结合的方法往往可以带来最理想的结果。
在Phyical_Collision中实现了碰撞检测所需要的各种方法。碰撞检测的对象被分为碰撞主体和碰撞客体,碰撞主体必须为包围盒,这样可以避免在多边形之间进行碰撞检测;碰撞客体可以为包围盒也可以为多边形,但是碰撞客体的包围盒必须是“轴向”的(包围盒的所有边都和相应坐标轴平行)。这样整个碰撞检测的过程就可以分为包围盒对包围盒的碰撞以及包围盒对多边形的碰撞。
包围盒对包围盒的碰撞十分简单,只要判断碰撞主体的包围盒顶点是否在客体的最大点和最小点之间,能够这么做是因为客体使用“轴向”包围盒。
而包围盒对多边形的碰撞检测就相对复杂的多。为了加快运算速度,避免不必要的碰撞检测,可以考虑使用两极碰撞检测。具体的说就是先进行包围盒对包围盒的碰撞,当碰撞发生时再进行包围盒对多边形的碰撞检测。 上图可以看到碰撞检测时使用的包围盒、碰撞光线、以及碰撞点的法线。 
下面的伪码描述包围盒对多边形的碰撞检测过程。

For(碰撞主体每个包围盒的顶点)
{
以当前顶点为原点,向当前移动方向发射一条射线。
使用D3DXIntersect判断该射线是否穿过碰撞客体。
If(穿过)
计算交点到当前顶点的距离D。
If(D>碰撞主体的半径)继续下一个顶点
Else
{
出现碰撞,获得与射线相交的三角形序号
利用索引缓冲获得三角形顶点序号
利用三角形顶点序号访问顶点缓冲
获得每个顶点的法线信息
利用顶点法线插值计算交点法线
和其他信息一起添加到碰撞队列中①
返回真
}
}
Else
{继续下一个顶点}
}

 
①碰撞队列用于保存当前侦发生的碰撞信息,该队列由Physical _Manager管理,在每一侦开始前被清空。该队列的数据将在计算摩擦反弹以及处理游戏逻辑时使用。
 
   
2.2 反弹和摩擦
在碰撞检测中得到的碰撞点法线信息可以被用于计算碰撞发生后物体的运动方向。碰撞发生后可能会有两种极端的情况:1.沿着法线的方向移动(完全反弹) 2.沿着切线方向移动(完全摩擦)。这里可以使用摩擦系数和反弹系数来控制物体碰撞后的走向。下面的代码片段用于计算碰撞发生后的反弹方向:

D3DXVECTOR3 refdirNormalized,refdir,normalvel,transformvel,DirectionNormalized;
float normalfac,length;
D3DXVec3Normalize(&DirectionNormalized,&direction);
D3DXVec3Normalize(&Normal,&Normal);
refdirNormalized=DirectionNormalized+
Normal*(-2.0f*D3DXVec3Dot(&Normal,&DirectionNormalized ));
D3DXVec3Normalize(&refdirNormalized,&refdirNormalized);
 D3DXVECTOR3 NormalizedPart,TangentPart;
 float DotNR=D3DXVec3Dot(&Normal,&refdirNormalized);
 if(DotNR<=0)return D3DXVECTOR3(0,0,0);
NormalizedPart=Normal*DotNR;
TangentPart=refdirNormalized-NormalizedPart; 
 refdir=bumpfac*NormalizedPart+TangentPart*frictionfac
        
     计算反弹方向,该方向为摩擦系数等于反弹系数时的运动方向。

     利用法线和反弹方向的点乘判断反弹方向是否保持在物体的正面。
     计算法线方向上的反弹分量。
     计算切线方向上的摩擦分量。
     利用摩擦系数和反弹系数算出最终的移动方向。
通过修改摩擦系数和反弹系数就可以适应不同情况的要求了。
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值