如前一篇所说,游戏当中用到的物理仿真,一般包括以下两个主要的环节:
-
碰撞检测:主要用来检测当前帧当中的几何体是否发生了碰撞;
-
运动学(力学):主要用来推算下一帧当中几何体的位置。
碰撞检测的理论基础是我们高中所学的解析几何。由于复杂几何体的解析表达十分复杂甚至不存在,对于游戏这种软实时系统,一般是采用基本几何体替代场景当中的复杂几何体来进行碰撞检测的。这些基本几何体被称为碰撞模型,也称为碰撞包围盒。
被作为碰撞模型使用的基本模型一般有如下几个:
-
平面。平面在其延展方向是无限大的。比如方程
z = 1 就描述了一个平行于x,y轴的面积为∞的平面;
-
球体。球体可以通过球心坐标和半径方便地描述;
-
长方体(立方体)。可以通过其中心坐标,以及长、宽、高进行描述;
-
圆柱体。圆柱体可以通过其中心坐标,底面半径及高描述;
-
圆锥体。与圆柱类似;
-
胶囊体。是一个圆柱和两个半球体的复合。一般用来作为游戏当中人形角色的包围盒,相对圆柱形的平底,半球形的底面对于地面的坡度或者较小的起伏有较好的适应性。
-
三角形。虽然是平面图形,但是我们在之前已经知道了,游戏当中的几何体一般都是用一连串的三角形描述的。所以对于不能使用上面这些基本形的情况,我们依然是使用一个三角形组成的网格模型,只不过一般顶点数远小于渲染用模型的低模,来进行碰撞检测。
这些基本形状的碰撞检测的原理就是解析几何当中的求交点计算。当然这是有相当计算量的,所以在游戏当中会采用很多手段来减少计算量,比如使用AABB算法来避免不必要的求交计算。
而运动学则主要是根据牛顿三定律计算物体的运动和位置的变化。
比如下面这么一个场景:

那么我们可以为每个台球绑定一个球形的碰撞模型,而为球台绑定一个长方形的碰撞模型。至于球杆,我们可以绑定一个圆柱体。当我们按下键盘上某个按键的时候,我们给白色的母球加一个100牛顿的力,那么就得到如下这么一个仿真的结果:

上传视频封面
球之所以会从球台边界跑出去,是因为目前我们给球台绑定的还是一个非常基本的长方形形状,所以球台边界和球台里面是一样高的。就导致了这个现象。
我们可以通过绑定更为复杂的形状,或者在球台边界绑定额外的长方形,来解决这个问题。
关于游戏逻辑
我在前面的文章当中也提到了,游戏逻辑本身并不应该是游戏引擎的一部分。游戏引擎提供接口给游戏内容开发者编写游戏逻辑。
因此在本篇的例子当中,我对我们的项目代码结构进行了重组,新加了一个游戏逻辑基类:GameLogic,并且改变之前将引擎代码直接编译成为可执行文件的做法,取而代之的是将其编译成为库文件,最终与Game目录下的从GameLogic继承而来的具体游戏逻辑进行链接,得到最终的可执行文件。
当然,仅仅这样其实还是远远不够的。为了封装成为一个可以复用的游戏引擎,我们接下来至少还需要做如下的工作:
-
将头文件分离出Private和Public版本。Private是编译引擎的时候参照的,具备完整的信息,而Public版本只暴露二次开发时需要知道的接口。这样做可以进一步分离游戏引擎和游戏本身,方便游戏引擎的版本升级;
-
统一不同RHI的Shader编写方式,导入一个中间方式(如:Nvidia CG),并且编写相关的转换工具;
-
对Asset目录进行区分,将引擎所用资源和每个游戏所用资源分开;
另外一方面,我们的游戏引擎现在虽然有了基本的图形渲染、输入、物理仿真模块,但是从功能上还缺少诸如AI、动画、视频播放、音频音效等部分。
另外从开发调试以及优化的观点,我们还缺少驱动模块(用来调整不同模块的执行频率以及CPU多核心调度)、Profiling及调试模块等。
最后我们还需要有编辑器,方便对导入的资源进行简单的修改,以及场景结构的安排等。