代码复用:触碰使玩家死亡的敌人通用脚本
添加一个Script Machine组件,新建一个Kill Player脚本。我们希望这个脚本可以在任何需要实现“触碰使玩家死亡”的逻辑的对象上挂载。
在新建的脚本中删去On Start和On Update节点,然后添加On Collision Enter 2D节点。

On Collision Enter 2D事件的触发时机和之前提到的On Trigger Enter 2D类似,只不过Trigger版本由Trigger模式的Collider触发,而Collision 版本则可以由非Trigger模式的Collider触发,且可以提供的信息比Trigger版本要更多。

触碰使玩家死亡的逻辑脚本如下:

注意需要将玩家对象的Tag设置为Player才能使上面的脚本发挥作用。

玩家杀死敌人逻辑
在Player对象下的Ground Check对象的Script中添加如下逻辑:

此处还需将Opossum对象的Tag修改为Enemy。
在Opossum对象下添加State Machine组件并创建一个State Machine可视化脚本,并将自动创建的Start状态重新命名为巡逻(Patrolling)状态。

右击Patrolling结点,选择Make Transition,创建一个新的状态以及过渡,并将其重命名为Dead(死亡状态)。

双击中间黄色的图标即可编辑状态之间的过渡条件,在其中使用Custom Event事件结点来触发过渡:

接着回到State Machine脚本,双击Dead结点来编辑死亡状态下的逻辑:目前只需要在进入状态时显示死亡的动画,也就是将之前设置的Animator的死亡参数置为真即可。

调试&修BUG:杀死敌人时玩家自身死亡
到目前为止设置编写的逻辑可能导致玩家在尝试杀死敌人时自己也死亡了,这是由于Ground Check的触发优先级比玩家与敌人的Collision触发的优先级要更低,也就是说会优先执行敌人杀死玩家的逻辑,没等Ground Check的触发杀死敌人逻辑,玩家自己就先死亡了。
解决办法是在Collision触发事件中加以判断:当玩家从敌人上方与其碰撞时不执行杀死玩家的逻辑。碰撞的位置可以通过On Collision Enter事件的Contacts(接触点)属性来获得。

根据碰撞范围的形状,碰撞点可能不止一个,因此Contacts属性是点数组(包含了所有碰撞位置的点),我们只需要选择其中一个点(比如说第一个)应该就可以判断其碰撞的方向。
从Contacts属性连出,创建一个First Item节点,然后继续连出,搜索Contact Point,找到子菜单下的Get Normal(获取接触点表面法线)方法。

为了方便测试Get Normal节点输出的内容与我们玩家与其接触方向的关系,我们可以使用“Debug: Log”节点来让Unity输出指定的信息到Console窗口:

完整的调试流程逻辑如下:

经过测试可以得出:
- 当Player从右侧触碰敌人时,Get Normal节点输出的是(-1,0);
- 当Player从左侧触碰敌人时,Get Normal节点输出的是(1,0);
- 当Player从上方触碰敌人时,Get Normal节点输出的是(0,-1);
因此,我们只需要判断Get Normal节点输出的向量的Y分量小于0即可判定玩家是从上方接触敌人的。换句话说,触发玩家死亡的条件是:触碰玩家的同时接触点法线的Y分量大于-1(考虑到浮点精度的问题,实际上写成大于-0.5)。完整逻辑如下图所示:

杀死敌人后销毁敌人对象
为了让敌人在播放完被杀死的动画后被删除,类似于Player死亡时禁用Ground Check子对象的方法,我们可以在敌人死亡动画的最后一帧添加一个销毁动作。
在Animation窗口找到敌人死亡动画剪辑并定位到最后一帧,选择图中2处的按钮(Add Event,添加事件):

然后在Inspector窗口中选择触发的函数为TriggerAnimationEvent(AnimationEvent),并将String参数设置为Destroy。不需要设置这里的Object的选项,因为此项为空时默认为该对象本身。不设置具体的对象可以帮助我们方便地将这个逻辑移植到所有敌人对象中。

这里还可以选择TriggerUnityEvent来触发Unity内置的事件。如果使用代码脚本文件来编写逻辑时,自定义的函数也会显示在这个Function列表中可被调用。
在Opossum对象的State Machine中的Dead状态下,将原有的On Update和On Exit State事件删除,添加Named Animation Event(命名动画事件)事件节点:

并将Name参数设置为之前填写的Destroy,最后该事件来编写销毁Opossum对象的父对象Enemy Opossum对象即可。逻辑如下图所示。

修BUG:销毁敌人时玩家卡在空中
现在的逻辑会使得角色在敌人开始播放死亡动画时好像“卡”在空中一样,这很明显是由于播放动画时敌人的碰撞器仍然生效,像地面一样阻止了玩家的下落。
但是考虑到正是由于碰撞器的存在才使得敌人被地面挡住,如果只是简单地取消碰撞体,会让敌人在播放死亡动画时从地面掉下。
这里的解决思路是:让角色在从敌人上方“踩死”敌人的同时自己再弹跳一下,用弹跳的时间来等动画播放完;当角色再次落下时,死亡动画播放完被销毁就不会有这种卡顿感了。
在Ground Check的脚本中找到让敌人死亡的逻辑部分,在后面接一个触发Player对象(Ground Check的父级对象)弹跳的自定义事件Kill Enemy Jump:

然后在Player对象的State Machine下找到控制跳跃动作的逻辑,添加自定义事件Kill Enemy Jump入口:

最后,为了让角色能在踩到敌人又跳起的过程中切换到正确的起跳动画剪辑,我们还需要在Player的Animator中添加一条从下落状态到起跳状态的过渡,如下图所示:

本文介绍如何在游戏开发中实现玩家与敌人的触碰交互逻辑,包括触碰致死、敌人状态转换及动画销毁等内容。

被折叠的 条评论
为什么被折叠?



