经常玩2d横版游戏的朋友们相信对这样的效果一定对这种单向跳跃平台很熟悉:我希望我的角色可以通过跳跃跳上平台,然后在平台之上按下键盘的下键后从平台上落下。
那么想要实现这样的效果具体要怎么做呢?我们还是先将想要实现的效果进行一个逻辑上的拆分:
(1)单向平台,即平台只检测上方的碰撞而不检测下方;(2)角色在平台之上的情况下通过按下键盘的下键使得角色落下平台。
关于第一点,自己其实当时是苦恼了很久的。因为unity的Collider组件当时的自己只知道作为普通的碰撞器或是触发器来进行使用,一个很粗糙的想法是时刻比较当前平台与玩家的坐标,如果平台坐标在玩家之上就作为触发器,一旦坐标低于平台就要作为碰撞器。但这个想法实际上有很大的缺陷——咱们的平台肯定不止一个,会有很多很多物体,这么多物体实时获取咱们角色的坐标显然是一件重复且开销很大的事情。
就算可以用一个管理类专门来获取角色的坐标然后通知给所有的平台以解决前一个问题,角色的轴心与平台的轴心设置仍旧会很麻烦。为了方便与地面的检测我们习惯角色都是以脚底为轴心的,而平台的轴心为了实现我们的效果也最好是用顶部作为轴心,这样只要角色的脚底在平台之上了就能让平台作为碰撞器发挥作用。但实际上这么一比较仍旧会出问题:如果我的角色从平台正下方跳到平台上确实可能做到,那么如果我们的角色是从平台的左右侧试图跳上平台呢?极大可能会出现角色脚底正好挨到平台边缘,而此时平台作为碰撞器会将玩家摊开,然后又因为玩家坐标低了变成触发器。
一开始我也查找了不少博客,从一些大佬们的实现方法中了解到了Collider组件还有一种由效果器触发的方式,而unity提供的组件中恰好就有一个叫Platform Effector 2D的组件,这个组件就是专门用来让碰撞器实现2D平台效果的。
在这个组件中,我们只需要勾选使用单向,然后将表面弧度设置为90度(一般2D游戏的单向平台都是设置为90度),这样就实现了平台进行单向碰撞检测的效果。
解决了第一个问题,我们再来解决第二个问题:如何让角色处于平台之上后按下键盘的下键让角色落下平台呢?
想要让角色落下平台,无非就是暂时屏蔽角色与平台之间的碰撞,想要实现这一效果的途径理论上来说也有很多种,但自己初次实现这个效果的时候不仅想复杂了而且还并没有达到自己理想的效果。
一开始也是查阅了一些大佬的博客,有一位大佬的方法是通过代码调整上图Plaform Effector 2D中的碰撞器遮罩。碰撞器遮罩中实际上就是我们所有图层,默认为Everything,即检测所有图层的碰撞。
那位大佬的方法是为Platform专门写一个脚本,在脚本中获取到Player的图层序号,保存碰撞器遮罩中检测的所有序号,然后在Update中实时检测一旦按下了键盘的下键,就将碰撞器遮罩的图层中的Player图层暂时剔除掉,用一个计时器来短暂计时,超过这一时间后再将Player图层重新添加回去。
自己一开始确实采用的这一方法(毕竟看上去问题其实还是挺大但确实暂时实现了自己想要的效果),但这一方法属于是最笨的方法,效果也并不是那么的好:首先关于计时器,你需要自己调整好一个时间不能太短,否则可能角色还没落下平台结果就恢复了对Player图层的碰撞检测,导致角色刚落下一半就又回到了平台上,而且如果有多种角色每个角色身高胖瘦各不相同,这样的检测显然是不行的;其次,只要检测到下键就会执行一次碰撞器遮罩剔除与恢复,这无疑会浪费性能,可能很多时候我们的角色并没有在平台上,在进行其他各种行动时我们都有可能按下键,这显然会导致很多无用的操作。
后来经过一番思索与优化后,我发现了问题所在:我一直在想着怎么在给平台检测玩家单独写的这个脚本之中的代码进行优化检测,那么为什么不可以逆向一下,我在玩家的移动之中来检测平台呢?更何况玩家的移动方式之类的我全都是在玩家的脚本中检测的。
我的角色检测地面的方式是学习的麦扣老师《Robbie》教程中的Raycast射线检测方式(非常感谢麦扣老师持续产出的游戏项目课程,带给了我非常多的帮助!),那么既然都用射线检测地面了,为什么不可以用射线检测平台呢?我们只需要给平台加一个Tag就可以用同款的射线方式检测了。
if (Input.GetKeyDown(KeyCode.DownArrow))
{
RaycastHit2D leftCheck = Raycast(new Vector2(-footOffset, 0), Vector2.down, groundDistance, groundLayer);
RaycastHit2D rightCheck = Raycast(new Vector2(footOffset, 0), Vector2.down, groundDistance, groundLayer);
if (leftCheck.collider.CompareTag("Platform") || rightCheck.collider.CompareTag("Platform"))
{
coll.enabled = false;
}
}
protected void PhysicsCheck()
{
RaycastHit2D leftCheck = Raycast(new Vector2(-footOffset,0), Vector2.down, groundDistance, groundLayer);
RaycastHit2D rightCheck = Raycast(new Vector2(footOffset, 0), Vector2.down, groundDistance, groundLayer);
if(leftCheck || rightCheck)
{
if(coll.enabled == false && (leftCheck.collider.CompareTag("Ground") || rightCheck.collider.CompareTag("Ground")))
{
coll.enabled = true;
}
}
}
于是在玩家移动的代码中我额外加了上图部分,只要按下下键我就从左脚与右脚发出向下的射线进行检测,如果检测到了平台那么我就暂时关闭玩家的碰撞器,随后在玩家检测地面碰撞的时候如果检测到左脚或右脚接触地面,并且此时的碰撞器是关闭的,我们就将碰撞器重新开启即可。
至此,已经基本实现了我们想要的单向平台,并且玩家按下键盘下键后角色可以从平台上落下的效果。不过我的这种实现方式仍有待完善,目前已知的问题是偶尔不知道为什么会在按下下键的碰撞检测时报错(但实际上效果并未受到影响);然后目前必须得在玩家落下平台接触到地面后才会恢复碰撞体,如果存在一段连续向下且只有平台的关卡,那么这种方式就需要更改了,可能得结合一下前面的计时器思路手动加一个玩家碰撞体恢复的计时器。或者还可以在PhysicsCheck中关于恢复碰撞体的条件判断那里做一点文章,不过暂时也没想出来具体怎么更改。如果大佬们有更好的想法欢迎在评论区中进行讨论!