Episode 06

发布时间 2023-03-22 21:08:46作者: Felix-Fu

Enemy Attacks——敌人攻击

Enemy

using System.Collections;
using UnityEngine;
using UnityEngine.AI;

[RequireComponent(typeof(NavMeshAgent))]

public class Enemy : LivingEnitity
{
    public enum State { Idle, Chasing, Attacking };//定义状态
    State currentState;

    NavMeshAgent pathfinder;
    Transform target;
    Material skinmaterial;

    Color originalColour;//初始颜色

    float attackDistanceThreshold = .5f;//攻击距离阈值
    float timeBetweenAttacks = 1;//攻击间隔时间

    float nextAttackTime;
    float myCollisionRadius;//enemy的碰撞半径
    float targetCollisionRadius;//player的碰撞半径

    protected override void Start()
    {
        base.Start();
        //追踪Player
        pathfinder = GetComponent<NavMeshAgent>();
        skinmaterial = GetComponent<Renderer>().material;
        originalColour = skinmaterial.color;

        currentState = State.Chasing;
        target = GameObject.FindGameObjectWithTag("Player").transform;

        myCollisionRadius = GetComponent<CapsuleCollider>().radius;
        targetCollisionRadius = GetComponent<CapsuleCollider>().radius;

        StartCoroutine(UpdatePath());
    }

    void Update()
    {
        if (Time.time > nextAttackTime)
        {
            float sqrDstToTarget = (target.position - transform.position).sqrMagnitude;
            //间隔距离小于攻击距离阈值
            if (sqrDstToTarget < Mathf.Pow(attackDistanceThreshold + myCollisionRadius + targetCollisionRadius, 2))
            {
                nextAttackTime = Time.time + timeBetweenAttacks;
                StartCoroutine(Attack());
            }
        }
    }

    IEnumerator Attack()
    {
        currentState = State.Attacking;
        pathfinder.enabled = false;//取消追逐

        Vector3 orginalPosition = transform.position;//初始位置
        Vector3 dirToTarget = (target.position - transform.position).normalized;//攻击方向
        Vector3 attackPosition = target.position - dirToTarget * myCollisionRadius;//攻击到的位置

        float attackSpeed = 3;
        float percent = 0;

        skinmaterial.color = Color.red;

        while (percent <= 1)
        {
            percent += Time.deltaTime * attackSpeed;
            float interpolation = (-Mathf.Pow(percent, 2) + percent) * 4;
            //线性位置变换,使攻击动作平滑
            transform.position = Vector3.Lerp(orginalPosition, attackPosition, interpolation);

            yield return null;
        }

        skinmaterial.color = originalColour;
        currentState = State.Chasing;
        pathfinder.enabled = true;
    }

    //不在Update里面使用SetDestination()防止计算开销过大
    IEnumerator UpdatePath()
    {
        float refreshRate = .25f;//0.25s计算一次
        while (target != null)
        {
            if (currentState == State.Chasing)
            {
                Vector3 dirToTarget = (target.position - transform.position).normalized;
                //追逐player间隔一点距离
                Vector3 targetPosition = target.position - dirToTarget * (myCollisionRadius + targetCollisionRadius + attackDistanceThreshold/2);
                //防止Die()后继续调用SetDestination()报错
                if (!dead)
                {
                    pathfinder.SetDestination(targetPosition);
                }
            }
            yield return new WaitForSeconds(refreshRate);
        }
    }
}

enum:枚举是直接在命名空间、类或结构中使用 enum 关键字定义的。所有常量名都可以在大括号内声明,并用逗号分隔。如果未将值分配给枚举成员,则编译器将在默认情况下为每个成员分配整数值(从零开始)。枚举的第一个成员将为0,并且每个连续枚举成员的值将增加1。可以为枚举成员分配不同的值。枚举成员的默认值的更改将自动按顺序向其他成员分配增量值。

Vector3 Lerp(Vector3 a, Vector3 b, float t):Linearly interpolates between two points.Interpolates between the points a and b by the interpolant t. The parameter t is clamped to the range [0, 1]. This is most commonly used to find a point some fraction of the way along a line between two endpoints (e.g. to move an object gradually between those points).The value returned equals a + (b - a) * t (which can also be written a * (1-t) + b*t).

代码逻辑

Chasing:Enemy追踪Player到离PlayerattackDistanceThreshold/2位置。(但在到达attackDistanceThreshold已经开始执行Attack()了)

Attacking:当Enemy与Player距离小于攻击距离阈值(attackDistanceThreshold)时,执行Attack(),以Lerp()插值方式移动Enemy进行攻击动画移动。

拓展: