创建一个角色控制器,第一步要做的事情是决定边界体。就目前来说,PhysX只支持立方体(NxBoxController)和胶囊(NxCapsulesController)。
第二,创建一个控制器管理器(controller manager)。如:
NxControllerManager* gManager = NxCreateControllerManager(myAllocator);
注意:控制器管理器是单件(同一时间只存在一个实例)。
第三、创建一个角色控制器。如:
NxScene* scene;
NxCapsuleControllerDesc desc;
<填充控制器描述符>
NxController* cpController = gManager->createController(scene,desc);
当你不需要控制器管理器的时候,应该释放它。
NxReleaseControllerManager( gManager);
注意:以前的类名“ControllerManager”现在也有效,但是不建议使用,并且它将在下一个版本被取消掉。
每一贞都可以调用以下函数来移动角色。
NxController::move(constNxVec3& disp,
NxU32 activeGroups, NxF32 minDist, NxU32& collisionFlags, NxF32 sharpness= 1.0f , const NxGroupsMask* groupsMask=NULL);
Disp: 位移向量
activeGroups: 碰撞组掩码
minDist:最小距离,角色的位移低于这个值将不发生位移。
collisionFlags:碰撞检测类型掩码
sharpness:锐度?
groupsMask:?
在每一贞里,可以通过角色控制器的position信息来保持外观模型的位置同步。一个控制器从来都不管旋转的事。因此也只能得到位置相关的信息。如:
const NxVec3& NxController::getPosition() const;
把这个信息传递给你的世界坐标矩阵,然后去渲染,这样你的角色位置就是经过碰撞检测修正过的,并且表现出更加平滑的移动。
角色使用的边界体只限于PhysX提供的标准几何体。
目前只支持2种不同的形状:
AABB:轴对齐的边界盒。由一个position和一个extents向量定义。这个盒子不会随着角色的旋转而旋转,始终是与世界的X、Y、Z 3个轴对齐的。
Capsule:胶囊。由一个position, 一个 height 和 一个 radius定义。(2个球+1个圆柱)。它可以获得更加平滑的移动,但是也会稍微多花些CPU时间。
注意:在版本2.3之前,有一个 球型控制器 NxSphereController,但是自从有了胶囊,它就被抛弃了。
注意:两种控制器都使用 Sweep API ,这就意味着并不是所有的可碰撞的形状都可以用于角色运动,只有那些可以被扫描的形状才能用于角色运动的判定。
角色碰撞体周围有一层被定义为“皮肤”,它被用来决定是否与其他形状有接触。皮肤的厚度由用户定义。如果为了Debug而显示角色碰撞体,别忘了还有个皮肤的概念。
AABB相关变量:
NxControllerDesc::position
NxControllerDesc::skinWidth
NxBoxControllerDesc::extents
Capsule相关变量
NxControllerDesc::position
NxControllerDesc::skinWidth
NxCapsuleControllerDesc::radius
NxCapsuleControllerDesc::height
关于AABB的一些解释:
皮肤:外表面
Extents:内表面
如果没有自动上台阶(跨越障碍)的功能,地面上一个非常小的台阶都会阻挡角色的移动。显然这不自然,因为如果台阶不是很高的话,我们可以轻松的走上去才对。但是台阶太高了的话,就不应该走上去。这个界定值由以下变量给出:
NxControllerDesc::stepOffset
为了实现自动上台阶,PhysX需要知道“上”的方向。当前PhysX只支持3个轴向向量:
NX_X ( 1, 0, 0 )
NX_Y ( 0, 1, 0 )
NX_Z ( 0, 0, 1 )
相关的变量为:
NxControllerDesc::upDirection
默认情况下,角色可以走到任何地方。不过在现实世界总是有限制存在。因此,“不允许在角度很大的多边形上走”的限制就有存在的必要。只要用户给出角度界定,PhysX就可以自动的把多边形分为“可以在上面行走的”和“不可以在上面行走的”。将来可能把检测级别推到三角面级别的精确度。
可以通过以下变量来设置界定值:
NxControllerDesc::slopeLimit
它是角度的cos。下面的代码规定了只有小于45度的多边形上才可以行走:
slopeLimit = cosf( NxMath::degToRad( 45.0F ));
如果值设置为 0.0f ,该特性就自动关闭(那样的话,角色哪都能走了)
这个特性并不总是需要的。一个更加普遍的做法是关掉这个特性,然后使用看不见的墙来限制角色的运动范围。
有时候实时的改变角色碰撞体的大小非常有用。比如如果你的角色可以蹲下,那么如果可以把碰撞体的高度减少的话,就可以移动到之前不能去的地方了
角色控制库支持动态更新碰撞体。但是如果不经过检测就直接改变碰撞体大小,很可能会使碰撞体在设置完之后与其他几何体相交或重叠。为了避免这种情况,首先应该调用PhysX为此准备的一些API来进行测试,保证碰撞体有足够的空间来更改大小,只有通过这些API检测之后,才对碰撞体进行更新。
跟AABB相关的函数:
bool NxBoxController::setExtents( const NxVec3& extents) = 0;
跟胶囊相关的函数:
bool NxCapsuleController::setRadius(NxF32 radius) = 0;
bool NxCapsuleController::setHeight(NxF32 radius) = 0;
相关的检测函数:
bool NxScene::checkOverlapSphere();
bool NxScene::checkOverlapAABB();
bool NxScene::checkOverlapCapsule();
当控制器发生一些事件的时候,可以设置回调函数。(比如当一个角色撞到了一个形状,或者另外一个角色)
当一个角色碰撞了另外一个形状,可以得到以下信息:
1、 当前控制器
2、 被碰撞的形状
3、 碰撞点
4、 碰撞点的法向量
5、 一个特征码(比如三角形序号)
6、 额外的运动数据。
这些信息可以应用于游戏的其他方面,比如播放音效、渲染细节、应用作用力或者别的什么。
注意:现在还不是所有的信息都可以得到,这取决于角色撞上了什么样的形状。最终会支持所有的形状。
当一个角色碰撞了另外一个角色,比如一个角色撞到了一个NPC,可以得到以下信息:
1、一对控制器
在游戏中,根据回调函数中返回的不同的动作编号,会产生不用的行为。例如PhysX要是觉得角色足够强壮,那他就可以推动某些箱子。
现在支持的动作编号如下:
NX_ACTION_NONE => 不允许物理(角色推不动对象)
NX_ACTION_PUSH => 允许物理(角色推的动对象)
如果给接触点一个作用力,物理引擎就会模拟出自然的推动过程,听起来是非常Cool的。不过这个特性对游戏来说可能会起反作用。边界体的形状是人为控制的,但是你不希望因为从Box控制器切换到Capsule控制器而导致了推动的效果发生变化。推动的效果应该游戏说了算。因此通常比较好的办法是在回调函数中给接触点一个作用力,而不是完全交给物理引擎去计算。另外如果是个胶囊的话,也很难推动一个盒子,因为你几乎不可能对着盒子的中间,推动盒子就会使盒子旋转,虽然你可能只是想把盒子沿直线推一点点。
注意:NX_ACTION_PUSH 当前还没有实现,例子中的效果是使用碰撞时提供的信息来实现的。
角色控制库 为每个角色创建了一个隐藏的运动学Actor。有时候这就是引起Actors数量混乱的原因。因此可能得到一个错误的Actor数量,或者在查询Actor的时候,返回了一个未知的Actor。
当角色控制器处理角色移动的时候,也会同步这个Actor。
可以通过以下函数得到这个Actor:
NxActor* NxController::getActor() const;
不过,应该避免直接操作这个Actor,这会导致问题。不过只是读取信息则没有问题。
NxCharacter 库里对Actor的操作跟普通的Actor没什么区别。精确的讲,他们使用相同的时间片策略(定长/变长)。当使用定长时间片策略时,会造成一些麻烦。因为NxController总是使用变长时间片策略(大多数情况下,是每贞的间隔)。因此在这种情况下NxController对象并不会跟Actor对象总是精确的同步。