游戏人生
首页
(current)
GameDevTools
登陆
|
注册
个人中心
注销
游戏引擎 浅入浅出
Introduction
Introduction
前言
前言
1. 游戏引擎框架介绍
1. 游戏引擎框架介绍
1.1 Unity的组成
1.2 游戏引擎组成
2. Opengl开发环境搭建
2. Opengl开发环境搭建
2.1 Opengl到底是什么
2.2 搭建Opengl开发环境
2.3 使用VisualStudio开发
3. 绘制多边形
3. 绘制多边形
3.1 画个三角形
3.2 画个正方形
3.3 画个立方体
4. 着色器
4. 着色器
4.1 Unity Shader和OpenGL Shader
4.2 顶点着色器
4.3 片段着色器
5. 绘制贴图
5. 绘制贴图
5.1 颜色和贴图
5.2 贴图文件介绍
5.3 CPU与GPU的通信方式
5.4 使用stb_image解析图片
5.5 绘制带贴图的立方体盒子
5.6 压缩纹理
5.7 图片压缩工具
5.8 使用压缩纹理
5.9 DXT压缩纹理扩展
6. 索引与缓冲区对象
6. 索引与缓冲区对象
6.1 顶点索引
6.2 缓冲区对象
6.3 OpenGL Core Profile
6.4 顶点数组对象
7. 绘制Mesh和材质
7. 绘制Mesh和材质
7.1 导出Mesh文件
7.2 使用Mesh文件
7.3 Shader文件创建与使用
7.4 创建材质
7.5 使用材质
7.6 MeshRenderer
8. 绘制静态模型
8. 绘制静态模型
8.1 Blender安装与配置
8.2 Blender制作模型
8.3 Blender Python设置开发环境
8.4 Blender Python创建物体
8.5 Blender Python导出顶点数据
8.6 加载导出的Mesh
9. 基于组件开发
9. 基于组件开发
9.1 基于RTTR实现反射
9.2 实现GameObject-Component
10. 相机
10. 相机
10.1 最简单的相机
10.2 多相机渲染
10.3 相机排序
10.4 CullingMask
11. 控制系统
11. 控制系统
11.1 键盘控制
11.2 鼠标控制
12. 拆分引擎和项目
12. 拆分引擎和项目
13. 绘制文字
13. 绘制文字
13.1 TrueType简介
13.2 绘制单个字符
13.3 绘制多个文字
13.4 彩色字
14. GUI
14. GUI
14.1 正交相机
14.2 UIImage
14.3 UIMask
14.4 UIText
14.5 UIButton
15. 播放音效
15. 播放音效
15.1 播放2D音效
15.2 播放3D音效
15.3 使用FMOD Studio音频引擎
16. Profiler
16. Profiler
16.1 初识easy_profiler
16.2 集成easy_profiler
17. 嵌入Lua
17. 嵌入Lua
17.1 Sol2与C++交互
17.2 更加友好的Lua框架设计
17.3 引擎集成sol2
17.4 调试Lua
18. 骨骼动画
18. 骨骼动画
18.1 Blender制作骨骼动画
18.2 Blender导出骨骼动画
18.3 解析骨骼动画
18.4 矩阵的主序
19. 骨骼蒙皮动画
19. 骨骼蒙皮动画
19.1 骨骼蒙皮动画实现
19.2 骨骼权重
19.3 Blender蒙皮刷权重
19.4 Blender导出蒙皮权重
19.5 加载权重文件
20. 解析FBX文件
20. 解析FBX文件
20.1 导出Mesh
20.2 导出骨骼动画
20.3 导出权重
20.4 渲染骨骼蒙皮动画
21. 多线程渲染
21. 多线程渲染
21.1 GLFW多线程渲染
21.2 基于任务队列的多线程渲染
21.3 完全异步的多线程模型
21.4 引擎支持多线程渲染
22. Physx物理引擎
22. Physx物理引擎
22.1 Physx实例-小球掉落
22.2 物理材质
22.3 碰撞检测
22.4 连续碰撞检测
22.5 场景查询
22.6 引擎集成Physx
23. 经典光照
23. 经典光照
23.1 环境光
23.2 漫反射光照模型
23.3 镜面高光光照模型
23.4 高光贴图
23.5 Shader结构体
23.6 Uniform Buffer Object
23.7 方向光
23.8 点光源
23.9 多光源
24. 引擎编辑器的实现
24. 引擎编辑器的实现
24.1 分析Godot引擎编辑器
24.2 FBO RenderTexture GameTurbo DLSS
24.3 ImGui介绍与使用
24.4 分离引擎核心层和应用层
24.5 使用ImGui实现引擎编辑器
24.6 Hierarchy与Inspector面板
24.7 Geometry Buffer
25. Shadow Mapping
25. Shadow Mapping
25.1 深度图
25.2 简单阴影
88. VSCode扩展开发与定制
88. VSCode扩展开发与定制
88.1 第一个VSCode扩展程序
88.2 从源码编译VSCode
88.3 打包VSCode内置扩展
88.4 打包LuaHelper到Code-OSS
89. Doxygen生成API文档
89. Doxygen生成API文档
90. GPU分析工具
90. GPU分析工具
90.1 RenderDoc分析不显示bug
98. SubstancePainter插件开发
98. SubstancePainter插件开发
98.1 SP插件开发环境
98.2 开发SP功能性插件
98.3 开发SP渲染插件
99. Toolbag插件开发
99. Toolbag插件开发
99.1 插件开发环境
99.2 API介绍
99.3 命令行调用Toolbag
99.4 更多实现
99.5 代码参考
附录1. Wwise音频引擎
附录1. Wwise音频引擎
1.1 Wwise名词概念
1.2 Wwise制作音效导出SoundBank
1.3 集成Wwise
1.4 封装Wwise播放3D音效
1.5 Wwise性能分析器介绍
1.6 猎人开发后记
代码资源下载
点我下载
Github
点赞、收藏、关注
目录
<< 22.3 碰撞检测
22.5 场景查询 >>
## 22.4 连续碰撞检测 ```text 「游戏引擎 浅入浅出」是一本开源电子书,PDF/随书代码/资源下载: https://github.com/ThisisGame/cpp-game-engine-book ``` ```bash CLion项目文件位于 samples\physx\physx_ccd ``` 在枪战类游戏会遇到一个问题,子弹穿透。 子弹速度极快,当子弹飞向敌人,会发现physx判定为没有碰撞发生。 以上一小节的项目,将速度乘以10,测试发现小球不再被墙体反弹,而是直接穿过去。 ```c++ //~zh 创建小球,并添加到场景中 //~en Create ball,add to scene. void CreateBall(){ //~en Create RigidBody,pos is (10,0,0) //~zh 创建刚体,坐标是 (10,0,0) PxRigidDynamic* body = gPhysics->createRigidDynamic(PxTransform(PxVec3(10, 5, 0))); //body->setLinearVelocity(PxVec3(-14.0f, 0.0f, 0.0f)); body->setLinearVelocity(PxVec3(-140.0f, 0.0f, 0.0f));//high-speed ...... } ```  那为什么会出现这种问题? 我们知道游戏引擎其实就是一个while循环,每一次循环里,先在`Update()`函数里处理游戏逻辑,然后在`Render()`函数去处理渲染任务。 Physx作为物理模块集成到引擎后,也要遵守这个规则,将处理Physx逻辑放在`FixUpdate()`函数里面。 <font color=red> FixUpdate() 函数是固定速率的。 这个固定速率是说一段时间内保证调用固定次数,例如一秒钟保证调用60次,不是说每一次调用间隔固定时间。 如果Update()、Render() 耗时很长,那么这一帧就多调用几次FixUpdate()。 </font> Update()、Render()、FixUpdate()组成了游戏的一帧。 按30帧的渲染帧率,每一帧之间间隔了0.033秒。 以100米/s的子弹速度,前后两帧子弹位移了3.3米。 前一帧子弹在敌人前面,子弹与敌人碰撞盒子没有相交,判定没有碰撞。 后一帧子弹飞行了3.3米,又到了敌人后面,子弹与敌人的碰撞盒子又没有相交,还是判定没有碰撞。 常用的解决办法是,从子弹当前位置向上一个位置打射线,检测射线是否打到了敌人的碰撞盒子。 Physx也提供了解决方案 -- 连续碰撞检测(Continuous Collision Detection,简称CCD)。 ### 1. 启用CCD 启用CCD分为2步,对场景启用CCD,对高速运动物体启用CCD。下面分别来看。 #### 1. 对场景启用CCD ```c++ //file:example/main.cpp line:43 //~zh 设置在碰撞发生时,Physx需要做的事情 //~en Set the actions when collision occurs,Physx needs to do. static PxFilterFlags SimulationFilterShader(PxFilterObjectAttributes attributes0, PxFilterData filterData0,PxFilterObjectAttributes attributes1, PxFilterData filterData1,PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize) { //~zh eNOTIFY_TOUCH_FOUND:当碰撞发生时处理回调。 eNOTIFY_TOUCH_LOST:当碰撞结束时处理回调。 //~en eNOTIFY_TOUCH_FOUND:When collision occurs,process callback. eNOTIFY_TOUCH_LOST:When collision ends,process callback. pairFlags = PxPairFlag::eCONTACT_DEFAULT | PxPairFlag::eNOTIFY_TOUCH_FOUND | PxPairFlag::eNOTIFY_TOUCH_LOST; if(gEnableCCD){ //~zh 场景启用CCD后,还需要指定碰撞时使用CCD,并且处理回调。 //~en When the scene is enabled CCD, you need to specify the collision to use CCD and handle the callback. pairFlags |= PxPairFlag::eDETECT_CCD_CONTACT|PxPairFlag::eNOTIFY_TOUCH_CCD; } return PxFilterFlags(); } //~en Create Scene //~zh 创建Scene void CreateScene(){ ...... if(gEnableCCD){ //~zh 启用CCD //~en Enable CCD sceneDesc.flags |= PxSceneFlag::eENABLE_CCD; } ...... } ``` 对场景启用CCD后,还需要在 `SimulationFilterShader` 中指定碰撞时使用CCD,并且处理回调。 #### 2. 对高速运动小球启用CCD ```c++ //file:example/main.cpp line:114 //~zh 创建小球,并添加到场景中 //~en Create ball,add to scene. void CreateBall(){ //~en Create RigidBody,pos is (10,0,0) //~zh 创建刚体,坐标是 (10,0,0) PxRigidDynamic* body = gPhysics->createRigidDynamic(PxTransform(PxVec3(10, 5, 0))); body->setLinearVelocity(PxVec3(-140.0f, 0.0f, 0.0f)); if(gEnableCCD){ //~en enable continuous collision detection due to high-speed motion. //~zh 对高速运动,开启连续碰撞检测。 body->setRigidBodyFlag(PxRigidBodyFlag::eENABLE_CCD, true); } ...... } ``` ### 2. 在回调中检测Flag 在 `SimulationFilterShader` 中指定了 `PxPairFlag`,表示需要处理进入碰撞、解除碰撞的回调。 在回调中,将 `event` 与指定的Flag对比,来区分进入碰撞、解除碰撞。 <a id="antiCollectorAdTxt" href="https://github.com/ThisisGame/cpp-game-engine-book">「游戏引擎 浅入浅出」是一本开源电子书,PDF/随书代码/资源下载: https://github.com/ThisisGame/cpp-game-engine-book</a> ```c++ //file:example/simulation_event_callback.h line:46 //~en SimulationEventCallback is a PxSimulationEventCallback that is used to receive events from the PhysX SDK. //~zh SimulationEventCallback 是一个用于从 PhysX SDK 接收事件的 PxSimulationEventCallback。 class SimulationEventCallback: public PxSimulationEventCallback { public: ...... void onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, PxU32 count) override { printf("onContact: %d pairs\n", count); while(count--) { const PxContactPair& current = *pairs++; if(current.events & (PxPairFlag::eNOTIFY_TOUCH_FOUND|PxPairFlag::eNOTIFY_TOUCH_CCD)) { printf("onContact Shape is entering volume\n"); } if(current.events & PxPairFlag::eNOTIFY_TOUCH_LOST) { printf("onContact Shape is leaving volume\n"); } ...... } } }; ``` ### 3. 测试CCD 启用CCD。 ```c++ //file:example/main.cpp line:20 //~en is high-speed motion enabled. //~zh 高速运动是否启用。 bool gEnableCCD = true; ``` 开启PVD,运行项目测试。  可以看到小球高速撞向墙壁,并且被反弹。  也输出了进入碰撞、解除碰撞的Log。 ### 4. CCD Trigger ```bash CLion项目文件位于 samples\physx\physx_ccd_trigger ``` 上面连续碰撞检测可以了,那Trigger该怎么弄呢? 下面代码直接给墙壁指定为 `PxShapeFlag::eTRIGGER_SHAPE` 是不行的: ``` PxShape* shape = gPhysics->createShape(PxBoxGeometry(halfExtent), *wallMaterial,false,PxShapeFlag::eVISUALIZATION | PxShapeFlag::eTRIGGER_SHAPE); ``` 因为CCD不支持Trigger。 我想了一下,Trigger其实也是走的碰撞检测逻辑,只不过碰撞检测完之后,没有去执行碰撞这个行为。 Shape对象可以附带一些信息,在墙的Shape附带信息里面设置一个1,就可以表示这是Trigger。 ```c++ //file:example/main.cpp line:98 //~en Create wall,add to scene. //~zh 创建墙,并添加到场景中 void CreateWall(){ ...... //~en Create wall shape. //~zh 创建墙体形状 const PxVec3 halfExtent(0.1f, 10.0f, 10.0f); PxShape* shape = gPhysics->createShape(PxBoxGeometry(halfExtent), *wallMaterial); //~zh 设置附加数据为1,表示当前Shape是Trigger //~en set shape's user data 1, it is a trigger. shape->setSimulationFilterData(PxFilterData(1,0,0,0)); ...... } ``` 然后我只要在 `SimulationFilterShader` 中,指定不执行碰撞行为就可以了。 ```c++ //file:example/main.cpp line:44 //~zh 设置在碰撞发生时,Physx需要做的事情 //~en Set the actions when collision occurs,Physx needs to do. static PxFilterFlags SimulationFilterShader(PxFilterObjectAttributes attributes0, PxFilterData filterData0,PxFilterObjectAttributes attributes1, PxFilterData filterData1,PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize) { //~zh eNOTIFY_TOUCH_FOUND:当碰撞发生时处理回调。 eNOTIFY_TOUCH_LOST:当碰撞结束时处理回调。 //~en eNOTIFY_TOUCH_FOUND:When collision occurs,process callback. eNOTIFY_TOUCH_LOST:When collision ends,process callback. pairFlags = PxPairFlag::eSOLVE_CONTACT | PxPairFlag::eDETECT_DISCRETE_CONTACT | PxPairFlag::eNOTIFY_TOUCH_FOUND | PxPairFlag::eNOTIFY_TOUCH_LOST; //~zh Trigger的意思就是不处理物理碰撞,只是触发一个回调函数。 //~en Trigger means that the physical collision is not processed,only a callback function is triggered. bool isTrigger=filterData0.word0==1 || filterData1.word0==1; if(isTrigger) { pairFlags = pairFlags ^ PxPairFlag::eSOLVE_CONTACT; } ...... } ``` 然后在回调里面也对附加数据进行判断,来判定是不是Trigger,来输出不同的Log。 ```c++ //file:example/simulation_event_callback.h line:46 //~en SimulationEventCallback is a PxSimulationEventCallback that is used to receive events from the PhysX SDK. //~zh SimulationEventCallback 是一个用于从 PhysX SDK 接收事件的 PxSimulationEventCallback。 class SimulationEventCallback: public PxSimulationEventCallback { public: ...... void onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, PxU32 count) override { printf("onContact: %d pairs\n", count); while(count--) { const PxContactPair& current = *pairs++; //~zh 判断Shape附加数据为1表示Trigger。 //~en If the shape's user data is 1, it is a trigger. if(current.shapes[0]->getSimulationFilterData().word0 & 0x1) { printf("Shape 0 is a trigger\n"); } if(current.shapes[1]->getSimulationFilterData().word0 & 0x1) { printf("Shape 1 is a trigger\n"); } if(current.events & (PxPairFlag::eNOTIFY_TOUCH_FOUND|PxPairFlag::eNOTIFY_TOUCH_CCD)) { printf("onContact Shape is entering volume\n"); } if(current.events & (PxPairFlag::eNOTIFY_TOUCH_LOST)) { printf("onContact Shape is leaving volume\n"); } } } }; ``` 运行项目测试,输出了Trigger的Log。  在PVD中看到小球穿墙而过。 
<< 22.3 碰撞检测
22.5 场景查询 >>
12
代码资源下载
点我下载
Github
点赞、收藏、关注
目录
Introduction
Introduction
前言
前言
1. 游戏引擎框架介绍
1. 游戏引擎框架介绍
1.1 Unity的组成
1.2 游戏引擎组成
2. Opengl开发环境搭建
2. Opengl开发环境搭建
2.1 Opengl到底是什么
2.2 搭建Opengl开发环境
2.3 使用VisualStudio开发
3. 绘制多边形
3. 绘制多边形
3.1 画个三角形
3.2 画个正方形
3.3 画个立方体
4. 着色器
4. 着色器
4.1 Unity Shader和OpenGL Shader
4.2 顶点着色器
4.3 片段着色器
5. 绘制贴图
5. 绘制贴图
5.1 颜色和贴图
5.2 贴图文件介绍
5.3 CPU与GPU的通信方式
5.4 使用stb_image解析图片
5.5 绘制带贴图的立方体盒子
5.6 压缩纹理
5.7 图片压缩工具
5.8 使用压缩纹理
5.9 DXT压缩纹理扩展
6. 索引与缓冲区对象
6. 索引与缓冲区对象
6.1 顶点索引
6.2 缓冲区对象
6.3 OpenGL Core Profile
6.4 顶点数组对象
7. 绘制Mesh和材质
7. 绘制Mesh和材质
7.1 导出Mesh文件
7.2 使用Mesh文件
7.3 Shader文件创建与使用
7.4 创建材质
7.5 使用材质
7.6 MeshRenderer
8. 绘制静态模型
8. 绘制静态模型
8.1 Blender安装与配置
8.2 Blender制作模型
8.3 Blender Python设置开发环境
8.4 Blender Python创建物体
8.5 Blender Python导出顶点数据
8.6 加载导出的Mesh
9. 基于组件开发
9. 基于组件开发
9.1 基于RTTR实现反射
9.2 实现GameObject-Component
10. 相机
10. 相机
10.1 最简单的相机
10.2 多相机渲染
10.3 相机排序
10.4 CullingMask
11. 控制系统
11. 控制系统
11.1 键盘控制
11.2 鼠标控制
12. 拆分引擎和项目
12. 拆分引擎和项目
13. 绘制文字
13. 绘制文字
13.1 TrueType简介
13.2 绘制单个字符
13.3 绘制多个文字
13.4 彩色字
14. GUI
14. GUI
14.1 正交相机
14.2 UIImage
14.3 UIMask
14.4 UIText
14.5 UIButton
15. 播放音效
15. 播放音效
15.1 播放2D音效
15.2 播放3D音效
15.3 使用FMOD Studio音频引擎
16. Profiler
16. Profiler
16.1 初识easy_profiler
16.2 集成easy_profiler
17. 嵌入Lua
17. 嵌入Lua
17.1 Sol2与C++交互
17.2 更加友好的Lua框架设计
17.3 引擎集成sol2
17.4 调试Lua
18. 骨骼动画
18. 骨骼动画
18.1 Blender制作骨骼动画
18.2 Blender导出骨骼动画
18.3 解析骨骼动画
18.4 矩阵的主序
19. 骨骼蒙皮动画
19. 骨骼蒙皮动画
19.1 骨骼蒙皮动画实现
19.2 骨骼权重
19.3 Blender蒙皮刷权重
19.4 Blender导出蒙皮权重
19.5 加载权重文件
20. 解析FBX文件
20. 解析FBX文件
20.1 导出Mesh
20.2 导出骨骼动画
20.3 导出权重
20.4 渲染骨骼蒙皮动画
21. 多线程渲染
21. 多线程渲染
21.1 GLFW多线程渲染
21.2 基于任务队列的多线程渲染
21.3 完全异步的多线程模型
21.4 引擎支持多线程渲染
22. Physx物理引擎
22. Physx物理引擎
22.1 Physx实例-小球掉落
22.2 物理材质
22.3 碰撞检测
22.4 连续碰撞检测
22.5 场景查询
22.6 引擎集成Physx
23. 经典光照
23. 经典光照
23.1 环境光
23.2 漫反射光照模型
23.3 镜面高光光照模型
23.4 高光贴图
23.5 Shader结构体
23.6 Uniform Buffer Object
23.7 方向光
23.8 点光源
23.9 多光源
24. 引擎编辑器的实现
24. 引擎编辑器的实现
24.1 分析Godot引擎编辑器
24.2 FBO RenderTexture GameTurbo DLSS
24.3 ImGui介绍与使用
24.4 分离引擎核心层和应用层
24.5 使用ImGui实现引擎编辑器
24.6 Hierarchy与Inspector面板
24.7 Geometry Buffer
25. Shadow Mapping
25. Shadow Mapping
25.1 深度图
25.2 简单阴影
88. VSCode扩展开发与定制
88. VSCode扩展开发与定制
88.1 第一个VSCode扩展程序
88.2 从源码编译VSCode
88.3 打包VSCode内置扩展
88.4 打包LuaHelper到Code-OSS
89. Doxygen生成API文档
89. Doxygen生成API文档
90. GPU分析工具
90. GPU分析工具
90.1 RenderDoc分析不显示bug
98. SubstancePainter插件开发
98. SubstancePainter插件开发
98.1 SP插件开发环境
98.2 开发SP功能性插件
98.3 开发SP渲染插件
99. Toolbag插件开发
99. Toolbag插件开发
99.1 插件开发环境
99.2 API介绍
99.3 命令行调用Toolbag
99.4 更多实现
99.5 代码参考
附录1. Wwise音频引擎
附录1. Wwise音频引擎
1.1 Wwise名词概念
1.2 Wwise制作音效导出SoundBank
1.3 集成Wwise
1.4 封装Wwise播放3D音效
1.5 Wwise性能分析器介绍
1.6 猎人开发后记