unity射击游戏项目中玩家是怎么检测可攻击的敌人的

在Unity射击游戏项目中,玩家检测可攻击的敌人通常涉及以下几个步骤和数学公式:

  1. 视野检测(Field of View, FOV)

    • 角度检测:首先,确定敌人是否在玩家的视野范围内。假设玩家的视野角度为FOV,玩家的前向向量为forward,敌人的位置为enemyPosition,玩家的位置为playerPosition
    • 计算玩家到敌人的向量directionToEnemy
      Vector3 directionToEnemy = (enemyPosition - playerPosition).normalized;
      
    • 计算玩家前向向量和directionToEnemy之间的夹角:
      float angleToEnemy = Vector3.Angle(forward, directionToEnemy);
      
    • 检查敌人是否在视野角度内:
      if (angleToEnemy < FOV / 2)
      {
          // 敌人在视野范围内
      }
      
  2. 距离检测

    • 确定敌人是否在玩家的攻击范围内。假设攻击范围为attackRange
    • 计算玩家和敌人之间的距离:
      float distanceToEnemy = Vector3.Distance(playerPosition, enemyPosition);
      
    • 检查敌人是否在攻击范围内:
      if (distanceToEnemy <= attackRange)
      {
          // 敌人在攻击范围内
      }
      
  3. 射线检测(Raycasting)

    • 确保玩家和敌人之间没有障碍物。使用射线检测来确定玩家是否可以直接看到敌人。
    • 发射一条从玩家位置到敌人位置的射线:
      RaycastHit hit;
      if (Physics.Raycast(playerPosition, directionToEnemy, out hit, attackRange))
      {
          if (hit.transform.position == enemyPosition)
          {
              // 没有障碍物,敌人可见
          }
      }
      

综合以上步骤,完整的检测流程如下:

void DetectEnemies()
{
    foreach (var enemy in enemies)
    {
        Vector3 directionToEnemy = (enemy.position - player.position).normalized;
        float angleToEnemy = Vector3.Angle(player.forward, directionToEnemy);
        float distanceToEnemy = Vector3.Distance(player.position, enemy.position);

        if (angleToEnemy < FOV / 2 && distanceToEnemy <= attackRange)
        {
            RaycastHit hit;
            if (Physics.Raycast(player.position, directionToEnemy, out hit, attackRange))
            {
                if (hit.transform.position == enemy.position)
                {
                    // 敌人在视野范围内、攻击范围内且没有障碍物
                    // 可以攻击敌人
                }
            }
        }
    }
}

通过这些步骤,玩家可以有效地检测到可攻击的敌人。

在继续之前,我们可以进一步优化和扩展上述检测流程,以提高性能和功能性。以下是一些可能的改进和扩展:

1. 优化性能

在大型场景中,逐个检查所有敌人可能会导致性能问题。可以使用一些优化技术来减少计算量。

使用层级检测(Layer Mask)

通过使用层级检测,可以减少不必要的物理检测。假设敌人和障碍物分别在不同的层级上。

int enemyLayerMask = LayerMask.GetMask("Enemy");
int obstacleLayerMask = LayerMask.GetMask("Obstacle");

void DetectEnemies()
{
    foreach (var enemy in enemies)
    {
        Vector3 directionToEnemy = (enemy.position - player.position).normalized;
        float angleToEnemy = Vector3.Angle(player.forward, directionToEnemy);
        float distanceToEnemy = Vector3.Distance(player.position, enemy.position);

        if (angleToEnemy < FOV / 2 && distanceToEnemy <= attackRange)
        {
            RaycastHit hit;
            if (Physics.Raycast(player.position, directionToEnemy, out hit, attackRange, obstacleLayerMask))
            {
                if (hit.transform.gameObject.layer == LayerMask.NameToLayer("Enemy"))
                {
                    // 敌人在视野范围内、攻击范围内且没有障碍物
                    // 可以攻击敌人
                }
            }
        }
    }
}

2. 使用物理触发器(Physics Triggers)

可以在玩家周围设置一个触发器区域,当敌人进入该区域时进行检测。这可以减少每帧的计算量。

void OnTriggerEnter(Collider other)
{
    if (other.gameObject.layer == LayerMask.NameToLayer("Enemy"))
    {
        // 敌人进入触发器区域,进行进一步检测
        Vector3 directionToEnemy = (other.transform.position - player.position).normalized;
        float angleToEnemy = Vector3.Angle(player.forward, directionToEnemy);
        float distanceToEnemy = Vector3.Distance(player.position, other.transform.position);

        if (angleToEnemy < FOV / 2 && distanceToEnemy <= attackRange)
        {
            RaycastHit hit;
            if (Physics.Raycast(player.position, directionToEnemy, out hit, attackRange, obstacleLayerMask))
            {
                if (hit.transform.gameObject.layer == LayerMask.NameToLayer("Enemy"))
                {
                    // 敌人在视野范围内、攻击范围内且没有障碍物
                    // 可以攻击敌人
                }
            }
        }
    }
}

3. 使用空间分区(Spatial Partitioning)

在大型场景中,可以使用四叉树(Quadtree)或八叉树(Octree)等空间分区技术来减少检测范围。

4. 视觉辅助(Visual Aids)

为了调试和优化,可以在Unity编辑器中使用Gizmos来可视化玩家的视野和攻击范围。

void OnDrawGizmos()
{
    Gizmos.color = Color.yellow;
    Gizmos.DrawWireSphere(player.position, attackRange);

    Vector3 leftBoundary = Quaternion.Euler(0, -FOV / 2, 0) * player.forward * attackRange;
    Vector3 rightBoundary = Quaternion.Euler(0, FOV / 2, 0) * player.forward * attackRange;

    Gizmos.color = Color.blue;
    Gizmos.DrawLine(player.position, player.position + leftBoundary);
    Gizmos.DrawLine(player.position, player.position + rightBoundary);
}

5. 多线程和异步计算

在复杂场景中,可以使用多线程或异步计算来分担检测任务,避免主线程卡顿。

6. AI行为树(Behavior Trees)

如果敌人有复杂的行为逻辑,可以使用行为树来管理敌人的状态和行为。

通过这些优化和扩展,可以提高玩家检测可攻击敌人的效率和准确性,从而提升游戏的整体性能和体验。

好的,让我们继续深入探讨如何在Unity射击游戏项目中优化和扩展玩家检测可攻击敌人的方法。

7. 使用事件系统

在大型场景中,频繁的检测可能会影响性能。可以使用事件系统来减少不必要的检测。例如,当敌人进入或离开玩家的视野范围时触发事件。

事件系统示例

首先,定义一个事件管理器来处理敌人进入和离开视野范围的事件。

using System;
using UnityEngine;

public class EnemyDetectionEventManager : MonoBehaviour
{
    public static event Action<Transform> OnEnemyEnterView;
    public static event Action<Transform> OnEnemyExitView;

    public static void EnemyEnterView(Transform enemy)
    {
        OnEnemyEnterView?.Invoke(enemy);
    }

    public static void EnemyExitView(Transform enemy)
    {
        OnEnemyExitView?.Invoke(enemy);
    }
}

然后,在玩家脚本中订阅这些事件,并在适当的时候触发它们。

public class PlayerDetection : MonoBehaviour
{
    public float FOV = 90f;
    public float attackRange = 10f;
    public LayerMask enemyLayerMask;
    public LayerMask obstacleLayerMask;

    private void OnEnable()
    {
        EnemyDetectionEventManager.OnEnemyEnterView += HandleEnemyEnterView;
        EnemyDetectionEventManager.OnEnemyExitView += HandleEnemyExitView;
    }

    private void OnDisable()
    {
        EnemyDetectionEventManager.OnEnemyEnterView -= HandleEnemyEnterView;
        EnemyDetectionEventManager.OnEnemyExitView -= HandleEnemyExitView;
    }

    private void HandleEnemyEnterView(Transform enemy)
    {
        // 处理敌人进入视野范围的逻辑
    }

    private void HandleEnemyExitView(Transform enemy)
    {
        // 处理敌人离开视野范围的逻辑
    }

    private void Update()
    {
        DetectEnemies();
    }

    void DetectEnemies()
    {
        Collider[] enemiesInRange = Physics.OverlapSphere(transform.position, attackRange, enemyLayerMask);
        foreach (var enemyCollider in enemiesInRange)
        {
            Transform enemy = enemyCollider.transform;
            Vector3 directionToEnemy = (enemy.position - transform.position).normalized;
            float angleToEnemy = Vector3.Angle(transform.forward, directionToEnemy);

            if (angleToEnemy < FOV / 2)
            {
                RaycastHit hit;
                if (Physics.Raycast(transform.position, directionToEnemy, out hit, attackRange, obstacleLayerMask))
                {
                    if (hit.transform == enemy)
                    {
                        // 敌人在视野范围内、攻击范围内且没有障碍物
                        // 触发敌人进入视野范围事件
                        EnemyDetectionEventManager.EnemyEnterView(enemy);
                    }
                }
            }
            else
            {
                // 触发敌人离开视野范围事件
                EnemyDetectionEventManager.EnemyExitView(enemy);
            }
        }
    }
}

8. 使用NavMesh进行路径检测

在复杂的3D环境中,可以使用Unity的NavMesh系统来帮助检测敌人是否在可攻击范围内。NavMesh可以帮助确定玩家和敌人之间是否有可行路径。

using UnityEngine.AI;

public class PlayerDetection : MonoBehaviour
{
    public float FOV = 90f;
    public float attackRange = 10f;
    public LayerMask enemyLayerMask;
    public LayerMask obstacleLayerMask;
    public NavMeshAgent navMeshAgent;

    void DetectEnemies()
    {
        Collider[] enemiesInRange = Physics.OverlapSphere(transform.position, attackRange, enemyLayerMask);
        foreach (var enemyCollider in enemiesInRange)
        {
            Transform enemy = enemyCollider.transform;
            Vector3 directionToEnemy = (enemy.position - transform.position).normalized;
            float angleToEnemy = Vector3.Angle(transform.forward, directionToEnemy);

            if (angleToEnemy < FOV / 2)
            {
                NavMeshPath path = new NavMeshPath();
                if (navMeshAgent.CalculatePath(enemy.position, path) && path.status == NavMeshPathStatus.PathComplete)
                {
                    // 敌人在视野范围内、攻击范围内且有可行路径
                    // 可以攻击敌人
                }
            }
        }
    }
}

9. 使用AI感知系统

如果你的游戏需要更复杂的AI感知系统,可以考虑使用Unity的ML-Agents或其他第三方AI感知系统。这些系统可以提供更高级的感知和决策功能。

10. 结合动画和音效

为了增强游戏体验,可以结合动画和音效来反馈敌人进入或离开视野范围的情况。例如,当敌人进入视野范围时,播放警告音效或显示警告图标。

public class PlayerDetection : MonoBehaviour
{
    public AudioClip enemyDetectedSound;
    private AudioSource audioSource;

    private void Start()
    {
        audioSource = GetComponent<AudioSource>();
    }

    private void HandleEnemyEnterView(Transform enemy)
    {
        // 播放警告音效
        audioSource.PlayOneShot(enemyDetectedSound);
        // 显示警告图标
        // ...
    }

    private void HandleEnemyExitView(Transform enemy)
    {
        // 隐藏警告图标
        // ...
    }
}

通过这些优化和扩展,可以进一步提高玩家检测可攻击敌人的效率和功能性,从而提升游戏的整体性能和体验。

当然,我们可以继续深入探讨更多的优化和扩展方法,以进一步提升玩家检测可攻击敌人的效率和功能性。

11. 使用对象池(Object Pooling)

在游戏中频繁创建和销毁对象(如射线、触发器等)可能会导致性能问题。对象池技术可以帮助减少内存分配和垃圾回收的开销。

对象池示例

首先,创建一个对象池管理器:

using System.Collections.Generic;
using UnityEngine;

public class ObjectPool<T> where T : Component
{
    private readonly T prefab;
    private readonly Queue<T> objects = new Queue<T>();

    public ObjectPool(T prefab, int initialSize)
    {
        this.prefab = prefab;
        for (int i = 0; i < initialSize; i++)
        {
            T obj = GameObject.Instantiate(prefab);
            obj.gameObject.SetActive(false);
            objects.Enqueue(obj);
        }
    }

    public T Get()
    {
        if (objects.Count > 0)
        {
            T obj = objects.Dequeue();
            obj.gameObject.SetActive(true);
            return obj;
        }
        else
        {
            T obj = GameObject.Instantiate(prefab);
            return obj;
        }
    }

    public void ReturnToPool(T obj)
    {
        obj.gameObject.SetActive(false);
        objects.Enqueue(obj);
    }
}

然后,在玩家检测脚本中使用对象池:

public class PlayerDetection : MonoBehaviour
{
    public float FOV = 90f;
    public float attackRange = 10f;
    public LayerMask enemyLayerMask;
    public LayerMask obstacleLayerMask;
    public GameObject rayPrefab;
    private ObjectPool<Ray> rayPool;

    private void Start()
    {
        rayPool = new ObjectPool<Ray>(rayPrefab.GetComponent<Ray>(), 10);
    }

    void DetectEnemies()
    {
        Collider[] enemiesInRange = Physics.OverlapSphere(transform.position, attackRange, enemyLayerMask);
        foreach (var enemyCollider in enemiesInRange)
        {
            Transform enemy = enemyCollider.transform;
            Vector3 directionToEnemy = (enemy.position - transform.position).normalized;
            float angleToEnemy = Vector3.Angle(transform.forward, directionToEnemy);

            if (angleToEnemy < FOV / 2)
            {
                Ray ray = rayPool.Get();
                RaycastHit hit;
                if (Physics.Raycast(transform.position, directionToEnemy, out hit, attackRange, obstacleLayerMask))
                {
                    if (hit.transform == enemy)
                    {
                        // 敌人在视野范围内、攻击范围内且没有障碍物
                        // 可以攻击敌人
                    }
                }
                rayPool.ReturnToPool(ray);
            }
        }
    }
}

12. 使用多线程和任务并行库(Task Parallel Library, TPL)

在复杂场景中,可以使用多线程或任务并行库来分担检测任务,避免主线程卡顿。

多线程示例

使用C#的任务并行库(TPL)来并行化敌人检测:

using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;

public class PlayerDetection : MonoBehaviour
{
    public float FOV = 90f;
    public float attackRange = 10f;
    public LayerMask enemyLayerMask;
    public LayerMask obstacleLayerMask;

    void DetectEnemies()
    {
        Collider[] enemiesInRange = Physics.OverlapSphere(transform.position, attackRange, enemyLayerMask);
        List<Task> tasks = new List<Task>();

        foreach (var enemyCollider in enemiesInRange)
        {
            tasks.Add(Task.Run(() =>
            {
                Transform enemy = enemyCollider.transform;
                Vector3 directionToEnemy = (enemy.position - transform.position).normalized;
                float angleToEnemy = Vector3.Angle(transform.forward, directionToEnemy);

                if (angleToEnemy < FOV / 2)
                {
                    RaycastHit hit;
                    if (Physics.Raycast(transform.position, directionToEnemy, out hit, attackRange, obstacleLayerMask))
                    {
                        if (hit.transform == enemy)
                        {
                            // 敌人在视野范围内、攻击范围内且没有障碍物
                            // 可以攻击敌人
                        }
                    }
                }
            }));
        }

        Task.WhenAll(tasks).ContinueWith(t =>
        {
            // 所有检测任务完成后的处理逻辑
        });
    }
}

13. 使用数据驱动设计

数据驱动设计可以使游戏逻辑更加灵活和可扩展。可以将敌人检测的参数(如FOV、攻击范围等)存储在数据文件中,并在运行时加载这些数据。

数据驱动示例

创建一个配置文件来存储检测参数:

{
    "FOV": 90,
    "attackRange": 10
}

然后,在玩家检测脚本中加载这些参数:

using System.IO;
using UnityEngine;

public class PlayerDetection : MonoBehaviour
{
    public float FOV;
    public float attackRange;
    public LayerMask enemyLayerMask;
    public LayerMask obstacleLayerMask;

    private void Start()
    {
        LoadDetectionParameters();
    }

    void LoadDetectionParameters()
    {
        string json = File.ReadAllText("path/to/config.json");
        DetectionParameters parameters = JsonUtility.FromJson<DetectionParameters>(json);
        FOV = parameters.FOV;
        attackRange = parameters.attackRange;
    }

    [System.Serializable]
    public class DetectionParameters
    {
        public float FOV;
        public float attackRange;
    }

    void DetectEnemies()
    {
        Collider[] enemiesInRange = Physics.OverlapSphere(transform.position, attackRange, enemyLayerMask);
        foreach (var enemyCollider in enemiesInRange)
        {
            Transform enemy = enemyCollider.transform;
            Vector3 directionToEnemy = (enemy.position - transform.position).normalized;
            float angleToEnemy = Vector3.Angle(transform.forward, directionToEnemy);

            if (angleToEnemy < FOV / 2)
            {
                RaycastHit hit;
                if (Physics.Raycast(transform.position, directionToEnemy, out hit, attackRange, obstacleLayerMask))
                {
                    if (hit.transform == enemy)
                    {
                        // 敌人在视野范围内、攻击范围内且没有障碍物
                        // 可以攻击敌人
                    }
                }
            }
        }
    }
}

14. 使用AI感知系统(如Unity ML-Agents)

如果你的游戏需要更复杂的AI感知系统,可以考虑使用Unity的ML-Agents或其他第三方AI感知系统。这些系统可以提供更高级的感知和决策功能。

Unity ML-Agents示例

使用Unity ML-Agents来训练一个AI代理,使其能够更智能地检测和攻击敌人。

using Unity.MLAgents;
using Unity.MLAgents.Sensors;
using UnityEngine;

public class PlayerAgent : Agent
{
    public float FOV = 90f;
    public float attackRange = 10f;
    public LayerMask enemyLayerMask;
    public LayerMask obstacleLayerMask;

    public override void OnEpisodeBegin()
    {
        // 初始化环境
    }

    public override void CollectObservations(VectorSensor sensor)
    {
        // 收集环境观察
    }

    public override void OnActionReceived(float[] vectorAction)
    {
        // 执行动作
        DetectEnemies();
    }

    void DetectEnemies()
    {
        Collider[] enemiesInRange = Physics.OverlapSphere(transform.position, attackRange, enemyLayerMask);
        foreach (var enemyCollider in enemiesInRange)
        {
            Transform enemy = enemyCollider.transform;
            Vector3 directionToEnemy = (enemy.position - transform.position).normalized;
            float angleToEnemy = Vector3.Angle(transform.forward, directionToEnemy);

            if (angleToEnemy < FOV / 2)
            {
                RaycastHit hit;
                if (Physics.Raycast(transform.position, directionToEnemy, out hit, attackRange, obstacleLayerMask))
                {
                    if (hit.transform == enemy)
                    {
                        // 敌人在视野范围内、攻击范围内且没有障碍物
                        // 可以攻击敌人
                    }
                }
            }
        }
    }
}

通过这些优化和扩展,可以进一步提高玩家检测可攻击敌人的效率和功能性,从而提升游戏的整体性能和体验。

好的,我们可以继续探讨更多的优化和扩展方法,以进一步提升玩家检测可攻击敌人的效率和功能性。

15. 使用层级剔除(Layer Culling)

在大型场景中,可以使用层级剔除来减少不必要的检测。通过将不同类型的对象分配到不同的层,并在检测时只考虑特定层的对象,可以显著提高性能。

层级剔除示例

首先,确保你的敌人和障碍物被分配到不同的层。然后,在检测时只考虑这些层。

public class PlayerDetection : MonoBehaviour
{
    public float FOV = 90f;
    public float attackRange = 10f;
    public LayerMask enemyLayerMask;
    public LayerMask obstacleLayerMask;

    void DetectEnemies()
    {
        Collider[] enemiesInRange = Physics.OverlapSphere(transform.position, attackRange, enemyLayerMask);
        foreach (var enemyCollider in enemiesInRange)
        {
            Transform enemy = enemyCollider.transform;
            Vector3 directionToEnemy = (enemy.position - transform.position).normalized;
            float angleToEnemy = Vector3.Angle(transform.forward, directionToEnemy);

            if (angleToEnemy < FOV / 2)
            {
                RaycastHit hit;
                if (Physics.Raycast(transform.position, directionToEnemy, out hit, attackRange, obstacleLayerMask))
                {
                    if (hit.transform == enemy)
                    {
                        // 敌人在视野范围内、攻击范围内且没有障碍物
                        // 可以攻击敌人
                    }
                }
            }
        }
    }
}

16. 使用八叉树(Octree)或四叉树(Quadtree)

在大型3D场景中,可以使用八叉树或四叉树来组织和管理空间中的对象。这种空间分割技术可以显著提高检测效率。

八叉树示例

首先,创建一个简单的八叉树结构:

public class Octree<T>
{
    private class Node
    {
        public Bounds bounds;
        public List<T> objects;
        public Node[] children;

        public Node(Bounds bounds)
        {
            this.bounds = bounds;
            objects = new List<T>();
            children = new Node[8];
        }
    }

    private Node root;

    public Octree(Bounds bounds)
    {
        root = new Node(bounds);
    }

    public void Insert(T obj, Vector3 position)
    {
        Insert(root, obj, position);
    }

    private void Insert(Node node, T obj, Vector3 position)
    {
        if (!node.bounds.Contains(position))
            return;

        if (node.objects.Count < 8 || node.bounds.size.magnitude < 1)
        {
            node.objects.Add(obj);
        }
        else
        {
            if (node.children[0] == null)
                Subdivide(node);

            for (int i = 0; i < 8; i++)
            {
                Insert(node.children[i], obj, position);
            }
        }
    }

    private void Subdivide(Node node)
    {
        Vector3 size = node.bounds.size / 2;
        Vector3 center = node.bounds.center;

        node.children[0] = new Node(new Bounds(center + new Vector3(-size.x, size.y, -size.z) / 2, size));
        node.children[1] = new Node(new Bounds(center + new Vector3(size.x, size.y, -size.z) / 2, size));
        node.children[2] = new Node(new Bounds(center + new Vector3(-size.x, size.y, size.z) / 2, size));
        node.children[3] = new Node(new Bounds(center + new Vector3(size.x, size.y, size.z) / 2, size));
        node.children[4] = new Node(new Bounds(center + new Vector3(-size.x, -size.y, -size.z) / 2, size));
        node.children[5] = new Node(new Bounds(center + new Vector3(size.x, -size.y, -size.z) / 2, size));
        node.children[6] = new Node(new Bounds(center + new Vector3(-size.x, -size.y, size.z) / 2, size));
        node.children[7] = new Node(new Bounds(center + new Vector3(size.x, -size.y, size.z) / 2, size));
    }

    public List<T> Query(Bounds bounds)
    {
        return Query(root, bounds);
    }

    private List<T> Query(Node node, Bounds bounds)
    {
        List<T> result = new List<T>();

        if (!node.bounds.Intersects(bounds))
            return result;

        foreach (var obj in node.objects)
        {
            result.Add(obj);
        }

        if (node.children[0] != null)
        {
            for (int i = 0; i < 8; i++)
            {
                result.AddRange(Query(node.children[i], bounds));
            }
        }

        return result;
    }
}

然后,在玩家检测脚本中使用八叉树:

public class PlayerDetection : MonoBehaviour
{
    public float FOV = 90f;
    public float attackRange = 10f;
    public LayerMask enemyLayerMask;
    public LayerMask obstacleLayerMask;
    private Octree<Transform> octree;

    private void Start()
    {
        Bounds bounds = new Bounds(Vector3.zero, new Vector3(100, 100, 100));
        octree = new Octree<Transform>(bounds);

        // 初始化八叉树,插入敌人对象
        foreach (var enemy in FindObjectsOfType<Enemy>())
        {
            octree.Insert(enemy.transform, enemy.transform.position);
        }
    }

    void DetectEnemies()
    {
        Bounds detectionBounds = new Bounds(transform.position, new Vector3(attackRange * 2, attackRange * 2, attackRange * 2));
        List<Transform> enemiesInRange = octree.Query(detectionBounds);

        foreach (var enemy in enemiesInRange)
        {
            Vector3 directionToEnemy = (enemy.position - transform.position).normalized;
            float angleToEnemy = Vector3.Angle(transform.forward, directionToEnemy);

            if (angleToEnemy < FOV / 2)
            {
                RaycastHit hit;
                if (Physics.Raycast(transform.position, directionToEnemy, out hit, attackRange, obstacleLayerMask))
                {
                    if (hit.transform == enemy)
                    {
                        // 敌人在视野范围内、攻击范围内且没有障碍物
                        // 可以攻击敌人
                    }
                }
            }
        }
    }
}

17. 使用GPU加速

在某些情况下,可以利用GPU来加速计算。Unity提供了Compute Shader和Job System等工具,可以用来在GPU上执行并行计算。

Compute Shader示例

首先,编写一个Compute Shader来处理敌人检测:

#pragma kernel CSMain

RWStructuredBuffer<float3> enemyPositions;
RWStructuredBuffer<int> results;

float3 playerPosition;
float3 playerForward;
float FOV;
float attackRange;

[numthreads(1, 1, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
    float3 enemyPosition = enemyPositions[id.x];
    float3 directionToEnemy = normalize(enemyPosition - playerPosition);
    float angleToEnemy = degrees(acos(dot(playerForward, directionToEnemy)));

    if (angleToEnemy < FOV / 2 && distance(playerPosition, enemyPosition) < attackRange)
    {
        results[id.x] = 1;
    }
    else
    {
        results[id.x] = 0;
    }
}

然后,在玩家检测脚本中使用Compute Shader:

using UnityEngine;

public class PlayerDetection : MonoBehaviour
{
    public float FOV = 90f;
    public float attackRange = 10f;
    public LayerMask enemyLayerMask;
    public LayerMask obstacleLayerMask;
    public ComputeShader computeShader;

    private Transform[] enemies;
    private ComputeBuffer enemyPositionsBuffer;
    private ComputeBuffer resultsBuffer;
    private int[] results;

    private void Start()
    {
        enemies = FindObjectsOfType<Enemy>().Select(e => e.transform).ToArray();
        enemyPositionsBuffer = new ComputeBuffer(enemies.Length, sizeof(float) * 3);
        resultsBuffer = new ComputeBuffer(enemies.Length, sizeof(int));
        results = new int[enemies.Length];
    }

    void DetectEnemies()
    {
        Vector3[] enemyPositions = enemies.Select(e => e.position).ToArray();
        enemyPositionsBuffer.SetData(enemyPositions);

        computeShader.SetBuffer(0, "enemyPositions", enemyPositionsBuffer);
        computeShader.SetBuffer(0, "results", resultsBuffer);
        computeShader.SetVector("playerPosition", transform.position);
        computeShader.SetVector("playerForward", transform.forward);
        computeShader.SetFloat("FOV", FOV);
        computeShader.SetFloat("attackRange", attackRange);

        computeShader.Dispatch(0, enemies.Length, 1, 1);

        resultsBuffer.GetData(results);

        for (int i = 0; i < results.Length; i++)
        {
            if (results[i] == 1)
            {
                Transform enemy = enemies[i];
                RaycastHit hit;
                Vector3 directionToEnemy = (enemy.position - transform.position).normalized;
                if (Physics.Raycast(transform.position, directionToEnemy, out hit, attackRange, obstacleLayerMask))
                {
                    if (hit.transform == enemy)
                    {
                        // 敌人在视野范围内、攻击范围内且没有障碍物
                        // 可以攻击敌人
                    }
                }
            }
        }
    }

    private void OnDestroy()
    {
        enemyPositionsBuffer.Release();
        resultsBuffer.Release();
    }
}

18. 使用行为树(Behavior Tree)

行为树是一种常用的AI决策工具,可以帮助管理复杂的AI行为。通过使用行为树,可以更清晰地定义和管理敌人检测和攻击逻辑。

行为树示例

首先,定义一个简单的行为树节点:

public abstract class BehaviorNode
{
    public abstract bool Execute();
}

public class SequenceNode : BehaviorNode
{
    private List<BehaviorNode> children = new List<BehaviorNode>();

    public void AddChild(BehaviorNode child)
    {
        children.Add(child);
    }

    public override bool Execute()
    {
        foreach (var child in children)
        {
            if (!child.Execute())
            {
                return false;
            }
        }
        return true;
    }
}

public class DetectEnemyNode : BehaviorNode
{
    private PlayerDetection playerDetection;

    public DetectEnemyNode(PlayerDetection playerDetection)
    {
        this.playerDetection = playerDetection;
    }

    public override bool Execute()
    {
        return playerDetection.DetectEnemies();
    }
}

然后,在玩家检测脚本中使用行为树:

public class PlayerDetection : MonoBehaviour
{
    public float FOV = 90f;
    public float attackRange = 10f;
    public LayerMask enemyLayerMask;
    public LayerMask obstacleLayerMask;

    private BehaviorNode behaviorTree;

    private void Start()
    {
        behaviorTree = new SequenceNode();
        ((SequenceNode)behaviorTree).AddChild(new DetectEnemyNode(this));
        // 添加更多行为节点
    }

    private void Update()
    {
        behaviorTree.Execute();
    }

    public bool DetectEnemies()
    {
        Collider[] enemiesInRange = Physics.OverlapSphere(transform.position, attackRange, enemyLayerMask);
        foreach (var enemyCollider in enemiesInRange)
        {
            Transform enemy = enemyCollider.transform;
            Vector3 directionToEnemy = (enemy.position - transform.position).normalized;
            float angleToEnemy = Vector3.Angle(transform.forward, directionToEnemy);

            if (angleToEnemy < FOV / 2)
            {
                RaycastHit hit;
                if (Physics.Raycast(transform.position, directionToEnemy, out hit, attackRange, obstacleLayerMask))
                {
                    if (hit.transform == enemy)
                    {
                        // 敌人在视野范围内、攻击范围内且没有障碍物
                        // 可以攻击敌人
                        return true;
                    }
                }
            }
        }
        return false;
    }
}

通过这些优化和扩展,可以进一步提高玩家检测可攻击敌人的效率和功能性,从而提升游戏的整体性能和体验。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

牛掰是怎么形成的

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值