前言:上一篇实现了敌人能动,有了点乐趣,但是敌人和玩家没什么对抗性。本篇将实现敌人追击玩家,并攻击玩家。
敌人攻击玩家
- 敌人检测玩家
- 目标
- 思路-碰撞检测的Trigger触发
- 实现
- 敌人攻击
- 目标
- 思路-模仿玩家发射子弹的思路
- 实现
- 效果
敌人检测玩家
目标
想法是这样的,玩家进入到敌人某个范围内,被敌人检测到后敌人会停下。
等玩家走后,敌人会继续原本终止的路径。
思路-碰撞检测的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;
}
}