屎山探索之旅——记一次失败的2D物理引擎复刻经历
前言
去年大约 11 月份的时候,我在 B 站看到了一个视频。UP 主在视频中展现了一个完全由他自己基于 C++ 实现的 2D 刚体物理引擎。于是出于兴趣和春招需要,我着手去学习修改该项目。不过,今天我决定放弃。
项目介绍
首先,该项目是完全基于C++手搓的,包括 GUI 应用框架、音乐播放模块以及字体显示模块等。在此之上,作者实现了 2D 刚体间的交互模拟程序。由其发布的视频展示了具体效果:C++ 实现 2D 刚体模拟_哔哩哔哩_bilibili,而作者的作品实质上是由Let’s Make a Physics Engine - YouTube所启发。
详细来说,这个刚体模拟程序实现了刚体的这些功能:碰撞、连接件、电磁力等,以及刚体本身的拖拽旋转等效果。刚体还细分了矩形、三角、圆等功能。当然,对应的渲染也不能落下。总之,就是一个引擎该有的样子。
那么我为什么要放弃呢?
TT.JMLANG
如果我不说你大概理解不了这个标题的意思,实际上,我想表达的是"Title. Jia mi language",即“标题是‘加密通话’“的意思。
在物理引擎相关的部分:即存储刚体、碰撞、AABB 包围盒等类的地方,其实结构还相对清晰,毕竟,结合文件名、函数名以及代码,再考虑到实际的算法,也是可以理解的。这也是为什么我如今才选择放弃。但是,这一部分依然有很多让我头大的问题。例如
- 作者喜欢使用宏定义,甚至可以说到滥用的地步。例如,
#define p cur.p
,cur
是一个全局唯一的类,并且在所有需要它做参数传进去的地方,形参都是这个名字。这样就可以省略几个字母并符合人全局唯一的思维的。
但是,不消说,宏定义我们都知道有许多的问题:不能直观找到定义位置、不方便 debug、全局性……其中最重要的就是我难以找到成员定义在哪里,而且也禁止我定位到定义处。想象一下按 F 12 以后跳转到了宏定义的定义处,这对我找出 p 的类型或者其它的东西有什么帮助呢?更糟糕的是,像上面例举的这种宏定义没法消除,因为展开以后cur.p
中的p
也该被宏定义展开了。
如果你理解这种感受,那么我们现在再扩展一点:cur.p
以及其它所有宏定义塞在名为my_def.cpp
和utils.cpp
的几个文件中,并且这个宏定义互相嵌套,这意味着你只有按照指定的顺序进行修改才能消除这些宏定义。并且不能直接删定义而是用删引用的方式,这样才能保证不会在其它文件出错。 - 作者喜欢使用缩写,并且这些缩写不一定常规。例如
bgr
是 background 这样的缩写都算普通,更常见的情况是难以猜出其中的含义,例如使用pra
代表 parameter, 使用tl
表示topleft
(指左上角的坐标系原点)。这样的缩写配合宏函数则会带来更大的折磨。例如一个宏函数getv
,其含义实际是get(ValueTo)Vector
作用是从名为f
文件指针中获取一个wstring
(对的,其实并不是std::vector
)的sz
(size),并读取sz
个值。而且其中还使用了另一个宏函数来读取值。 - 使用
struct
而非class
:这意味着所有的成员都是public
的,不通过查找引用的方式,无法确定一个函数就是有没有被外界访问,也无法确定一个变量有没有被外界访问。
此外还有一些我不太喜欢但是相对没有那么让人痛苦的问题
- 使用
typedef
而不是enum class
的方式定义枚举,不够类型安全; - 使用更倾向于使用 if-else 而非 switch;
- 喜欢将多行短代码放到一行使其具有一定长度;
- 不使用一行仅定义一个成员变量的方式;
- 不采用大驼峰的方式命名函数;
- 大括号不换行;
- 大量隐含的类型转换;
还有一些小问题和架构的问题就不吐槽了(毕竟没太看懂没法下嘴)。
它回报比你要求的更多
仅仅上述问题并没有令我放弃,毕竟正如我前面也说了"在物理引擎相关的部分……其实结构还相对清晰……再考虑到实际的算法,也是可以理解的。"但是,这个项目给东西远远不止是一个物理引擎。他还包括一个应用框架。这个框架中,按钮、面板、字体显示、音乐演奏、还有一个指令面板,都是在上面的问题下写就的,而我对这些部分的理解绝没有物理引擎部分那么多。
更退一步的来说,即使我想要学习和理解这些内容,那么这个框架也是有不足的:
- 字体和音乐演奏都是自设的格式,难以编辑,也并不通用,不支持通常的字体文件和音乐文件;
- 面板采用的不是原生 UI,无法调整画框大小;
- 没有异步加载等功能;
对于作者来说,在这个过程在中可以学到一些创建 GUI 的方式。但是既然这不是我写的,那么是否通过这样的途径去学习理解就很值得打问号了。
总结和收获
总结就是代码规范确实是很重要的东西,因为不遵守代码规范等等原因,这样的一份开源代码对我而言学习成本太高,因此最后我选择了放弃。
代码规范、命名规范、域以及其它的东西……这些看上去很飘渺的东西在这一次学习之旅中以反面例子的形式,给我直观地展现了出来。
当然,另一方面,这也意味着代码规范又没那么重要:这是一个完全不尊重代码规范的野蛮生长的作品,但是它确实能跑。代码规范指导着我们如何写出更好的代码,可不能拘泥于规范,将规范视为圣经、雷池,不敢稍逾一步。前面吐槽了那么多作者写的代码的问题,但是三个月过去,作者又新发了若干视频,我却并没有收获一个可以写在简历上的项目,这就是差距了。
尽管如此,通过阅读项目,我对 2D 物理引擎有关的代码确实更熟悉了,也更了解 cpp 的一些新特性。例如智能指针等等,这些新特性是我在秋招时自学的,但是我并没将其实践起来,通过这个项目,我消除了这种实践上的陌生感。