石头人的击退技能加上远程攻击,让近身攻击变得困难。这节内容增加和石头的交互:我们通过点击石头人扔的石头,让其砸向石头人,进而造成反击。
1. 实现石头对角色造成伤害
石头既可以对角色造成伤害,也可以对石头人造成伤害。此处我们首先定义石头当前的状态:
- public enum RockState
- {
- HitPlayer,
- HitEnemy,
- HitNothing
- }
我们通过碰撞来判断是否产生伤害。因为之前石头上增加了刚体组件,所以可以继承 OnCollisionEnter 函数。我们的主要实现逻辑都在 OnCollisionEnter() 中完成。
如代码清单 1 所示,因为石头一开始跑出来是要攻击角色的,所以首先将石头的状态设置为攻击角色(第 19 行)。OnCollisionEnter() 中首先完成攻击角色的功能。因为攻击角色状态下,石头还会碰到地面等物体,所以需要判断是否碰到了角色,这边通过标签判断(第 43 行)。
石头碰撞到角色后,会将角色击退,实现是兽人击退的逻辑是一样的(第 45 至 47 行),并产生眩晕(第 49 行)。伤害产生逻辑在 TakeDamage() 函数中(第 59 至 64 行),更新人物角色的生命值数据。最后将石头的状态更新为 HitNothing 状态。
攻击角色的功能实现好了之后,我们可以将人物的血量调成 1 进行测试。可以看到石头碰撞到角色后,角色被击退,被死亡(伤害逻辑正确)。
2. 实现石头对石头人造成伤害
如代码清单 2 所示,和对角色造成伤害的逻辑一样,如果碰撞到了石头人则对石头人造成伤害。此处是不想石头对其他敌人造成伤害,特定判断需要是石头人。判断石头人的方式是寻找物体上是否有石头人的代码组件(第 22 行)。
造成伤害的逻辑写好了,还剩下点击石头进行交互的功能。我们把点击交互的功能复用之前点击攻击的功能,石头交互的逻辑放在之前 PlayerController.Hit() 的动画事件函数中。
2.1 添加可攻击物体标签
为了判断攻击的物体是敌人还是石头,如图 1 所示,我们为石头指定一个新创建的 Attackable 标签。
添加好新标签之后,我们回到 PlayerController 中。如代码清单 3 所示,我们改变 Hit() 函数中逻辑,如果当前攻击的对象是可攻击物体,则调用 HitAttackableObject();如果是敌人,则还是之前的伤害计算逻辑。
HitAttackableObject() 中实现可交互物体的逻辑。如果是石头,并且石头还需要是无攻击状态的,则改变它的状态为攻击敌人的状态。同时朝人物面向方向“投掷”石头。
2.2 鼠标逻辑
目前 Attackable 的标签鼠标还不能响应,我们需要回到 MouseManager 中添加。如代码清单 4 所示,Attackable 的识别响应逻辑和 Enemy 的一样。
3. 改变到 HitNothing 状态
现在只有石头击中角色时,才会变成 HitNothing 状态。如果角色通过走位躲掉石头,就会无法反击石头人。
没有击中角色状态下,我们判断石头是否达到静止,如果静止了则将状态置为 HitNothing。因为是物理信息,所以我们需要放在 FixedUpdate() 中进行实时检测,而不是 Update() 中。
如代码清单 5 所示,我们判断速度的平方值,如果小于 1,则切换状态为 HitNothing(第 15 到 18 行)。注意刚开始时石头的速度是零,我们需要将其设置为单位向量(长度为 1),否则会帧检测时直接变为 HitNothing 状态。
不要忘了在对石头攻击加力的时候,也要先设置初始速度,否则也会切换到 HitNothing 状态。
- public class PlayerController : MonoBehaviour
- {
- void HitAttackableObject()
- {
- RockController rock = m_enemyObj.GetComponent<RockController>();
- if (rock != null &&
- rock.m_rockState == RockController.RockState.HitNothing)
- {
- Rigidbody rigidbody = m_enemyObj.GetComponent<Rigidbody>();
- rigidbody.velocity = Vector3.one;
- rock.m_rockState = RockController.RockState.HitEnemy;
- rigidbody.AddForce(transform.forward * 20, ForceMode.Impulse);
- }
- }
- }
石头达到静止状态的时候,角色会穿模石头。需要为角色物体也设置 Rigidbody 组件,并勾选 Is Kinematic 选项。
4. 实现石头破碎效果
我们使用粒子系统实现石头破碎的效果。在 Hierarchy 窗体中右击选择 Effects - Particle System 创建一个粒子系统对象。
如图 2 所示,粒子系统可设置的属性非常多,我们来一点点设置以满意我们的要求:
1. 取消勾选 Looping,不需要循环播放。
2. Duration 可以设置播放时长,这边设置为 0.2。
3. Max Particles 和 Emission - Rate over Time 决定粒子的数量。这边将 Rate over Time 设置为 40。
4. Gravity Modifier 设置为 1,指定重力大小,让粒子不往上飘。
5. 我们想产生的粒子也能发生碰撞效果,设置 Collision - Type 为 World;Collides With 保持为 Everything。
6. 设置 Start Lifetime 为 1,Start Speed 为 4,让粒子的消亡的快一点。
7. 需要指定粒子的形状是石头。指定 Renderer - Meshes 为 RockMesh。Renderer - Material 指定为 PolyartStandard。
8. 默认的石头太大了。我们将 Start Size 设置为 0.1 到 0.6 这个范围。
9. 想要石头炸裂时会旋转。设置 Rotation over Lifetime - Angular Velocity 为 90;Rotation by Speed - Angular Velocity 为 100。
以上我们的破碎粒子特效已经制作好了。我们将其拖拽到 Assets 窗体中设置为预制体,并删除原先 Hierarchy 窗体中的对象。
如代码清单 6 所示,我们创建一个 GameObject 用于拖拽指定上述创建好的粒子系统(第 3 行)。在石头击中石头人时实例化粒子系统(第 13 行)。因为默认勾选了 Play On Awake,所以实例化时会自动播放。
至此石头反击相关的内容就都实现完毕了。最终的效果如图 3 所示。