Unity中实现预制体自动巡逻与攻击敌人的完整实现指南

✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。

🍎个人主页:Java Fans的博客

🍊个人信条:不迁怒,不贰过。小知识,大智慧。

💞当前专栏:Java案例分享专栏

✨特色专栏:国学周更-心性养成之路

🥭本文内容:Unity中实现预制体自动巡逻与攻击敌人的完整实现指南

文章目录

    • 前言
    • [1. 巡逻功能:](#1. 巡逻功能:)
      • [1.1 引入命名空间](#1.1 引入命名空间)
      • [1.2 定义 Patrol 类](#1.2 定义 Patrol 类)
      • [1.3 声明公共变量](#1.3 声明公共变量)
      • [1.4 声明私有变量](#1.4 声明私有变量)
      • [1.5 Start 方法](#1.5 Start 方法)
      • [1.6 Update 方法](#1.6 Update 方法)
      • [1.7 设置随机巡逻点的逻辑](#1.7 设置随机巡逻点的逻辑)
      • [1.8 巡逻等待的协程](#1.8 巡逻等待的协程)
      • [1.9 巡逻功能总结](#1.9 巡逻功能总结)
    • [2. 发现敌人:](#2. 发现敌人:)
      • [2.1 类声明](#2.1 类声明)
      • [2.2 声明公共变量](#2.2 声明公共变量)
      • [2.3 声明私有变量](#2.3 声明私有变量)
      • [2.4 Update 方法](#2.4 Update 方法)
      • [2.5 MoveToEnemy 方法](#2.5 MoveToEnemy 方法)
      • [2.6 发现敌人总结](#2.6 发现敌人总结)
    • [3. 攻击敌人:](#3. 攻击敌人:)
      • [3.1 类声明](#3.1 类声明)
      • [3.2 声明公共变量](#3.2 声明公共变量)
      • [3.3. 声明私有变量](#3.3. 声明私有变量)
      • [3.4. Update 方法](#3.4. Update 方法)
      • [3.5 Attack 方法](#3.5 Attack 方法)
      • [3.6 攻击敌人总结](#3.6 攻击敌人总结)
    • [4. 整合逻辑:](#4. 整合逻辑:)
      • [4.1 定义枚举类型](#4.1 定义枚举类型)
      • [4.2 声明当前状态](#4.2 声明当前状态)
      • [4.3 Update 方法](#4.3 Update 方法)
        • [4.3.1 巡逻状态](#4.3.1 巡逻状态)
        • [4.3.2 追逐状态](#4.3.2 追逐状态)
        • [4.3.3 攻击状态](#4.3.3 攻击状态)
      • [4.4 整合逻辑总结](#4.4 整合逻辑总结)
    • 总结

前言

在当今游戏开发领域,自动化行为技术的应用已经成为游戏开发者们追求的热门话题。无论是为了增加游戏的趣味性、提升游戏体验,还是为了减轻开发者的工作负担,实现预制体自动化行为已经成为许多游戏项目的必备功能之一。本文将深入探讨如何在Unity游戏引擎中实现预制体自动巡逻并攻击敌人的全过程。

在游戏开发中,预制体是一种非常常见的游戏对象,它们可以被重复使用并在场景中多次实例化。然而,让预制体具备自主行为、能够在游戏世界中自主移动、探索并与敌人交互,是一项具有挑战性的任务。本文将引导读者逐步实现预制体的自动化行为,包括目标位置到达后的随机巡逻、敌人发现与追击、攻击等关键步骤。

通过本文的指导,读者将深入了解自动化行为背后的技术原理,学习如何利用Unity强大的功能和工具,为游戏角色赋予更加智能和自主的行为。无论是想要开发一款冒险游戏、射击游戏还是策略游戏,本文都将为您提供宝贵的指导和灵感,帮助您实现游戏中角色的自动化行为,为玩家带来更加丰富和有趣的游戏体验。

1. 巡逻功能:

预制体在到达目标位置后,会在设定的范围内随机选择一个点作为新的目标位置,并移动到该点。

c 复制代码
using UnityEngine;
using UnityEngine.AI;
 
public class Patrol : MonoBehaviour
{
    public Transform centerPoint; // 巡逻的中心点
    public float patrolRadius = 10f; // 巡逻半径
    public float patrolWaitTime = 2f; // 巡逻停留时间
    private NavMeshAgent agent;
    private Vector3 targetPosition;
 
    void Start()
    {
        agent = GetComponent<NavMeshAgent>();
        SetRandomPatrolPoint();
    }
 
    void Update()
    {
        if (!agent.pathPending && agent.remainingDistance < 0.5f)
        {
            StartCoroutine(PatrolWait());
        }
    }
 
    // 设置随机的巡逻点
    void SetRandomPatrolPoint()
    {
        Vector3 randomDirection = Random.insideUnitSphere * patrolRadius;
        randomDirection += centerPoint.position;
        NavMeshHit hit;
        NavMesh.SamplePosition(randomDirection, out hit, patrolRadius, 1);
        targetPosition = hit.position;
        agent.SetDestination(targetPosition);
    }
 
    // 巡逻点到达后的等待
    IEnumerator PatrolWait()
    {
        yield return new WaitForSeconds(patrolWaitTime);
        SetRandomPatrolPoint();
    }
}

这段代码是一个 Unity 脚本,主要用于实现一个 AI 角色的巡逻行为。下面是对代码的逐行解释:

1.1 引入命名空间

c 复制代码
using UnityEngine;
using UnityEngine.AI;
  • UnityEngine 是 Unity 的核心命名空间,包含大多数常用类。

  • UnityEngine.AI 是用于处理导航和 AI 行为的命名空间,包含 NavMeshAgent 类。

1.2 定义 Patrol 类

c 复制代码
public class Patrol : MonoBehaviour

定义一个名为 Patrol 的公共类,继承自 MonoBehaviour。这使得 Patrol 可以被附加到 Unity 的游戏对象上,并具有 Unity 生命周期方法(如 Start 和 Update)。

1.3 声明公共变量

c 复制代码
public Transform centerPoint; // 巡逻的中心点
public float patrolRadius = 10f; // 巡逻半径
public float patrolWaitTime = 2f; // 巡逻停留时间
  • centerPoint: 一个 Transform 类型的公共变量,用于定义巡逻的中心点(AI 角色的巡逻范围中心)。

  • patrolRadius: 一个 float 类型的公共变量,定义 AI 角色巡逻的半径,默认为 10。

  • patrolWaitTime: 一个 float 类型的公共变量,定义 AI 角色在每个巡逻点停留的时间,默认为 2 秒。

1.4 声明私有变量

c 复制代码
private NavMeshAgent agent;
private Vector3 targetPosition;
  • agent: 一个 NavMeshAgent 类型的私有变量,用于获取和控制 AI 角色的导航行为。

  • targetPosition: 一个 Vector3 类型的私有变量,用于存储 AI 角色当前巡逻的目标位置。

1.5 Start 方法

c 复制代码
void Start()
{
    agent = GetComponent<NavMeshAgent>();
    SetRandomPatrolPoint();
}

Start 方法在游戏开始时被调用。

  • agent = GetComponent(): 获取附加在该游戏对象上的 NavMeshAgent 组件,并将其赋值给 agent 变量。

  • SetRandomPatrolPoint(): 调用 SetRandomPatrolPoint 方法,初始化 AI 的巡逻目标点。

1.6 Update 方法

c 复制代码
void Update()
{
    if (!agent.pathPending && agent.remainingDistance < 0.5f)
    {
        StartCoroutine(PatrolWait());
    }
}

Update 方法在每一帧被调用。

  • if (!agent.pathPending && agent.remainingDistance < 0.5f): 检查 AI 角色是否已经到达目标位置(remainingDistance 小于 0.5 表示接近目标)。pathPending 表示当前是否还有路径计算未完成。

  • StartCoroutine(PatrolWait()): 如果到达目标位置,则启动一个协程,调用 PatrolWait 方法。

1.7 设置随机巡逻点的逻辑

c 复制代码
void SetRandomPatrolPoint()
{
    Vector3 randomDirection = Random.insideUnitSphere * patrolRadius;
    randomDirection += centerPoint.position;
    NavMeshHit hit;
    NavMesh.SamplePosition(randomDirection, out hit, patrolRadius, 1);
    targetPosition = hit.position;
    agent.SetDestination(targetPosition);
}
  • Vector3 randomDirection = Random.insideUnitSphere * patrolRadius;: 生成一个在球体内的随机方向,并乘以 patrolRadius,以得到一个随机方向的偏移量。

  • randomDirection += centerPoint.position;: 将随机偏移量添加到巡逻中心点的坐标,确定目标位置。

  • NavMeshHit hit;: 声明一个 NavMeshHit 变量,用于接收导航网格采样结果。

  • NavMesh.SamplePosition(randomDirection, out hit, patrolRadius, 1);: 在 randomDirection 位置附近采样,找到一个有效的导航网格位置,并将结果存储在 hit 中。

  • targetPosition = hit.position;: 将有效的目标位置赋值给 targetPosition。

  • agent.SetDestination(targetPosition);: 将 AI 角色的目标位置设置为新的巡逻点,使其开始移动。

1.8 巡逻等待的协程

c 复制代码
IEnumerator PatrolWait()
{
    yield return new WaitForSeconds(patrolWaitTime);
    SetRandomPatrolPoint();
}
  • IEnumerator PatrolWait(): 定义一个协程方法,允许使用 yield return。

  • yield return new WaitForSeconds(patrolWaitTime);: 暂停协程执行,等待 patrolWaitTime 秒。

  • SetRandomPatrolPoint();: 等待结束后,调用 SetRandomPatrolPoint 方法,设置新的巡逻目标点。

1.9 巡逻功能总结

这段代码实现了一个 AI 角色在指定中心点周围巡逻的行为。它会在每个目标点停留一定时间后,再随机选择新的目标点,并利用 Unity 的导航系统(NavMesh)来处理移动。这样可以创建一个简单的巡逻行为,适用于 NPC(非玩家角色)在游戏中的导航。

2. 发现敌人:

在巡逻过程中,如果在一定范围内发现敌人(使用 Physics.OverlapSphere 或者 Physics.Raycast),预制体会停止巡逻并向敌人移动。

c 复制代码
public class DetectEnemy : MonoBehaviour
{
    public float detectionRadius = 15f; // 发现敌人的范围
    public LayerMask enemyLayer; // 敌人的图层
    private GameObject enemyTarget;
 
    void Update()
    {
        Collider[] hitColliders = Physics.OverlapSphere(transform.position, detectionRadius, enemyLayer);
        if (hitColliders.Length > 0)
        {
            enemyTarget = hitColliders[0].gameObject;
            MoveToEnemy();
        }
    }
 
    void MoveToEnemy()
    {
        NavMeshAgent agent = GetComponent<NavMeshAgent>();
        agent.SetDestination(enemyTarget.transform.position);
    }
}

这段 Unity 脚本代码实现了一个 AI 角色检测敌人并朝向敌人移动的功能。下面是对代码逐行的解释:

2.1 类声明

c 复制代码
public class DetectEnemy : MonoBehaviour

定义一个名为 DetectEnemy 的公共类,继承自 MonoBehaviour。这意味着该类可以被附加到 Unity 中的游戏对象,并能够使用 Unity 的生命周期方法。

2.2 声明公共变量

c 复制代码
public float detectionRadius = 15f; // 发现敌人的范围
public LayerMask enemyLayer; // 敌人的图层
  • detectionRadius: 一个公共变量,类型为 float,用于定义 AI 角色检测敌人的半径,默认为 15。

  • enemyLayer: 一个公共变量,类型为 LayerMask,用于指定哪些图层被视为敌人。这使得该 AI 角色能够仅检测特定类型的对象。

2.3 声明私有变量

c 复制代码
private GameObject enemyTarget;
  • enemyTarget: 一个私有变量,类型为 GameObject,用于存储检测到的敌人对象的引用。

2.4 Update 方法

c 复制代码
void Update()
{
    Collider[] hitColliders = Physics.OverlapSphere(transform.position, detectionRadius, enemyLayer);
    if (hitColliders.Length > 0)
    {
        enemyTarget = hitColliders[0].gameObject;
        MoveToEnemy();
    }
}

Update 方法在每一帧被调用,用于检查和更新 AI 角色的状态。

  • Collider[] hitColliders = Physics.OverlapSphere(transform.position, detectionRadius, enemyLayer);: 使用 Physics.OverlapSphere 方法在 AI 角色的位置(transform.position)周围创建一个球体,以 detectionRadius 为半径,检测所有与 enemyLayer 图层相交的碰撞体(即敌人)。返回的碰撞体存储在 hitColliders 数组中。

  • if (hitColliders.Length > 0): 检查是否检测到了任何敌人(即 hitColliders 数组的长度是否大于 0)。

  • enemyTarget = hitColliders[0].gameObject;: 如果检测到敌人,获取第一个敌人的引用,并将其赋值给 enemyTarget 变量。

  • MoveToEnemy();: 调用 MoveToEnemy 方法,指示 AI 角色朝向敌人移动。

2.5 MoveToEnemy 方法

c 复制代码
void MoveToEnemy()
{
    NavMeshAgent agent = GetComponent<NavMeshAgent>();
    agent.SetDestination(enemyTarget.transform.position);
}
  • void MoveToEnemy(): 定义一个私有方法,用于处理朝向敌人移动的逻辑。

  • NavMeshAgent agent = GetComponent();: 获取附加在该游戏对象上的 NavMeshAgent 组件,并将其赋值给 agent 变量,以便使用导航功能。

  • agent.SetDestination(enemyTarget.transform.position);: 将 AI 角色的目标位置设置为 enemyTarget(即敌人)的当前位置,使 AI 角色开始向敌人移动。

2.6 发现敌人总结

这段代码实现了一个基本的敌人检测系统,允许 AI 角色在指定半径内检测到敌人,并朝向第一个检测到的敌人移动。它利用 Unity 的物理系统(Physics.OverlapSphere)和导航系统(NavMeshAgent)来实现这一功能。这个脚本可以用于游戏中 AI 角色的敌人追踪行为。

3. 攻击敌人:

当预制体靠近敌人一定距离时,执行攻击动作。

c 复制代码
public class AttackEnemy : MonoBehaviour
{
    public float attackRange = 2f; // 攻击范围
    public float attackCooldown = 1.5f; // 攻击冷却时间
    private float lastAttackTime;
 
    void Update()
    {
        if (enemyTarget != null && Vector3.Distance(transform.position, enemyTarget.transform.position) <= attackRange)
        {
            if (Time.time >= lastAttackTime + attackCooldown)
            {
                Attack();
                lastAttackTime = Time.time;
            }
        }
    }
 
    void Attack()
    {
        // 攻击行为,例如减去敌人生命值
        Debug.Log("Attacking the enemy!");
    }
}

这段 Unity 脚本代码实现了一个 AI 角色攻击敌人的功能。下面是对代码逐行的解释:

3.1 类声明

c 复制代码
public class AttackEnemy : MonoBehaviour

定义一个名为 AttackEnemy 的公共类,继承自 MonoBehaviour。这意味着该类可以被附加到 Unity 的游戏对象上,并能够使用 Unity 的生命周期方法。

3.2 声明公共变量

c 复制代码
public float attackRange = 2f; // 攻击范围
public float attackCooldown = 1.5f; // 攻击冷却时间
  • attackRange: 一个公共变量,类型为 float,用于定义 AI 角色攻击的有效范围,默认为 2(单位通常为米)。

  • attackCooldown: 一个公共变量,类型为 float,用于定义攻击之间的冷却时间,默认为 1.5 秒。

3.3. 声明私有变量

c 复制代码
private float lastAttackTime;
  • lastAttackTime: 一个私有变量,类型为 float,用于记录上一次攻击的时间。这有助于管理攻击的冷却时间。

3.4. Update 方法

c 复制代码
void Update()
{
    if (enemyTarget != null && Vector3.Distance(transform.position, enemyTarget.transform.position) <= attackRange)
    {
        if (Time.time >= lastAttackTime + attackCooldown)
        {
            Attack();
            lastAttackTime = Time.time;
        }
    }
}

Update 方法在每一帧被调用,用于检查 AI 角色的攻击条件。

  • if (enemyTarget != null && Vector3.Distance(transform.position, enemyTarget.transform.position) <= attackRange):

首先检查 enemyTarget 是否为 null,确保 AI 角色已经有目标。

使用 Vector3.Distance 方法计算 AI 角色与敌人之间的距离。如果距离小于或等于 attackRange,则表示敌人在攻击范围内。

  • if (Time.time >= lastAttackTime + attackCooldown): 检查当前时间是否大于等于上一次攻击时间加上冷却时间。如果满足条件,表示可以进行下一次攻击。

  • Attack();: 调用 Attack 方法,执行攻击动作。

  • lastAttackTime = Time.time;: 更新 lastAttackTime 为当前时间,以记录本次攻击的时间。

3.5 Attack 方法

c 复制代码
void Attack()
{
    // 攻击行为,例如减去敌人生命值
    Debug.Log("Attacking the enemy!");
}
  • void Attack(): 定义一个私有方法,用于处理攻击行为。

在方法中,通过 Debug.Log 输出信息,表示正在攻击敌人。这一行代码只是示例,实际游戏中可以替换为减少敌人生命值、播放攻击动画、音效等攻击逻辑。

3.6 攻击敌人总结

这段代码实现了 AI 角色在一定范围内检测敌人并进行攻击的功能。它使用冷却机制来控制攻击的频率,确保 AI 不会在攻击之间过于频繁地进行攻击。这个脚本可以与敌人检测和移动脚本结合使用,以实现完整的 AI 行为模式。

4. 整合逻辑:

将巡逻、发现敌人和攻击的逻辑整合在一个脚本或通过多个脚本管理,使得预制体能在巡逻、发现敌人、追逐敌人和攻击之间进行状态切换。你可以使用状态机来更清晰地管理这些状态的切换。

c 复制代码
public enum AIState { Patrol, Chase, Attack }
public AIState currentState = AIState.Patrol;
 
void Update()
{
    switch (currentState)
    {
        case AIState.Patrol:
            Patrol();
            if (DetectEnemy())
                currentState = AIState.Chase;
            break;
        case AIState.Chase:
            ChaseEnemy();
            if (IsInAttackRange())
                currentState = AIState.Attack;
            break;
        case AIState.Attack:
            AttackEnemy();
            if (!IsInAttackRange())
                currentState = AIState.Chase;
            break;
    }
}

这段 Unity 脚本代码实现了一个简单的 AI 状态机,用于控制 AI 角色在不同状态下的行为。这种设计模式可以帮助开发者管理复杂的角色行为,使其在不同情境下表现出不同的反应。以下是对代码逐行的解释:

4.1 定义枚举类型

c 复制代码
public enum AIState { Patrol, Chase, Attack }

public enum AIState: 定义一个公共枚举类型 AIState,用于表示 AI 角色的不同状态。

  • 枚举包含三个状态:

  • Patrol: 巡逻状态。

  • Chase: 追逐状态。

  • Attack: 攻击状态。

4.2 声明当前状态

c 复制代码
public AIState currentState = AIState.Patrol;

public AIState currentState: 声明一个公共变量 currentState,用于存储 AI 角色的当前状态。

  • = AIState.Patrol: 初始化 currentState 为 Patrol,表示 AI 角色开始时处于巡逻状态。

4.3 Update 方法

c 复制代码
void Update()
{
    switch (currentState)
    {
        case AIState.Patrol:
            Patrol();
            if (DetectEnemy())
                currentState = AIState.Chase;
            break;
        case AIState.Chase:
            ChaseEnemy();
            if (IsInAttackRange())
                currentState = AIState.Attack;
            break;
        case AIState.Attack:
            AttackEnemy();
            if (!IsInAttackRange())
                currentState = AIState.Chase;
            break;
    }
}

Update 方法在每一帧被调用,用于检查并执行 AI 角色的行为。

  • switch (currentState): 根据 currentState 的值,执行不同的代码块。
4.3.1 巡逻状态
c 复制代码
case AIState.Patrol:
    Patrol();
    if (DetectEnemy())
        currentState = AIState.Chase;
    break;
  • Patrol();: 调用 Patrol 方法,执行巡逻行为。

  • if (DetectEnemy()): 检测是否发现敌人,如果发现,切换状态为 Chase(追逐)。

4.3.2 追逐状态
c 复制代码
case AIState.Chase:
    ChaseEnemy();
    if (IsInAttackRange())
        currentState = AIState.Attack;
    break;
  • ChaseEnemy();: 调用 ChaseEnemy 方法,执行追逐敌人的行为。

  • if (IsInAttackRange()): 检查是否在攻击范围内,如果是,切换状态为 Attack(攻击)。

4.3.3 攻击状态
c 复制代码
case AIState.Attack:
    AttackEnemy();
    if (!IsInAttackRange())
        currentState = AIState.Chase;
    break;
  • AttackEnemy();: 调用 AttackEnemy 方法,执行攻击行为。

  • if (!IsInAttackRange()): 检查是否不再处于攻击范围内。如果不在攻击范围内,切换状态为 Chase(追逐),重新开始追逐敌人。

4.4 整合逻辑总结

这段代码实现了一个简单的 AI 状态机,使得 AI 角色能够根据当前的状态执行相应的行为:巡逻、追逐或攻击敌人。通过使用状态机,AI 能够更灵活地管理行为,便于扩展和维护。具体的巡逻、追逐和攻击行为需要在其他方法(如 Patrol、ChaseEnemy 和 AttackEnemy)中实现。

总结

通过使用Unity中的NavMeshAgent实现预制体的随机巡逻,结合Physics.OverlapSphere检测敌人并触发追击状态,以及在距离足够近时执行攻击动作,我们成功地实现了预制体的自动化行为。通过合理的状态切换逻辑管理预制体的行为,我们为游戏角色赋予了智能和自主性,使其能够在游戏世界中自主探索、发现敌人并进行攻击。这种综合应用不仅提升了游戏的交互性和挑战性,也为开发者们提供了实现自动化行为的有效方法,为游戏开发注入了新的活力和可能性。


码文不易,本篇文章就介绍到这里,如果想要学习更多Java系列知识点击关注博主,博主带你零基础学习Java知识。与此同时,对于日常生活有困扰的朋友,欢迎阅读我的第四栏目《国学周更---心性养成之路》,学习技术的同时,我们也注重了心性的养成。

相关推荐
牙膏上的小苏打233319 分钟前
Unity Surround开关后导致获取主显示器分辨率错误
unity·主屏幕
Unity大海2 小时前
诠视科技Unity SDK开发环境配置、项目设置、apk打包。
科技·unity·游戏引擎
浅陌sss8 小时前
Unity中 粒子系统使用整理(一)
unity·游戏引擎
维度攻城狮12 小时前
实现在Unity3D中仿真汽车,而且还能使用ros2控制
python·unity·docker·汽车·ros2·rviz2
为你写首诗ge15 小时前
【Unity网络编程知识】FTP学习
网络·unity
神码编程17 小时前
【Unity】 HTFramework框架(六十四)SaveDataRuntime运行时保存组件参数、预制体
unity·编辑器·游戏引擎
菲fay19 小时前
Unity 单例模式写法
unity·单例模式
火一线20 小时前
【Framework-Client系列】UIGenerate介绍
游戏·unity
ZKY_2421 小时前
【工具】Json在线解析工具
unity·json
ZKY_241 天前
【Unity】处理文字显示不全的问题
unity·游戏引擎