《土豆荣耀》重构笔记(十七)随机生成可拾取道具
前言
  我们在前面的文章中,已经实现了随机生成足够多的怪物的功能。为了能延长游戏时间,增加游戏的趣味性,我们需要随机生成一些可拾取的道具,来恢复角色的血量或者增加角色可释放的炸弹数。可拾取道具的需求如下:
可拾取道具的需求:
可拾取道具在设定上是空投补给的,所以在落地之前,可拾取道具将会在降落伞的作用下缓慢下降,在落地之后,可拾取道具上的降落伞缓慢消失可拾取道具一共有两种,分别是能恢复角色血量的医疗箱和增加角色可释放炸弹数的装备箱可拾取道具在降落的过程中也能被角色拾取装备箱被导弹击中会被直接引爆
制作降落伞动画
  根据可拾取道具的需求,我们知道降落伞应该有两个动画,一个是降落时在空中飘动的动画,另一个则是落地时缓慢消失的动画。首先,我们在Assets\Animation和Assets\Animator文件夹下新建一个名为Prop的文件夹,分别用来保存降落伞的动画文件和动画状态机文件。创建完毕之后,我们先来制作降落伞降落时在空中飘动的动画。
制作
降落伞降落时在空中飘动的动画的步骤:
- 在场景中创建一个名为
Parachute的Empty GameObject,然后将Assets\Sprites\Props文件夹下的prop_parachute图片拖拽至Parachute物体,使其成为Parachute物体的子物体 - 将
prop_parachute物体上SpriteRenderer组件的Sorting Layer设置为Foreground,Order In Layer设置为1 - 打开
Animation窗口,在Hierarchy窗口中选中Parachute物体,然后点击Create创建一个名为FloatDown.anim的动画,并将其保存在Assets\Animation\Prop文件夹下 - 将
Assets\Animation\Prop文件夹下的Parachute.controller文件移动至Assets\Animator\Prop文件夹下 - 点击
Animation窗口的红点按钮,开始为FloatDown.anim添加关键帧,添加的关键帧信息如下:FloatDown.anim的关键帧:第一帧:frame: 0Parachute:Rotaition: (0, 0, -12)
第二帧:frame: 30Parachute:Rotaition: (0, 0, 12)
第三帧:frame: 60Parachute:Rotaition: (0, 0, 12)
  制作完成之后,我们接着制作降落伞落地时缓慢消失的动画。
制作
降落伞落地时缓慢消失的动画的步骤:
- 为
Parachute物体添加Destroyer.cs脚本 - 在
Animation窗口选中FloatDown下拉框,然后点击Create New Clip创建一个名为Landing.anim的动画,并将其保存在Assets\Animation\Prop文件夹下 - 点击
Animation窗口的红点按钮,开始为Landing.anim添加关键帧,添加的关键帧信息如下:Landing.anim的关键帧:第一帧:frame: 0prop_parachute:Scale: (1, 1, 1)prop_parachute:GameObject.IsActive: trueprop_parachute:Sprite Renderer.Color: (1, 1, 1, 1)
第二帧:frame: 45prop_parachute:Sprite Renderer.Color: (1, 1, 1, 0)
第三帧:frame: 60prop_parachute:Scale: (0, 0, 1)prop_parachute:GameObject.IsActive: false
- 在
Landing.anim的最后一帧处添加一个Animation Event,选择DestroyGameObject方法作为这个Animation Event的调用方法
制作降落伞的其他工作
  制作完成降落伞的所有动画之后,我们接下来继续编辑控制降落伞动画的动画状态机,也就是Unity自动为我们创建的Parachute.controller文件。
编辑
Parachute.controller的步骤:
- 打开
Animator窗口,然后选择Hierarchy窗口中的Parachute物体 - 新建一个名为
Landing的Trigger变量 - 新建一个从
FloatDown到Landing的Transition,然后设置转移条件为Landing这一Trigger变量,并取消Has Exit Time的勾选
  降落伞的动画状态机编辑完成之后,我们还需要让降落伞能在重力的作用下缓慢下降。因此,我们需要为Parachute物体添加Rigidbody2D组件,并将Parachute物体上Rigidbody2D组件的Linear Grag属性设置为6,用来模拟降落伞下落时受到的空气阻力。
编写控制可拾取道具的脚本
  降落伞制作完毕之后,我们先根据可拾取道具的需求来编写控制可拾取道具的脚本。首先,我们在Assets\Scripts文件夹下创建一个名为Prop的文件夹,并在Assets\Scripts\Prop文件夹下分别创建一个名为MedicalBoxPickup和一个名为AmmunitionBoxPickup的C#脚本。创建完毕之后,我们先来编写MedicalBoxPickup.cs,用于控制医疗箱:
1 | |
代码说明:
  这里,我们使用Trigger来识别当前降落伞触碰到哪些物体,因此我们需要新建一个名为Ground的Tag,并将Foreground物体下的子物体env_PlatformBridge、env_PlatformBridge (1)、env_PlatformTop、env_PlatformTop (1)以及env_PlatformUfo的Tag设置为Ground。
  接着,我们需要改写PlayerHealth.cs,编辑PlayerHealth.cs如下:
1 | |
代码说明:
  这里,我们新增了一个变量m_IsDead来检测角色是否死亡,然后增加一个用于恢复角色血量的Heal函数。最后,我们在UpdateHealthBar内限制了角色的血量。
  编写MedicalBoxPickup.cs之后,我们继续编写AmmunitionBoxPickup.cs来控制弹药箱:
1 | |
  最后,我们在PlayerAttack.cs中增加AddBomb函数:
1 | |
制作可拾取道具
  编写完控制可拾取道具的脚本之后,我们开始制作可拾取道具。首先,我们需要将Assets\Sprites\Props文件夹下的prop_crate_ammo和prop_crate_health图片的Pixel Per Unit都调整为200创建完之后,我们复制场景中的Parachute物体,并将Parachute物体重命名为MedicalBoxPickup,将复制得到的Parachute(1)物体重名为AmmunitionBoxPickup。接着,我们在Assets\Prefabs文件夹下新建一个名为Props的文件夹,用于保存可拾取道具的Prefab。最后,可拾取道具不应该跟怪物产生交互,因此我们还需要新建一个名为Pickup的Layer,并在Layer Collision Matrix中取消Enemy-Pickup和Setting-Pickup这两项的勾选:
  做完这些准备工作之后,我们先来制作医疗箱这一可拾取道具:
医疗箱制作步骤如下:
- 将
Assets\Sprites\Props文件夹下的prop_crate_health拖拽至MedicalBoxPickup物体成为其子物体 - 修改
prop_crate_health物体的Position为(0.2, -0.7, 0) - 将
prop_crate_health物体上SpriteRenderer组件的Sorting Layer设置为Foreground,Order In Layer设置为0 - 为
prop_crate_health物体添加MedicalBoxPickup.cs,设置Heal Amount为50,Pickup Effect为Assets\Audio\FX下的healthPickup - 将
prop_crate_health物体上BoxCollider2D组件的Offset设置为(-0.2, 0),Size设置为(1.8, 1.4) - 将
prop_crate_health物体上CircleCollider2D组件的Offset设置为(-0.2, 0),Radius设置为1.4,并勾选Is Trigger - 将
MedicalBoxPickup物体的Layer设置为Pickup,并选择Yes, change children将子物体的Layer也设置为Pickup - 将
MedicalBoxPickup物体拖拽至Assets\Prefabs\Props文件夹下,将其制作为Prefab,并删除场景中的MedicalBoxPickup物体
  医疗箱制作完毕之后,我们继续制作弹药箱:
弹药箱制作步骤如下:
- 将
Assets\Sprites\Props文件夹下的prop_crate_ammo拖拽至AmmunitionBoxPickup物体成为其子物体 - 修改
prop_crate_ammo物体的Position为(-0.1, -0.65, 0) - 将
prop_crate_ammo物体上SpriteRenderer组件的Sorting Layer设置为Foreground,Order In Layer设置为0 - 为
prop_crate_ammo物体添加AmmunitionBoxPickup.cs,设置Bomb Amount为2,Pickup Effect为Assets\Audio\FX下的bombCollect - 将
prop_crate_ammo物体上BoxCollider2D组件的Offset设置为(0, -0.05),Size设置为(1.84, 1.25) - 将
prop_crate_ammo物体上CircleCollider2D组件的Offset设置为(0, 0),Radius设置为1.4,并勾选Is Trigger - 将
AmmunitionBoxPickup物体的Layer设置为Pickup,并选择Yes, change children将子物体的Layer也设置为Pickup - 新建一个名为
AmmunitionBox的Tag,将prop_crate_ammo物体的Tag设置为AmmunitionBox - 为
prop_crate_ammo物体添加Bomb.cs,并修改其属性如下:Bomb.cs的属性DamageAmount: 50BombRadius: 10BombForce: 800BoomClip:Assets\Audio\FX下的bigboomFuseTime: 1.5fFuseClip:Assets\Audio\FX下的bombfuse
- 将
AmmunitionBoxPickup物体拖拽至Assets\Prefabs\Props文件夹下,将其制作为Prefab,并删除场景中的AmmunitionBoxPickup物体
  弹药箱制作完毕之后,为了能让弹药箱被导弹击中之后直接引爆,我们需要修改Missile.cs的OnTriggerEnter2D函数:
1 | |
  最后,如果弹药箱在降落过程中被引爆,那么我们需要直接销毁整个降落伞,所以我们还需要修改Bomb.cs的Explode函数
1 | |
生成可拾取道具
  最后,我们还需要使用Generator来生成我们制作好的可交互道具。首先,我们在Hierarchy窗口的Generator物体下创建一个名为PickupGenerators的Empty GameObject。接着,我们在PickupGenerators物体下创建一个名为PickupGenerator的Empty GameObject,并为PickupGenerator物体添加Generator.cs组件。
PickupGenerator的设置如下:
Position: (-10, 20, 0)Generate Delay: 5Generate Interval: 10Prefab Orientation: NonePrefabs:Element0:Assets\Prefabs\Props文件夹下的AmmunitionBoxPickup物体的PrefabElement1:Assets\Prefabs\Props文件夹下的MedicalBoxPickup物体的Prefab
  编辑完之后,我们将PickupGenerator物体拖拽至Assets\Prefabs\Generators文件夹下将其制作为Prefab。然后复制场景中已经成为实例对象的PickupGenerator物体得到PickupGenerator (1)物体,并将PickupGenerator (1)物体的Position修改为(10, 20, 0)。运行游戏,我们可以看到此时游戏中已经能不断随机生成弹药箱和医疗箱。
后言
  至此,我们已经完成了随机生成可交互道具的功能。最后,本篇文章所做的修改,可以在PotatoGloryTutorial这个仓库的essay15分支下看到,读者可以clone这个仓库到本地进行查看。
