Unity3D学习FPS游戏(12)敌人检测和攻击玩家

前言:上一篇实现了敌人能动,有了点乐趣,但是敌人和玩家没什么对抗性。本篇将实现敌人追击玩家,并攻击玩家。

敌人检测玩家

目标

想法是这样的,玩家进入到敌人某个范围内,被敌人检测到后敌人会停下。
等玩家走后,敌人会继续原本终止的路径。

思路-碰撞检测的Trigger触发

还记得之前博客Unity3D学习FPS游戏(10)子弹攻击敌人掉血(碰撞检测)提到的碰撞检测吗?要用实现敌人检测玩家的原理也是一样的,只不过我们不需要碰撞了。

之前提到过碰撞检测有两种:Collision碰撞和Trigger触发。其中Collision碰撞是会有碰撞效果的,而Trigger触发则没有。

而敌人检测玩家就可以用没有碰撞的“Trigger触发”来检测。

回顾一下Trigger触发:

  • 函数:OnTriggerEnter/OnTriggerStay/OnTriggerExit
  • 效果:不会发生物体碰撞,也不会受到重力等物理作用力的影响,双方会直接穿过。
  • 条件:双方都有Collider,至少有一个运动的物体有Rigidbody。至少有一个碰撞体勾选IsTrigger。

目前,子弹有Colider且运动带有刚体,而玩家的Character Controller具备碰撞体(Collider)特性。

实现

给敌人构建一个检测范围,为其添加一个Sphere Collider球型的碰撞器,勾上IsTrigger就是触发器了。
在这里插入图片描述
调整检测范围,也就是Sphere Collider的Radius半径,我设置为了5。
在这里插入图片描述
编写代码思路,在检测到了玩家,就停下来攻击,并且不停面向玩家(为后面发射子弹做准备),等玩家离开视野后继续原本中断的路径。
需要编写OnTriggerEnter/OnTriggerStay/OnTriggerExit三个部分。
OnTriggerEnter检测到玩家进入范围的时候,停止移动;
OnTriggerStay玩家一直在检测范围内的时候,不停朝向玩家;
OnTriggerExit检测到玩家离开范围的时候,继续走原本的路径。

[Header("攻击数值")]
private Transform player;// 获取玩家坐标
private bool isFire;// 发射状态
void Start()
{
    isFire = false;// 初始不发射
    player = GameObject.Find("Player").transform;// 获取玩家位置
}
void Update()
{
    // 不在开火状态;如果路径已经算好了,而且到达目标位置很近
    if (!isFire&&!enemyAgent.pathPending&&enemyAgent.remainingDistance<0.1f)
    {
        SetNextDestination();
    }
}
private void OnTriggerEnter(Collider collision)
{
    if (collision.gameObject.CompareTag("Player"))
    {
        isFire = true;
        enemyAgent.isStopped = true;
    }
}
private void OnTriggerStay(Collider collision)
{
    if (collision.gameObject.CompareTag("Player"))
    {
        LookAtPlayer();
    }
}
private void OnTriggerExit(Collider collision)
{
    if (collision.gameObject.CompareTag("Player"))
    {
        enemyAgent.isStopped = false;
        isFire = false;
    }
}
// 朝向玩家
private void LookAtPlayer()
{
    // transform.LookAt(player);// 可以替代
    Vector3 direction = (player.position - transform.position).normalized;
    Quaternion lookRotation = Quaternion.LookRotation(direction);
    transform.rotation = lookRotation;
}

敌人攻击

目标

目前我们的敌人已经可以检测到玩家了,现在敌人要学会攻击玩家,朝着玩家发射子弹!
在玩家到检测范围内的时候,朝着玩家不停发射子弹。

思路-模仿玩家发射子弹的思路

发射子弹的思路,其实我们之前就已经做过了,在博客武器发射子弹对象池优化发射子弹中。和之前写玩家发射子弹的思路一样,只不过这里敌人没有武器模型了,所以Enemy的WeaponController我们可以直接挂载在敌人身上。

类似与玩家的WeaponController,我们可以写一个类似的EnemyWeaponController,只不过发射子弹的信号不再是鼠标输入,而是EnemyController中检测到玩家。另外,敌人子弹是没有弹夹限制。逻辑比之前玩家武器控制简单很多。

实现

EnemyWeaponController
在敌人预制体下面,设置一个子弹发射点用来作为发射子弹的起始位置,记得不要碰到敌人本身的碰撞体不然会有碰撞。
在这里插入图片描述

新建一个EnemyWeaponController,按照之前玩家发射子弹的代码思路改写,只是发射子弹不再是鼠标输入控制了;子弹也是不限额的,所以并没有弹夹这一说。

子弹还是用之前的玩家子弹的预制体,但是音效改一下,免得和玩家混了。

[Header("子弹数值")]
public Transform shootPoint;// 子弹发射位置
public GameObject bullet;// 子弹预制体
public float shootInterval = 2F;// 子弹间隔时间
private bool isFire;// 发射状态

public int bulletNum = 20;// 对象池大小
private ObjectPool<GameObject> bulletPool;// 子弹对象池

[Header("音效")]
public AudioSource shootAudio;// 发射音效

private void Awake()
{
    bulletPool = new ObjectPool<GameObject>(CreateBullet, BulletOnGet, BulletOnRelease, BulletOnDestory, true, 10, bulletNum);// 对象池构建
}

GameObject CreateBullet()
{
    GameObject obj = Instantiate(bullet, shootPoint);
    obj.GetComponent<BulletController>().characterType = CharacterType.Enemy;
    obj.GetComponent<BulletController>().bulletPool = bulletPool;
    return obj;
}

void BulletOnGet(GameObject obj)
{
    obj.GetComponent<BulletController>().BulletReset();
    obj.gameObject.SetActive(true);
}

void BulletOnRelease(GameObject obj)
{
    obj.gameObject.SetActive(false);
}

void BulletOnDestory(GameObject obj)
{
    Destroy(obj);
}
// 开始攻击
public void StartFire()
{
    StartCoroutine("Shoot");
}
// 停止攻击
public void StopFire()
{
    StopCoroutine("Shoot");
}
// 发射子弹协程
IEnumerator Shoot()
{
    while (true)
    {
        GameObject newBullet = bulletPool.Get();// 生成子弹
        if (shootAudio)// 发射音效
            shootAudio.Play();
        yield return new WaitForSeconds(shootInterval);
    }
}

在这里插入图片描述
EnemyController
让EnemyController调用WeaponController,在原本敌人控制代码的OnTriggerEnter部分开始攻击,OnTriggerExit部分停止攻击。

private void OnTriggerEnter(Collider collision)
{
    if (collision.gameObject.CompareTag("Player"))
    {
        isFire = true;
        enemyAgent.isStopped = true;
        this.GetComponent<EnemyWeaponController>().StartFire() ;
    }
}
private void OnTriggerExit(Collider collision)
{
    if (collision.gameObject.CompareTag("Player"))
    {
        this.GetComponent<EnemyWeaponController>().StopFire();
        enemyAgent.isStopped = false;
        isFire = false;
    }
}

效果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值