《土豆荣耀》重构笔记(十七)随机生成可拾取道具
前言
  我们在前面的文章中,已经实现了随机生成足够多的怪物
的功能。为了能延长游戏时间,增加游戏的趣味性,我们需要随机生成一些可拾取的道具
,来恢复角色的血量
或者增加角色可释放的炸弹数
。可拾取道具
的需求如下:
可拾取道具
的需求:
可拾取道具
在设定上是空投补给
的,所以在落地之前,可拾取道具
将会在降落伞的作用下缓慢下降
,在落地之后,可拾取道具
上的降落伞缓慢消失
可拾取道具
一共有两种,分别是能恢复角色血量
的医疗箱
和增加角色可释放炸弹数
的装备箱
可拾取道具
在降落的过程
中也能被角色拾取装备箱
被导弹击中
会被直接引爆
制作降落伞动画
  根据可拾取道具的需求
,我们知道降落伞应该有两个动画,一个是降落时在空中飘动的动画
,另一个则是落地时缓慢消失的动画
。首先,我们在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
下的bigboom
FuseTime
: 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这个仓库到本地进行查看。