【Unity2DMobileGame_PirateBomb13】—— 完善敌人攻击的方法

236 阅读4分钟

文章目录

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

添加动画

  1. 添加Layers命名为AttackLayer,并把它的权重修改为1,表示为完全覆盖上一层的动画。
  2. 创建进入Layer的状态 CreateState–>Empty 命名为(AttackMode),这一状态不为它实装动画片段,用它来代表无论之前是哪一个Layer的动画都可以进入下一个动画的状态,通常使用的时创建一个空的物体方便管理,它代表上一个动画的状态
  3. 攻击状态中的怪物除了跑动以外,还有攻击和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 炸弹效果
        }
    }
}