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

news/2024/11/14 12:41:33 标签: Unity, FPS, 游戏

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

敌人攻击玩家

  • 敌人检测玩家
    • 目标
    • 思路-碰撞检测的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;
    }
}

效果

在这里插入图片描述


http://www.niftyadmin.cn/n/5751921.html

相关文章

深度解读UI设计:从概念到实践一站式知晓

在当今数字化的时代&#xff0c;我们与各种电子设备和软件应用的交互日益频繁。而在这背后&#xff0c;UI设计&#xff08;User Interface Design&#xff0c;用户界面设计&#xff09;起着至关重要的作用&#xff0c;它直接影响着用户与产品之间的交互体验和视觉感受。那么&am…

0-Equity-Transformer:求解NP-Hard Min-Max路由问题的顺序生成算法(AAAI-24)(完)(code)

文章目录 AbstractIntroduction问题表述Methodology多智能体位置编码公平上下文编码训练方案ExperimentsmTSP的性能评估mPDP的性能评估Related WorkConclusionAbstract 最小最大路由问题旨在通过智能体合作完成任务来最小化多个智能体中最长行程的长度。这些问题包括对现实世界…

Ubuntu 22.04 ftp搭建

1、安装ftp服务 sudo apt-get install vsftpd 2、FTP安装完成后&#xff0c;会默认为我们创建用户名为ftp的用户&#xff0c;默认无密码。我们需要为其设置密码。输入 sudo passwd ftp 3、创建ftp用户的home目录 sudo mkdir /home/ftp 4、设置ftp目录的权限 sudo chmod 777 /ho…

Python毕业设计选题:基于django+vue的仓库管理系统设计

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 用户管理 公告信息管理 产品信息管理 产品类型管理 用户…

陈文自媒体:65岁大爷,3年搞了150多万!

昨天下班去跑步&#xff0c;偶遇一个小区的大爷&#xff0c;他们两夫妻也去跑步&#xff0c;热情的我和他们聊起来了。 两夫妻已经60了&#xff0c;年纪和我老爸一个年纪的&#xff0c;从他们走路的动作来看&#xff0c;我认为他们的身体还是不错&#xff0c;简单介绍一下这个…

JSqlParser、JavaCC实操

1. 背景 项目中使用mubatis-plus&#xff0c;有个sql报错&#xff0c;信息如下 通过debug我发现是第四行代码报错 net.sf.jsqlparser.parser.CCJSqlParserUtil#parseStatements public static Statements parseStatements(String sqls) throws JSQLParserException {CCJSqlP…

热点更新场景,OceanBase如何实现性能优化

案例背景 这个案例来自一个保险行业的客户&#xff1a;他们的核心系统底层采用了OceanBase数据库作为存储解决方案&#xff0c;然而&#xff0c;在系统上线运行后&#xff0c;出现了一个异常情况&#xff0c;执行简单的主键更新语句时SQL执行时间出现了显著的波动。为了迅速定…

《实时流计算系统设计与实现》-Part 2-笔记

做不到实时 做不到实时的原因 实时计算很难。通过增量计算的方式来间接获得问题的&#xff08;伪&#xff09;实时结果&#xff0c;即使这些结果带有迟滞性和近似性&#xff0c;但只要能够带来尽可能最新的信息&#xff0c;那也是有价值的。 原因可分成3个方面&#xff1a; …