碰撞优化
当完成框架代码,游戏可以跑起来后,随之而来的就是优化问题了。优化是可以从各个方面下手的,而我想继续讨论性能上和操作上的一些优化处理。
从上一篇文章中可以知道,当做碰撞检测时,是一个碰撞体和场景内的所有符合条件的碰撞体进行位置比较,那毫无疑问,如果场景中的碰撞体数量增加,那检测的计算开销就也会相应的增加。因此核心思路就是,减少检测时参与计算碰撞体的数量。进一步试想一下,有些碰撞体距离目标碰撞体很远,在直觉上它们肯定与目标碰撞体想撞的,所以就可以不参与计算,直接排除。对于这一情况的处理,有一种常规的算法思路,叫空间划分。
空间划分是对游戏场景进行分区,而每一个区域会标记那些呆在自己内部的GameObject,当这些GameObject需要进行碰撞检测时,通常情况下它们只需要检测和自己在同一个区域的GameObject,或者相邻区域的GameObject,更远区域地就不再进行检测了。常见的空间划分有网格划分,四叉树,八叉树等等。
起先我是想采用网格划分的方法来处理,这个是一个相对简单且直观的空间划分算法,将场景划分为等大的网格,碰撞体只需要检测自己所在网格和相邻网格内的其他碰撞体即可。如下图所示,场景被绿色网格划分为了一块又一块,当角色和右边箱子碰撞时,实际上只需要检测自己本身所在的这个区域,或者再加上四周相邻的八个区域就行了,无需去知道其他区域里面有什么。
但实际上我没有在项目中使用该算法,这也是正是我想谈论的一点。在开发中,通用的算法确实可以帮助我们解决不少的问题,但有时候我们可以从项目本身的一些特点去做考虑,看看能不能找到一些更取巧的Trick呢?
一开始考虑空间划分的原因是场景内的碰撞体偏多,而其中百分之八十的都是静态墙体和地面(后面两者统称为墙体)。项目的墙体是由tilemap绘制的,由于摒弃了引擎自带的碰撞系统,所以导致墙体的每一块tile都是一个Collider,于是在一个场景中,可能有三四十个墙体Collider,两三个Kinematic,四五个Trigger。如果能够大幅减少墙体Collider的数量,那么在碰撞检测时,即便每次遍历全部的碰撞体,开销也是相当小的。由于tilemap中tile的排布是相当有序的,所以考虑去将相邻的tile进行融合,形成一个更大的矩形Collider。通过一番尝试和网上找资料,发现Love2D实现了该算法。具体代码如何实现就不在此讨论了,直接阅读源码还是很容易理解的。
上面两张图是融合tile的前后对比,很明显地看到融合后的碰撞体数量被很大减少了。可以说这是一个简单且实用的方案,无需添加之前说的空间划分的处理,尽量保持了项目的简洁。
手感优化
手感对于动作类游戏来说是至关重要的,GMTK的这个视频就讨论过《Celeste》中角色的操作手感是如何被打造的。这里面涉及到角色的启动时的加速度,停止时的减速度,下落时的重力改变等等参数。其中个人很喜欢的一个处理就是运动角色碰撞到墙体边缘时产生一定偏移,让角色能够“滑过”边缘。而自己也在项目里实现了向上方向的处理,实际效果如下。
想要实现这样的效果,核心点在于当角色与墙壁发生碰撞时,需要用算法进行适当的处理。首先我们需要弄清楚如何去定义边缘,当运动角色与头顶上的墙体相撞时,本质上是一个矩形和另一个矩形相交,而这两个矩形相交出矩形的长度就很重要。如下图所示,如果是第一种情况,运动角色和墙体相交在各自的一角,那么这个时候进行滑动是非常合理的,而且从画面表现上来看也很正常,但如果是第二种,实际上就是运动角色有大半部分被墙体挡住,自然就不应该去做滑动。
因此,在进行碰撞检测时,首先检测是否相交,如果相交了,在计算相交的长度,长度如果在一个设定的阈值里面,就可以让角色进行x方向的位移偏转,而这个偏转长度正好是相交的长度。还有一点就是,需要考虑运动角色的方向,如果运动角色的x方向,和碰撞墙体正好是相反的,即角色向左运动,碰到的墙体在右上角,那就可以进行滑动,反之则不行。
大功告成
当开始动笔写下这篇文章时,正是去年年末,疫情放开,自家小孩快出生的时候。小孩出生后又是一阵忙碌。自己本就是一个有些拖延的人,有时候坐在电脑前也没有动力打开文档继续完成,再加之还有装修房子,膝盖做手术这些林林总总的事情,导致文章一拖再拖,到现在已达半年之久。不过无论如何,我还是很庆幸自己完成了一开始设想的计划,希望以后我还能继续完成自己的计划,并且不拖延。
暂无关于此日志的评论。