游戏引擎 浅入浅出

Introduction

前言












12. 拆分引擎和项目















89. Doxygen生成API文档





代码资源下载


目录

22.3 碰撞检测

  1. 「游戏引擎 浅入浅出」是一本开源电子书,PDF/随书代码/资源下载: https://github.com/ThisisGame/cpp-game-engine-book
  1. CLion项目文件位于 samples\physx\physx_collision_detection

在Physx的设定,刚体(PxRigidBody)只是一个质点,它拥有质量,但是具体的表现是通过形状(PxShape)来约束的。

同一个刚体,可以附加一个正方体的Shape,也可以附加一个球体的Shape,不同形状的Shape有着不同的物理检测的结果。

对同一个形状的Shape,可以设置不同的标志(PxShapeFlag),PxShapeFlag::eSIMULATION_SHAPE表示刚体将在物理模拟中参与碰撞,PxShapeFlag::eTRIGGER_SHAPE则表示只作为Trigger,不参与碰撞。

其实在上一节的两个实例,就已经是参与碰撞的。

这是因为创建Shape默认是指定了PxShapeFlag::eSIMULATION_SHAPE,即参与碰撞。

  1. //file:physx/include/PxPhysics.h line:377
  2. //@}
  3. /** @name Shapes
  4. */
  5. //@{
  6. /**
  7. \brief Creates a shape which may be attached to multiple actors
  8. The shape will be created with a reference count of 1.
  9. \param [in] geometry The geometry for the shape
  10. \param [in] material The material for the shape
  11. \param [in] isExclusive Whether this shape is exclusive to a single actor or maybe be shared
  12. \param [in] shapeFlags The PxShapeFlags to be set
  13. Shared shapes are not mutable when they are attached to an actor
  14. @see PxShape
  15. */
  16. PX_FORCE_INLINE PxShape* createShape( const PxGeometry& geometry,
  17. const PxMaterial& material,
  18. bool isExclusive = false,
  19. PxShapeFlags shapeFlags = PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eSIMULATION_SHAPE)
  20. {
  21. PxMaterial* materialPtr = const_cast<PxMaterial*>(&material);
  22. return createShape(geometry, &materialPtr, 1, isExclusive, shapeFlags);
  23. }

但是之前并没有添加回调,下面就来将回调加上。

本小节通过小球撞墙的例子,来看着两个标志的表现,以及测试事件回调。

1. 事件回调

Physx提供了事件回调接口PxSimulationEventCallback

例子里继承并重写了接口,输出Log,用于观察碰撞检测。

  1. //file:example/simulation_event_callback.h line:14
  2. //~en SimulationEventCallback is a PxSimulationEventCallback that is used to receive events from the PhysX SDK.
  3. //~zh SimulationEventCallback 是一个用于从 PhysX SDK 接收事件的 PxSimulationEventCallback。
  4. class SimulationEventCallback: public PxSimulationEventCallback {
  5. public:
  6. void onConstraintBreak(PxConstraintInfo* constraints, PxU32 count) override {
  7. printf("onConstraintBreak\n");
  8. }
  9. void onWake(PxActor** actors, PxU32 count) override {
  10. printf("onWake\n");
  11. }
  12. void onSleep(PxActor** actors, PxU32 count) override {
  13. printf("onSleep\n");
  14. }
  15. void onTrigger(PxTriggerPair* pairs, PxU32 count) override {
  16. printf("onTrigger: %d trigger pairs\n", count);
  17. while(count--)
  18. {
  19. const PxTriggerPair& current = *pairs++;
  20. if(current.status & PxPairFlag::eNOTIFY_TOUCH_FOUND)
  21. printf("Shape is entering trigger volume\n");
  22. if(current.status & PxPairFlag::eNOTIFY_TOUCH_LOST)
  23. printf("Shape is leaving trigger volume\n");
  24. }
  25. }
  26. void onAdvance(const PxRigidBody*const*, const PxTransform*, const PxU32) override {
  27. printf("onAdvance\n");
  28. }
  29. void onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, PxU32 count) override {
  30. printf("onContact: %d pairs\n", count);
  31. }
  32. };

在创建Scene的时候,对SceneDesc指定了事件回调处理SimulationEventCallback的实例gSimulationEventCallback

  1. //file:example/main.cpp line:38
  2. //~zh 设置在碰撞发生时,Physx需要做的事情
  3. //~en Set the actions when collision occurs,Physx needs to do.
  4. static PxFilterFlags SimulationFilterShader(PxFilterObjectAttributes attributes0, PxFilterData filterData0,PxFilterObjectAttributes attributes1, PxFilterData filterData1,PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize) {
  5. pairFlags = PxPairFlag::eCONTACT_DEFAULT | PxPairFlag::eNOTIFY_TOUCH_FOUND;
  6. return PxFilterFlags();
  7. }
  8. //~en Create Scene
  9. //~zh 创建Scene
  10. void CreateScene(){
  11. PxSceneDesc sceneDesc(gPhysics->getTolerancesScale());
  12. sceneDesc.gravity = PxVec3(0.0f, -0.98f, 0.0f);
  13. gDispatcher = PxDefaultCpuDispatcherCreate(2);
  14. sceneDesc.cpuDispatcher = gDispatcher;
  15. //~zh 指定事件回调
  16. //~en Specify the event callback
  17. sceneDesc.simulationEventCallback = &gSimulationEventCallback;
  18. //~zh 设置在碰撞发生时,Physx需要做的事情
  19. //~en Set the actions when collision occurs,Physx needs to do.
  20. sceneDesc.filterShader = SimulationFilterShader;
  21. gScene = gPhysics->createScene(sceneDesc);
  22. PxPvdSceneClient* pvdClient = gScene->getScenePvdClient();
  23. if(pvdClient)
  24. {
  25. pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS, true);
  26. pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_CONTACTS, true);
  27. pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES, true);
  28. }
  29. }

由于物理引擎是特别消耗性能的,所以Physx默认是不处理碰撞检测回调的,所以还需要将碰撞检测回调的处理标志加上:

  1. //~zh 设置在碰撞发生时,Physx需要做的事情
  2. //~en Set the actions when collision occurs,Physx needs to do.
  3. sceneDesc.filterShader = SimulationFilterShader;

在函数SimulationFilterShader里,指定了PxPairFlag::eNOTIFY_TOUCH_FOUND,这个标志表示在碰撞发生时,Physx需要处理回调函数。

至此,事件回调就设置好了。

2. 检测碰撞

创建墙壁与小球。

  1. //file:example/main.cpp line:71
  2. //~en Create wall,add to scene.
  3. //~zh 创建墙,并添加到场景中
  4. void CreateWall(){
  5. //~en Create RigidBody,pos is (0,10,0)
  6. //~zh 创建刚体,坐标是 (0,10,0)
  7. PxRigidStatic* body = gPhysics->createRigidStatic(PxTransform(PxVec3(0, 10, 0)));
  8. //~en Create Physx Material.
  9. //~zh 创建物理材质
  10. PxMaterial* wallMaterial = gPhysics->createMaterial(1.0f, 1.0f, 0.0f);
  11. //~en Create wall shape.
  12. //~zh 创建墙体形状
  13. const PxVec3 halfExtent(0.1f, 10.0f, 10.0f);
  14. PxShape* shape = gPhysics->createShape(PxBoxGeometry(halfExtent), *wallMaterial);
  15. //~en Add shape to body.
  16. //~zh 设置刚体形状,长方体的一面墙。
  17. body->attachShape(*shape);
  18. shape->release();
  19. //~en Add body to scene.
  20. //~zh 将刚体添加到场景中
  21. gScene->addActor(*body);
  22. }
  23. //~zh 创建小球,并添加到场景中
  24. //~en Create ball,add to scene.
  25. void CreateBall(){
  26. //~en Create RigidBody,pos is (10,0,0)
  27. //~zh 创建刚体,坐标是 (10,0,0)
  28. PxRigidDynamic* body = gPhysics->createRigidDynamic(PxTransform(PxVec3(10, 5, 0)));
  29. body->setLinearVelocity(PxVec3(-14.0f, 0.0f, 0.0f));
  30. //~en Create Physx Material.
  31. //~zh 创建小球的物理材质
  32. PxMaterial* ballMaterial = gPhysics->createMaterial(0.5f, 0.5f, 1.0f);
  33. //~en Set rigid body sharp
  34. //~zh 设置刚体形状,一个球。
  35. float radius = 0.5f;
  36. PxShape* shape = gPhysics->createShape(PxSphereGeometry(radius), *ballMaterial);
  37. body->attachShape(*shape);
  38. shape->release();
  39. //~en calculate mass,mass = volume * density
  40. //~zh 根据体积、密度计算质量
  41. PxRigidBodyExt::updateMassAndInertia(*body, 1.0f);
  42. gScene->addActor(*body);
  43. }

和上一节的代码没有太大差别,只是创建地板换成了创建墙壁,就不多介绍了。

输出了碰撞的Log。

在PVD中可以看到发生了碰撞,小球被墙壁反弹。

3. 作为Trigger

将墙作为Trigger,不参与碰撞。

  1. //file:example/main.cpp line:71
  2. //~en Create wall,add to scene.
  3. //~zh 创建墙,并添加到场景中
  4. void CreateWall(){
  5. ......
  6. //~en Create wall shape.
  7. //~zh 创建墙体形状
  8. const PxVec3 halfExtent(0.1f, 10.0f, 10.0f);
  9. // PxShape* shape = gPhysics->createShape(PxBoxGeometry(halfExtent), *wallMaterial);
  10. PxShape* shape = gPhysics->createShape(PxBoxGeometry(halfExtent), *wallMaterial,false,PxShapeFlag::eVISUALIZATION | PxShapeFlag::eTRIGGER_SHAPE);
  11. ......
  12. }

输出了Trigger的Log。

在PVD中可以看到没有碰撞,小球直接穿过了墙壁。

Introduction

前言












12. 拆分引擎和项目















89. Doxygen生成API文档