《土豆荣耀》重构笔记(十一)实现发射导弹的功能
前言
  在实现了怪物攻击角色的功能之后,我们接下来需要实现玩家攻击怪物的功能。玩家攻击怪物的方式有发射导弹
和放置炸弹
。导弹在场景中会以恒定速率飞行,碰到物体时爆炸,若碰到怪物则对怪物造成伤害
。接下来,我们开始制作能伤害怪物导弹并实现角色发射导弹的功能。
调整图片的Pixels Per Unit
  在Hierarchy
创建一个名为Missile
的Empty GameObject
,然后将Assets\Sprites\Props
下的part_rocket
和part_flame
拖拽到Missile
成为其子物体(由于part_flame是图集,所以添加到场景后会创建一个名为frame1的GameObject)
。可以看到,此时part_rocket
和flame1
比角色和怪物大了很多,这是因为part_rocket
和part_flame
的像素太大了,我们需要对part_rocket
和part_flame
进行调整。
  我们可以在游戏场景中调整part_rocket
和flame1
的Scale
将其缩小,但是这样的话,我们每次往场景里添加part_rocket
和part_flame
时,都必须手动将其缩小一遍。为了减少不必要的修改,我们直接修改part_rocket
和part_flame
的Pixels Per Unit
。在Unity里,Pixels Per Unit
表示一个Unity3D单位对应该图片多少个图片像素
,我们增大图片的Pixels Per Unit
,那么将该图片添加到游戏场景中时,其大小也会随之变小。
  在Project
窗口分别选中part_rocket
和part_flame
,然后将它们的Pixels Per Unit
放大5
倍,也就是分别修改为500
和125
。修改完成后,点击右下角的Apply
,可以看到,游戏场景中的part_rocket
和part_flame
都缩小了。
创建导弹Prefab
  接下来,我们创建一个名为Weapons
的Sorting Layer
,然后调整Weapons
这一Sorting Layer
的位置,让其处于Foreground
和Character
之间。
Missile
子物体各属性的值:
part_rocket
:Position
: (0, 0, 0)Sorting Layer
: Weapons,Order In Layer
: 1
flame1
:Position
: (-1.5, 0, 0)Sorting Layer
: Weapons,Order In Layer
: 0
  修改完成之后,我们在Assets\Prefabs
下创建一个名为Weapons
的文件夹,并将Missile
从Hierarchy
窗口拖到Weapons
文件夹下,将Missile
做成Prefab。
让导弹飞起来
  为了导弹拥有速度这一物理属性,我们需要为Missile
添加Rigidbody2D
组件。为了避免导弹出现翻转的问题,我们需要勾选Rigidbody2D
组件的Freeze Rotation Z
。此外,因为导弹在飞行的过程中,不会受到重力影响下落,为了提高游戏性能
,我们可以将其Rigidbody2D
组件的Body Type
设置为Kinematic
。关于Rigidbody2D
组件各种Body Type
对应的作用,可以查阅Unity关于Rigidbody2D的说明。
  接着,为了让导弹知道是否撞上场景内的其他物体,我们为Missile
添加一个Capsule Collider2D
组件。因为导弹和其他物体碰撞之后,不会产生一系列的物理碰撞效果,所以为了提高游戏性能
,我们可以将勾选Capsule Collider 2D
组件的Is Trigger
属性。这样,当Missile
和其他物体发生碰撞时,物理引擎将不对Missile
和其他物体进行碰撞模拟。
  添加完Rigidbody2D
和Capsule Collider2D
后,我们在Assets\Scripts
下创建一个名为Weapons
的文件夹,然后在Weapons
文件夹下创建一个名为Missile
的C#脚本。最后我们将Missile.cs
添加到Missile
上,并打开Missile.cs
进行编辑。
1 |
|
代码说明:
Rigidbody2D.bodyType
:我们可以通过修改Rigidbody2D.bodyType
的值来动态修改Rigidbody2D
的类型Collider2D.isTrigger
:我们可以通过修改Collider2D.isTrigger
的值来动态设置Collider2D是否开启Trigger
属性OnTriggerEnter2D
:当我们开启Collider2D的Trigger
属性时,我们需要使用OnTriggerEnter
来知道Collider2D正和哪些物体发生接触。类似的可用于Trigger检测的函数还有OnTriggerStay
和OnTriggerExit
。
  编辑完Missile.cs
之后,运行游戏,可以看到导弹在场景内以恒定速率飞行,在接触到其他物体之后消失。
制作导弹爆炸动画
  导弹在接触到其他物体之后直接消失游戏体验不佳,因此我们还需要一个爆炸的动画。
  首先,在Assets\Animation
和Assets\Animator
下创建一个名为Weapons
的文件夹。接着,打开Assets\Sprites\FX
文件夹,可以看到part_explosion
的Sprite Mode
为Multiple
,被切割为四张图片。因为导弹爆炸动画是更换当前显示的图片实现的,四张图片分别代表爆炸动画的四个关键帧
,这里,我们采用一种新的制作帧动画的方式来快速制作帧动画
。
  在场景中新建一个名为MissileExplosion
的Empty GameObject,然后同时选中part_explosion
切割出来的四张图片,然后将它们拖拽到Hierarchy
窗口中MissileExplosion
上。因为我们同时给游戏场景里面添加了多个Sprite,Unity会认为我们想使用这些Sprite为MissileExplosion
制作帧动画,所以会询问我们新建的帧动画保存的位置。这里,我们将该帧动画命名为MissileExplosion.anim
并将其保存至Assets\Animation\Weapons
文件夹下,Unity会自动利用我们选择的四张图片帮我们创建好帧动画
。
  接着,我们在Project
窗口将Assets\Animation\Weapons
文件夹下的MissileExplosion.controller
移动至Assets\Animator\Weapons
文件夹下。因为我们是为空物体创建的动画,因此我们看不到动画的位置。为了便于观察,我们需要为MissileExplosion
添加一个初始Sprite,这里我们选择Assets\Sprites\FX
下part_explosion
切割出来的part_explosion_0
作为MissileExplosion
的初始Sprite。
  最后,我们还需要在导弹爆炸时
播放爆炸音效。在MissileExplosion
下添加AudioSource
组件,然后将Assets\Audio\FX
下的rocketExplode
拖拽到AudioClip
的赋值框处。
  添加完音效之后,我们将MissileExplosion
拖拽到Assets\Prefabs\Weapons
文件夹下做成Prefab。运行游戏,可以听到播放了一次导弹爆炸音效,且导弹爆炸动画在不断循环播放。
添加Animation Event
  我们不希望导弹爆炸动画一直在游戏场景中循环播放,我们希望导弹爆炸动画播放完之后,动画能自动被销毁。也就是说,我们希望导弹爆炸动画在播放至最后一帧时,能调用一个销毁自己的函数,我们可以使用Animation Event来完成这件事。
  首先,我们在Assets\Scripts\Utility
文件夹下创建一个名为Destroyer
的C#脚本后,编辑Destroyer.cs
。
1 |
|
  接着,把Destroyer.cs
添加到Hierarchy
窗口的MissileExplosion
上。我们打开Animation
窗口并选择MissileExplosion
,在最后一帧处右击鼠标选择Add Animation Event
并设置调用的函数为DestroyGameObject
。
  运行游戏,可以看到爆炸动画播放完之后,被自动销毁。最后,我们将MissileExplosion
产生的修改Apply至Prefab上,删除Hierarchy
窗口中的MissileExplosion
物体,并选中Assets\Prefabs\Weapons
下的Missile
,将MissileExplosion
拖拽到Explosion
赋值框。因为我们是在Prefab做修改,所以场景中所有Prefab的实例都会同步修改
。再次运行游戏,可以看到导弹接触到物体之后,已经能正常产生爆炸效果了。
制作导弹飞行特效
  为了避免让玩家决定导弹是在平移而不是飞行,我们需要在导弹飞行过程中加入火焰喷射动画
和烟雾拖尾粒子
。
  首先,我们使用跟创建导弹爆炸动画一样的方式创建导弹的飞行过程中的火焰喷射动画。在Project
窗口中同时选中flame1
和flame2
两张图片,并将它们拖动至Missile
的子物体flame1
上。接着,我们将新创建的动画命名为Flame.anim
并将其保存至Assets\Animation\Weapons
下,并将Assets\Animation\Weapons
下的flame1.controller
移动至Assets\Animator\Weapons
文件夹即可。此时,将导弹的飞行速度调小至2
,然后运行游戏,就可以看到导弹飞行的过程中在播放火焰喷射动画。
  除了创建火焰喷射动画,我们还需要使用粒子系统(Particle System)
来制作导弹飞行时的烟雾拖尾效果。
  首先,在Missile
下创建一个名为trail
的Empty GameObject,然后设置其Position
为(-0.5, 0, 0)
。接着,我们为trail
添加Particle System
组件,因为Particle System
组件里面还内嵌了很多包括Emission
、Render
等模块,我们不在这里一一细讲每个模块,感兴趣的同学可以参阅Particle System modules来了解例子系统各个模块的功能。
trail
物体的Particle System
组件设置:
- Main Module:
Start Lifetime
: 0.3Start Size(Random Between Two Constants)
: (0.3, 0.8)Start Rotation(Random Between Two Constants)
: (-30, 30)Start Color(Random Between Two Colors)
:((255, 255, 255, 255), (147, 142, 138, 255))Gravity Modefier
: 0.1Simulation Space
: WorldScaling Mode
: Shape
- Emission:
Rate Over Time
: 30
- Limit Velocity Over Lifetime:
Dampen
: 0.7
- Size over Lifetime:
Size(Random Between Two Constants)
: (1, 2)
- Rotation over Lifetime:
Angular Velocity(Random Between Two Constants)
: (-10, 10)
- Texture Sheet Animation
Animation
: Single RowRandow Row
: falseFrame over Time(Random Between Two Constants)
: (1, 3.9996)
- Render
Material
: SmokeMax Particle Size
: 5Sorting Layer
: Weapons
  修改完成后,将导弹的速度调回25
,运行游戏,可以看到导弹飞行的过程中出现了烟雾拖尾的效果。此时,我们将Missile
的修改Apply至它的Prefab上,保存修改,然后在Hierarchy
窗口中删除Missile
物体。需要注意的是,为了方便阐述,上面大部分属性都没有使用Curve
,为了做出效果更好的粒子系统,大家可以根据自己的喜好来调整各个Particle System modules
的属性。
发射导弹
  接下来,我们可以实现发射导弹的功能。在Assets\Scripts\Player
下创建一个名为PlayerAttack
的C#脚本。然后编辑PlayerAttack.cs
如下
1 |
|
此外,我们还需要编辑Missile.cs
,加入Flip函数
,并且添加根据朝向来设置速度的代码。
1 |
|
  编辑完成之后,我们在Player
下新建一个名为ShootingPoint
的Empty GameObject,然后将其Position
设置为(1.2, 0.25, 0)
。接着,我们将Missile
的Prefab拖拽至MissilePrefab
的赋值框处,将Player
的子物体ShootingPoint
拖拽至ShootingPoint
的赋值框处,将Assets\Audio\FX
下的bazooka
拖拽到ShootEffect
的赋值框处,最后点击Apply
将修改应用至Player
的Prefab,保存场景产生的修改。运行游戏,我们已经可以通过点击鼠标左键来发射导弹
了。
后言
  至此,我们已经完成了角色发射导弹的功能。由于篇幅限制,我们将会在下一篇文章里实现利用导弹对怪物造成伤害的功能。最后,本篇文章所做的修改,可以在PotatoGloryTutorial这个仓库的essay9
分支下看到,读者可以clone这个仓库到本地进行查看。