文章目录
1.切换攻击目标
1.1 状态切换
在Enemy的代码中,敌人身上有一个attckList列表,它会收集当前敌人在它的检测范围之内出现的炸弹和敌人。这个列表的中的元素如果大于0,说明有可以攻击的对象,敌人切换到攻击状态,同样如果列表元素等于0,则切换到巡逻状态。
if (enemy.attackList.Count == 0)
enemy.TransitionToState(enemy.patrolState);
1.2 确定攻击目标
根据attackList列表中的物体来选择哪个物体距离敌人最近,敌人就选择这个物体当作攻击的目标。
当游戏进入攻击状态,至少需要先将敌人目标点置换到attackList列表的第一个元素,如果有多余的物体,就在Update里判断去寻找距离敌人的最近的元素。敌人和玩家都是在移动的,炸弹是不会移动的,很可能Player在移动时,炸弹成为了距离敌人最近的目标。
public override void OnUpdate(Enemy enemy)
{
// 没有对象可以进行攻击,转换为巡逻状态
if (enemy.attackList.Count == 0)
enemy.TransitionToState(enemy.patrolState);
if (enemy.attackList.Count > 1)
{
for (int i = 0; i < enemy.attackList.Count; i++)
{
if (Mathf.Abs(enemy.transform.position.x - enemy.attackList[i].position.x) <
Mathf.Abs(enemy.transform.position.x - enemy.targetPonit.position.x))
enemy.targetPonit = enemy.attackList[i];
}
}
enemy.MoveToTarget();
}
1.4 使用Tag确定目标的类型
分别为玩家和炸弹添加Player和Bomb标签,根据标签来查找物体的类型,从而确定攻击的方式。
攻击需要优先于移动判断,因为进入攻击的一瞬间,目标有可能已经在敌人的面前了。
// 根据标签类型选择攻击方式 在移动之前
if(enemy.targetPonit.CompareTag("Player"))
enemy.AttackAction();
if(enemy.targetPonit.CompareTag("Bomb"))
enemy.SkillAction();
2.攻击方式
2.1 添加的变量
攻击方式一定是敌人跑到Player身边进行攻击的方式,因此敌人到Player之间需要距离的判断,除了距离的判断之外,敌人不可能连续不断的进行攻击,同时也需要技能的冷却时间。
在这里的构思,普工和技能分别有不同的CD和距离。普攻可以近身,技能攻击可以距离稍微较远。后续也可以根据不同的技能来制定不同的距离和冷却CD。
[Header("Attack Settings")] public float attackRate; // 普攻击CD 技能CD
public float skillRate; // 技能CD
public float attackRange; // 攻击距离
public float skillRange; // 技能打击距离
private float nextAttack = 0; // 初始化为0,保证在游戏运行的开始就可以执行攻击
2.2 攻击方法
不管是技能攻击还是普攻,都需要先判断敌人和被攻击对象(player或者炸弹)之间的距离和skillRange,attackRange之间的大小。使用Vector2.Distance来判断两个点之间的距离,这里的判断不能使用x轴来判断,如果Player完全高于敌人在它的正上方,此时由于是根据x轴判断,他们之间的距离是极小的,敌人是要进行攻击,但这不符合需求。
如果距离满足就可以进行攻击,由于攻击是有CD时间的,所以在攻击之前还需要一层时间间隔的判断。
/// 攻击玩家
public void AttackAction()
{
// 先判断攻击距离
if (Vector2.Distance(transform.position, targetPonit.position) < attackRange)
{
// 释放之前先判断CD冷却
if (Time.time > nextAttack)
{
// 播放攻击动画
Debug.Log("普通攻击");
nextAttack = Time.time + attackRate;
}
}
}
/// 对炸弹进行技能攻击 虚方法,让子类可以重写
public virtual void SkillAction()
{
// 先判断攻击距离
if (Vector2.Distance(transform.position, targetPonit.position) < skillRange)
{
// 释放之前先判断CD冷却
if (Time.time > nextAttack)
{
// 播放攻击动画
nextAttack = Time.time + skillRate;
}
}
}
3.结合动画完成攻击效果
3.1 设置状态机
选中cucumber的精灵,在Animator窗口中进行修改。
为了实现普通攻击和技能攻击两个动画效果,需要在Parameter中添加两个触发器,分别是Attack和Skill
添加动画
添加Layers命名为AttackLayer
,并把它的权重修改为1,表示为完全覆盖上一层的动画。创建进入Layer的状态
CreateState–>Empty 命名为(AttackMode),这一状态不为它实装动画片段,用它来代表无论之前是哪一个Layer的动画都可以进入下一个动画的状态,通常使用的时创建一个空的物体方便管理,它代表上一个动画的状态- 攻击状态中的怪物除了跑动以外,还有攻击和skill两个动画,将这两个动画片段添加进状态机
AttackMode–>Run
使用state来控制。Equals=2,无退出时间,无修正的Duration
run–>attack
条件设置触发器attack,无退出时间,无修正的Duration
run–>skill
条件设置触发器skill,无退出时间,无修正的Duration
skill–>run / attack–>run
Exit time改成1,代表执行完动画,duration改为0,不需要添加任何条件
run–>AttackMode
指的是回到之前的状态,比如人物跳出攻击范围之后需要返回到patrolState。无退出时间,无修正的Duration,条件使用state Less=2,比2小就返回。
状态机如下图所示:
3.2 代码实现动画效果
在2.2小节中实现了敌人的攻击方法,在方法内添加播放动画的代码。
1. 添加攻击动画
anim.SetTrigger("attack");
2. 添加技能动画
anim.SetTrigger("skill");
3. 设置state
在进入攻击状态的时候就应该将控制动画状态的state设置为2,使它进入AttackLayer。
public override void EnterState(Enemy enemy)
{
enemy.animState = 2;
// 先把当前目标换到检测列表中的第一个变量
enemy.targetPonit = enemy.attackList[0];
}
如下图所示:
怪物攻击玩家是的动画和吹灭炸弹的动画已经加载实现,这里Player、Enemy和Bomb是在三个不同的层级。
4.设置Hit Point攻击点
4.1 创建打击点HitPoint并录制动画
普通攻击是所有怪物通用的技能,因为他们都需要对玩家产生攻击的效果。所以使用物理碰撞的方法来检测怪物的攻击点。
- 为SpriteSetting创建新的空物体取名为HitPoint
- 为攻击点添加BoxCollider2D,根据怪物攻击点动画来确定攻击点的位置
- 隐藏碰撞体在游戏一开始时不显示,播放攻击的时候才显示
使用动画录制的方法记录整个动画录制过程
1.点中Animation下的录制按钮,此时时间轴变成了红色,接下来对所有物体各种各样的操作都会被记录为一个关键帧的节点
2.对动画进行录制,(第三帧显示窗口并靠左,第五帧窗口靠右,第七帧隐藏窗口)
4.2 添加HitPoint脚本
1.把HitPoint设置成Trigger,使打击点碰撞到炸弹或者Player时不至于被顶开或者弹开
2.创建脚本HitPoint.cs
using UnityEngine;
public class HitPoint : MonoBehaviour
{
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
// TODO 玩家受到伤害
}
if (other.CompareTag("Bomb"))
{
// TODO 炸弹效果
}
}
}