这个博客搭好已经有半年多的时间了。调好模板和评论系统后,我却一直懒于写文章。在毕业入职后,一项任务的解决过程中,我真切感受到了书面整理的重要性。以此为契机,终于开始动笔了。
本文通过记录工作中一个问题的解决过程,分析笔记乃至写博客的意义。
Unity 与 CritterAI 的格式转换
问题描述
利用 Unity,可以实现游戏客户端的寻路功能。为了在服务器端也能进行寻路,我们使用了 CritterAI 插件。但是我们并不想使用 CritterAI 来 bake 数据,而只想利用它可以在服务器端进行处理的能力。因此需要把 Unity bake 的数据转换成 CritterAI 识别的数据格式。这就是我的任务。
为此,我需要把 Unity 原生的 NavMesh 导出为 CritterAI 的 Navmesh 。具体来说,是要把 UnityEngine.AI.NavMeshTriangulation
类转换成 org.critterai.nav.Navmesh
类。
然而,虽然 CritterAI 给了 Unity C# 的接口,但生成 Navmesh 的核心操作,全都是使用 C++ 编写的。最后得到的数据结构里有一项 char* data
,这是序列化后的数据,我需要找出这个 char*
是如何生成的。
解决过程
我一度深陷函数的调用之中,层层引用倒推,打开了二十个左右的文件,互相跳转,弄得整个人晕头转向。有的时候想回头找之前看过的函数,却不能很直接地随手翻到,让人心烦意乱。
终于,我冷静下来,并决定把每次跳转都给记录下来。令人难以置信的是,当我做了记录以后,真的是犹如拨开云雾见青天,一切都豁然开朗起来,是那么的顺利。
dtMeshTile -> data (char*)
<- dtNavMesh.addTile(data, ...)
<- dtnmBuildDTNavMeshFromRaw(data, ...)
<- UnsafeCreate(serializedMesh, ...)
我通过函数的引用,向上倒推,可以看到在 UnsafeCreate
这里陷入了死循环,因为这里的输入 serializedMesh
已经是生成好的 char*
了。
<- dtnmAddTile(rcnTileData -> data, ...)
<- Navmesh.AddTile(NavmeshTileData, ...)
<- NavmeshTileData.Create(NavmeshTileBuildData)
-> dtnmBuildTileData(rcnNavMeshCreateParams, ...)
-> dtCreateNavMeshData(dtNavMeshCreateParams, ...)
不过这没有关系,我往回退一层,走另一个引用,层层向上,就清楚的发现其实我只需要把参数传进 dtCreateNavMeshData(...)
就可以了。基于此,我把 NavMeshTriangulation
的数据与这里需要的参数做了一个转换,最后成功地实现了需求。
总结与思考
我承认,在晕头转向的过程中,我对 CritterAI 这个项目的理解也在不断加深。但是事实上,真正解决问题,还是依靠记笔记的方法。
人脑的容量是有限的,与其只把它当做一个记忆栈使用,不如把它用在更宝贵的思考上。当需要记忆的条目多起来后,不妨及时把需要记忆的部分从脑中转移到其他载体上。既解放了头脑,又使条目显得更加清晰有条理。
有的时候问题其实并没有那么复杂,只是当它在脑海里纠结时,可能让人甚至忘记了本来的目的。这时不如清晰地把问题描述出来。就像数学中的倒推法,为了得到结果,需要什么条件,一步步记录下来,明确自己的目标,不要在解决过程中迷失了方向。
写博客的意义
在这项工作中,我确实深刻体会到了书面整理的重要性。其实不只是在问题的解决上,在学习知识的过程中,书面整理也可以发挥很大的作用。
需要说明的是,单纯形式上的漂亮笔记是没有任何意义的,就好像只是收藏了很多资料就自以为已经把其中的内容全部掌握的那种满足感一样,是虚假的。
真正有用的笔记,一定要结合个人的思考。而博客,恰好就是这样一种笔记。它不是单纯的知识点的罗列,其中必然夹杂着作者本人对问题的理解。
知识经过一层梳理,会清晰很多。有的时候一个问题,大致想想似乎已经弄明白了整个过程,但是实际操作的时候总会遇到这样或那样的细节问题;有的时候一些知识看起来已经了解了,但是在向别人讲解的过程中才会发现自己说不清的地方。而写博客,对全面深刻的理解问题,有很大帮助。
综上,我认为,写博客的意义包括但不限于
- 博客的形式让人分享自己的理解,尝试把问题讲清楚,梳理了自己的思路。
- 把重要的信息记录下来,方便日后的查阅。
- 分享解决问题的过程,给予他人帮助,在互相交流中得到提高。
既然如此,那就从这篇文章开始吧。