在Unity射击游戏项目中,玩家检测可攻击的敌人通常涉及以下几个步骤和数学公式:
-
视野检测(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) { // 敌人在视野范围内 }
- 角度检测:首先,确定敌人是否在玩家的视野范围内。假设玩家的视野角度为
-
距离检测:
- 确定敌人是否在玩家的攻击范围内。假设攻击范围为
attackRange
。 - 计算玩家和敌人之间的距离:
float distanceToEnemy = Vector3.Distance(playerPosition, enemyPosition);
- 检查敌人是否在攻击范围内:
if (distanceToEnemy <= attackRange) { // 敌人在攻击范围内 }
- 确定敌人是否在玩家的攻击范围内。假设攻击范围为
-
射线检测(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;
}
}
通过这些优化和扩展,可以进一步提高玩家检测可攻击敌人的效率和功能性,从而提升游戏的整体性能和体验。