前言
本篇将会完成小游戏的跳跃动作以及场景、地面、障碍物的制作。
PS. 为了节省时间以后的文章可能不会贴代码,只讲实现原理。
角色跳跃
按下键盘的 w 键或者 上方向键可以让角色进行单段跳跃。
动作实现
因为我不是用物理组件来实现的,所以这里需要自己手动写跳跃的逻辑。
当玩家按下跳跃键时,给与角色一个 y 轴向上的速度即可,与控制角色移动的原理一样,演示效果:
角色“原地升天”!
重力下降
这样直接让角色升天可不行,必须受到重力让其下坠。
如果现实世界一样,当一个人跳起来的时候,受到重力影响 y 轴速度会不断降低,直到掉回地板,有地板的支撑才不会继续下坠。
所以只要让角色的 y 速度时刻都在减少即可实现重力效果,为了方便演示看到效果,初始 y 速度设置为 0,不然角色会直接掉下去:
没有地板的支撑,所以会掉出屏幕外面。
地板
角色站在地板可以认为是碰撞事件,即角色和地板产生了碰撞(接触)。
给角色加上刚体组件和碰撞组件,同时地板也要加上碰撞组件,并且设置为触发器。
当角色与地板发生碰撞的时候,就给与角色一个“站在地板上”的状态。
当角色处于「站在地板上」状态的时候,不会受到重力影响而下坠,这样就可以让角色看起来像“站在地板上”了!
演示效果:
角色从空中掉到地板的时候,虽然不再继续下降,但是又出现新的问题——人物“陷入”地板了。
出现这种情况是因为游戏是按帧渲染的,当一个物体处于高速运动状态的时候,一帧的位移很难正好处于碰撞物体的表面。
尽管我们在 OnTriggerEnter2D
触发器事件立即让碰撞物停止行动了,但还是无法避免这种情况。
地板穿透问题
假设有一面墙壁,朝着墙壁发射子弹,不考虑物理效果,让子弹撞到墙的时候立即停止行动,在游戏里会出现下面这种情况:
这是因为子弹飞行的速度太快了,导致每一帧的位置变化非常大,刚好停在墙壁表面的情况几乎不可能出现。
如上图所示,虽然保证每一帧的间隔时间相同,但是当子弹与墙壁碰撞的时候,子弹已经移动到墙壁里了。
这个时候让子弹停止行动也无法解决陷入的问题。
不过这个问题很容易解决,只要让角色与地板发生碰撞的时候,调整角色的坐标刚好站在地板上不就可以了!?
没错,改进原来的代码,让角色和地板接触的时候,改变主角的 y 坐标为地板上方,演示如下:
可以看到,虽然调整 y 坐标以后角色确实没有陷入地板,但问题也很明显,落到地板的一刹那出现了一个“幻影”。
因此,这并非最好的解决方法。
解决穿透问题
高速运动的刚体穿透碰撞体是一件很常见的问题,网上的教程一般都是类似上面这样调整坐标或者回到前一帧的位置,但这样就会造成碰撞“抖动”现象,对游戏的操作体验和观感都不好。
有一个比较靠谱的方案就是「射线检测法」。
从枪口发射出来的子弹,同时会向前方发射一条射线用来探知前方的碰撞体。
当射线探知的前方有一面墙壁的时候,就可以提前告诉子弹墙壁表面的坐标,当子弹运动到这个坐标的时候,就不再继续前进了。
这样一来,子弹刚好接触到墙壁表面的位置就停下来了,而且因为接触到墙壁也与墙壁产生了碰撞事件,从而可以在碰撞回调方法进行逻辑处理。
但是角色跟子弹不一样,如果要用射线检测法,就要从上下左右 4 个方向进行检测,这样比较麻烦。
相反,从射线检测法得到灵感,我想到一个「探知领域」法:
角色的周围存在一个「探知领域」,这个领域是一个比角色本身的碰撞盒子稍微大一点的碰撞检测器,因为角色跳跃的时候,并不知道自己的落地点在哪,有可能跳到一个障碍物的上面,也可能落在地板上面,所以需要一个用来检测周围环境的探知领域,由于探知领域的面积比角色本身的碰撞体大,所以探知区域先与地板、障碍物触发碰撞事件,因此就可以提前通知角色的落地点了。
理论搞清楚了,就开始动手解决问题!
创建一个空白对象,挂在 Player 节点下面,然后给对象加上 Box Collider 2D
碰撞体组件,宽高设置为比角色稍微大一点。
因为挂在 Player 节点,父节点本身就有刚体组件,所以探知领域不需要再添加刚体。
新建一个 Tag 命名为 Detect
,赋予探知领域此 Tag,用来区分碰撞检测:
……未完待续。
遇到的问题
我发现如果自己来写这跳跃系统的话,工作量实在太大了。
折腾了一个下午,居然都在解决陷入地板的问题,但是如果直接使用 unity 的物理系统就不需要考虑这个问题。
然后又担心物理系统会让小游戏变得太“僵硬”,于是就去看了一些其他平台跳跃游戏的示例,结果发现他们全部都是基于物理系统的。
甚至包括蔚蓝这种操作手感顶尖的跳跃游戏,也可以用物理效果做出来。
最后我决定把控制角色的脚本用物理系统重写一遍,下文开始新篇章。