Unity自定义角色控制器(一):碰撞检测
发表于2016-09-06
在使用Unity开发了多个项目之后,我得出了两个结论:第一个就是,对于所有对游戏开发感兴趣的人来讲,Unity都是一个非常好的引擎。第二个就是其内建的Character Controller非常糟糕。在关于自定义角色控制器的工作上,已经持续了数周,查找相关资料是非常困难的。自从我找不到可读资料起,我就打算自己写一些资料。计划分享一下自己所学到的以及所处理的问题。在实现方面,我会使用上面提到的Unity游戏引擎来开发。你可以在这里访问他们的网站,以及这里下载他们最新的版本。我实在很喜欢用Unity。它在处理很多底层问题的同时有给了开发者很多自由。而且它还有着非常活跃的社区,起到了很大的帮助。
不幸的是,之前也说了,Unity也带着世界上最糟糕的角色控制器。在与Unity对比之前,这里我阐明一下广义上的角色控制器是什么。其实角色控制器无非就是处理角色与游戏世界碰撞的一堆代码。它不像箱子、栅栏之类的可以有物理引擎统一处理,角色行为上的特殊性需要代码特殊处理。由于我们需要做碰撞检测,我们还是需要选择一个几何形体去表示我们的角色。大多数3D游戏都会选择胶囊体。
胶囊体的广泛使用有着很多原因,具体原因我们有机会再讨论。现在,为了让问题简单,我们先看看二维情况下的角色控制器。
你会看到我已经把坐标标记为z轴和y轴,而不是x轴和y轴。这是因为我将会从顶视图来观察这个三维世界。由于顶视图的缘故,那个蓝色圆形是胶囊体。绿色矩形是一堵墙。理想的状况是,角色无法穿过墙壁。因此当角色与墙体交叉时,我希望能够检测出碰撞的发生,然后正确地进行处理。之所以我们自己处理碰撞检测,也就是看看圆形是否与矩形发生了交叉,有两个原因。一个是Unity有相当多的资源(我们后面会讲)来处理这个问题,第二个就是这是一个讲解碰撞检测的好例子。我们将会让碰撞体计算出正确的位置从而得到合理的行为。
上面展示了我们的角色尝试去朝着墙面运动。为了处理这个问题,我们从角色位移的起点与位移的终点之间进行一次扫略体测试。这次测试的结果是有一面墙在我们的前方,并且返回到墙面的距离。那么我们就可以直接拿到这个距离,将角色按照方向移动所得碰撞距离,从而移动到墙体之前。(PS:Unity有着很多内建的功能可以做到这点,包括Rigidbody.SweepTest,Physics.SphereCast,Physics.CapsuleCast)。
但是,这并不完全是我们想要的效果。如果我们用了这种方法,角色在与物体稍稍碰撞之后,就再也没有进行任何移动了。问题在于它缺少了现实中的反弹与侧滑。
这个效果就更加合理一些。最初的扫略测试是在移动方向上进行的。当扫略测试接触到墙体之后,角色就直接移动过去,就像刚才那样。但是这次我们更进一步,让角色向上滑动来补足丢失的移动,这样使得可以沿着表面滑动。这是一个理想中的角色控制器该有的行为,但这不是实现它的最佳方法。首先,这么做效率不是很高:每次你想移动角色,你就需要执行这个函数。如果每帧只执行一次还好,但是假如因为某些原因这个函数要执行多次,那就消耗很大。其次,碰撞处理是依赖于角色移动的方向和距离。如果角色因为某种神奇的原因进入了墙体内部,角色是不会被推出的。实际上,我发现这个问题很让人头疼。
这是一个糟糕的情况。我们可以看到我们的英雄现在已经在墙体内部了。这种情况下,我们不应该再考虑怎么像之前一样处理碰撞,而是当做一种独立的情况来处理。我们不再关心玩家朝哪个方向移动或者移动了多远。而是我们应该考虑的是,这一刻他应该在哪个位置,这个位置是否存在问题。在上图中,我们可以看到玩家正在与墙体交叉(在墙体内部了),因此他当前位置存在问题,并且需要修正。自从我们不再将处理碰撞检测作为运动的反馈,我们是不知道之前的位置在哪或者移动了多远。而我们所知道的是,他目前卡在墙内,而我们需要将他从墙体内挪出来。但是应该挪到哪里呢?就像之前的例子一样,我们应该在刚接触墙面的时候就将其推出。这里我们有不少候选位置。
每个半透明黄色圆圈都指示了一个潜在的满足推出的位置。但是我们应该选择哪个呢?很简单,只要选择距离角色最近的墙面的最近点即可。
这里我们计算得出距离角色最近点位于右边。接着我们就可以将角色移动到该点,加上角色的半径(红色部分)。
到这里就结束了我们深入神秘的角色控制器的第一部分。下次,我将会开始谈谈如何在Unity中实现,以及一些角色控制器会用到的更加复杂的函数。
致谢与引用:
这里的大多数知识都来源于两个信息源:Unity论坛上的用户techmage以及逆向分析Unity角色控制器包的用户fholm。两个名字都不知道怎么发音。不管怎样,你都可以在这里下载,在RPGController目录下。这是一个很不错的项目,代码写得很好。很显然,他时做Unity顾问的,如果有谁需要这个服务的话。最后,如果谁有关于这个话题的很好的资源,不妨与我分享一下。