第二部分:敌人控制器——杂项功能

       包含以下内容:

1:随机生成巡逻点   2:判断是否发现敌人  3:巡逻等待  4:敌人死亡   5:敌人UI标记

        敌人的逻辑在状态机具体的状态中实现。由于对状态进行了复用,因此在具体状态中声明的变量要上升到敌人类EnemyController中(防止多个敌人状态机对同一ScriptObject状态类进行使用和赋值造成的冲突问题)。

        以下是一些敌人中杂项内容:

1:随机生成一个巡逻点  涉及到圆内随机生成一点

要点:生成两个0-1的随机数,一个作为角度a,一个作为距离圆心的距离r

           a*=2π       r要先取根号 再乘以巡逻半径(确保点在圆内是均匀分布的)

           最后根据公式 X=originX + r * cos a  Z=originZ + r * sin a       Y 高度值默认保持不变

    public Vector3 GetRandomPartolPoint()
    {
        float randomAngle = Random.value;
        float randomRadius = Random.value;
        randomRadius = Mathf.Sqrt(randomRadius); 
        randomAngle *= 2 * Mathf.PI;
        randomRadius *= patrolRadius;
        return new Vector3(orignalPosition.x + randomRadius * Mathf.Cos(randomAngle), transform.position.y, orignalPosition.z + randomRadius * Mathf.Sin(randomAngle));
    }

2:判断是否发现敌人

要点:使用 OverlapSphereNonAlloc 判断指定范围内有无Layer为 Player的物体,如果没有,则直接返回。 否则,还需要判断敌人和人物之间有无障碍物(可以使用射线检测),最后还需要判断人物是否是面向敌人的。

    private bool CanFindPlayer()
    {
        int count = Physics.OverlapSphereNonAlloc(transform.position, findPlayerRadius, playerCollider, playerLayer);
        if (count == 0) return false;
        //从敌人位置向目标位置发射一条射线,判断中间有无障碍物
        float distance = Vector3.Distance(transform.position, playerCollider[0].transform.position);
        if (!Physics.Raycast(transform.position + transform.up * 0.5f, transform.forward.normalized,distance,barrierLayer))
        {
            //判断敌人是否是朝向角色的
            return transform.IsFacingTarget(player.transform);
        }
        return false;
    }

静态方法:使用人物forward向量以及人物指向敌人的向量 进行点击Dot运算(记得两个向量取normalized变成单位向量),判断的值可以自行决定。(点积计算两个单位向量角度的cos值)

 /// <summary>
    /// 
    /// </summary>
    /// <param name="transform"></param>
    /// <param name="目标的transform"></param>
    /// <returns></returns>
    public static bool IsFacingTarget(this Transform transform, Transform targetTransform)
    {
        if (Vector3.Dot(transform.forward, (targetTransform.position - transform.position).normalized) > 0.35f)
            return true;
        return false;
    }

3:巡逻等待时间的协程逻辑 :等待时间结束后进行转向,转向完成后才能移动。

    //巡逻等待时间的 协程
    public IEnumerator WaitPatrolTime(float waitTime, Vector3 targetPos)
    {
        IsWait = true;
        curSpeed = 0;
        while (waitTime > 0)
        {
            waitTime -= Time.deltaTime;
            yield return null;
        }
        yield return RotateToTatgetPos(targetPos);
        IsWait = false;
    }

4:敌人死亡后的事件

(1)第一个是处理敌人死亡后的事件(主要是如果正在被锁定,则移除玩家对敌人的锁定)。

(2)第二个是处理处决的标志(敌人死亡后,如果有处决标志的话,应该消失)

(3)在Death动画结束后才实际消除物体。

    public void AfterDeathAnimation()
    {
        //TODO:使用对象池管理敌人
        GlobalEvent.CallOnEnemyDeath(this);
        GlobalEvent.CallEnemyExitWeakState(this);
        Destroy(transform.gameObject);
    }

5:敌人UI标记(聚焦标记 + 处决标记)

要点:使用 overlay 的Canvas实现 (使用World Space模式的话会被敌人遮挡)

           聚焦标记只有一个,用全局事件的方式来响应玩家的聚焦功能。

           处决标记可能会出现多个,使用字典<敌人,处决标志>来记录所有的处决标志,同样使用全局事件的方式,在敌人进入虚弱状态时触发,在敌人退出虚弱状态时移除。

            标志要跟着敌人走,因此在LateUpdate中更新所有标志的位置。

(使用Camera.main.WorldToScreenPoint(currentLockedEnemy.transform.position)将世界坐标点转化为屏幕坐标点)

public class EnemyPointUI : MonoBehaviour
{
    [SerializeField] private GameObject executedPointPrefab;

    [SerializeField] private Image FocusPointImage;

    [SerializeField] private Transform pointParent;

    private Dictionary<EnemyController, GameObject> executedPoints = new();

    private EnemyController currentLockedEnemy;

    private void OnEnable()
    {
        GlobalEvent.EnemyEnterWeakState += CreateExecutedPoints;
        GlobalEvent.EnemyExitWeakState += DeleteExecutedPoints;

        GlobalEvent.EnterFocusOnEnemy += OnEnterFocusOnEnemy;
        GlobalEvent.ExitFocusOnEnemy += OnExitFocusOnEnemy;

    }

    private void OnDisable()
    {
        GlobalEvent.EnemyEnterWeakState -= CreateExecutedPoints;
        GlobalEvent.EnemyExitWeakState -= DeleteExecutedPoints;

        GlobalEvent.EnterFocusOnEnemy -= OnEnterFocusOnEnemy;
        GlobalEvent.ExitFocusOnEnemy -= OnExitFocusOnEnemy;
    }

    private void LateUpdate()
    {
        if (currentLockedEnemy != null)
        {
            FocusPointImage.transform.position = Camera.main.WorldToScreenPoint(currentLockedEnemy.transform.position);
        }
        foreach(var redPoint in executedPoints)
        {
            redPoint.Value.transform.position = Camera.main.WorldToScreenPoint(redPoint.Key.transform.position);
        }
    }

    //锁定敌人的事件
    public void OnEnterFocusOnEnemy(EnemyController enemy)
    {
        currentLockedEnemy = enemy;
        FocusPointImage.transform.position = Camera.main.WorldToScreenPoint(currentLockedEnemy.transform.position);
        FocusPointImage.gameObject.SetActive(true);
    }

    //取消锁定的事件
    public void OnExitFocusOnEnemy()
    {
        currentLockedEnemy = null;
        FocusPointImage.gameObject.SetActive(false);
    }

    //生成处决的points
    public void CreateExecutedPoints(EnemyController enemy)
    {
        if (enemy == null) return;
        if(!executedPoints.ContainsKey(enemy))
        {
            executedPoints.Add(enemy, Instantiate(executedPointPrefab, pointParent));
            executedPoints[enemy].transform.position = Camera.main.WorldToScreenPoint(enemy.transform.position);
        }
    }

    //删除处决的points
    public void DeleteExecutedPoints(EnemyController enemy)
    {
        if (enemy == null) return;
        if(executedPoints.ContainsKey(enemy))
        {
            Destroy(executedPoints[enemy].gameObject);
            executedPoints.Remove(enemy);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值