feat: Enhance Physics Perception System with new detection modes and performance optimizations
- Updated PhysicsPerceptionSystem to support seven detection modes: RangeCircle, BatchLOS, FanCast, BoxCast, Sight, RayCast, and TriggerZone. - Improved documentation for each detection mode, including performance optimization strategies. - Introduced PerceptionTriggerProxy for event-driven detection in TriggerZone slots. - Added SightBatchSystem to manage Sight slots efficiently, reducing CPU spikes during high enemy counts. - Updated SensorSlotNames to reflect new detection modes and their purposes. - Enhanced internal logic for detecting targets and managing detection events.
This commit is contained in:
@@ -7,7 +7,7 @@ namespace BaseGames.Enemies.AI
|
||||
{
|
||||
/// <summary>
|
||||
/// BD Conditional:玩家是否可见(LOS 检测)。
|
||||
/// 读取 EnemyBase._losResult 字段(由 BatchLOSSystem 每帧写入,或降级 3 帧节流 Raycast)。
|
||||
/// 读取 EnemyBase.IsPlayerVisible(),结果来自 PhysicsPerceptionSystem(LOS / Sight 槽位)。
|
||||
/// </summary>
|
||||
[TaskName("Is Player Visible?")]
|
||||
[TaskCategory("BaseGames/Enemy/Perception")]
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Jobs;
|
||||
using BaseGames.Core;
|
||||
|
||||
namespace BaseGames.Enemies.AI
|
||||
{
|
||||
/// <summary>
|
||||
/// 批量视线检测系统(架构 07_EnemyModule §12)。
|
||||
/// 每 FixedUpdate:
|
||||
/// 1. 读取上一帧 RaycastHit2D 结果,回调各注册者。
|
||||
/// 2. 重新构建本帧 RaycastCommand 批次,使用 Physics2D.RaycastAll 同步模式执行。
|
||||
///
|
||||
/// ⚠️ Unity 2022.3 中 Physics2D 批量命令(RaycastCommand2D)尚未稳定,
|
||||
/// 此实现使用 FixedUpdate 内顺序 Raycast2D(节流),确保零 GC 分配。
|
||||
/// 当敌人数量 > 20 时建议切换到 Job System RaycastCommand。
|
||||
/// </summary>
|
||||
[DefaultExecutionOrder(-200)]
|
||||
public class BatchLOSSystem : MonoBehaviour
|
||||
{
|
||||
[SerializeField, Min(1)] private int _maxRequestersPerFrame = 8;
|
||||
|
||||
private void Awake() => ServiceLocator.Register<BatchLOSSystem>(this);
|
||||
private void OnDestroy() => ServiceLocator.Unregister<BatchLOSSystem>(this);
|
||||
|
||||
private readonly List<ILOSRequester> _requesters = new();
|
||||
private readonly HashSet<ILOSRequester> _requesterSet = new(); // O(1) 包含查询
|
||||
// _indexMap 记录每个 requester 在 _requesters 中的下标,供 Unregister 实现 O(1) 删除
|
||||
private readonly Dictionary<ILOSRequester, int> _indexMap = new();
|
||||
private int _currentOffset = 0;
|
||||
|
||||
// ── 注册 ──────────────────────────────────────────────────────────
|
||||
public void Register(ILOSRequester requester)
|
||||
{
|
||||
if (_requesterSet.Add(requester))
|
||||
{
|
||||
_indexMap[requester] = _requesters.Count;
|
||||
_requesters.Add(requester);
|
||||
}
|
||||
}
|
||||
|
||||
public void Unregister(ILOSRequester requester)
|
||||
{
|
||||
if (!_requesterSet.Remove(requester)) return;
|
||||
|
||||
int idx = _indexMap[requester];
|
||||
int last = _requesters.Count - 1;
|
||||
|
||||
// Swap-and-pop:将末尾元素移动到被删除的位置,避免 O(n) 搬移
|
||||
if (idx != last)
|
||||
{
|
||||
var moved = _requesters[last];
|
||||
_requesters[idx] = moved;
|
||||
_indexMap[moved] = idx;
|
||||
}
|
||||
|
||||
_requesters.RemoveAt(last);
|
||||
_indexMap.Remove(requester);
|
||||
|
||||
// 修正偏移量,防止越界
|
||||
if (_currentOffset >= _requesters.Count) _currentOffset = 0;
|
||||
}
|
||||
|
||||
// ── FixedUpdate ───────────────────────────────────────────────────
|
||||
private void FixedUpdate()
|
||||
{
|
||||
if (_requesters.Count == 0) return;
|
||||
|
||||
// 每帧轮询部分请求者(均匀分配,避免单帧全量射线)
|
||||
int count = Mathf.Min(_maxRequestersPerFrame, _requesters.Count);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int idx = (_currentOffset + i) % _requesters.Count;
|
||||
var requester = _requesters[idx];
|
||||
|
||||
bool hasLOS = false;
|
||||
if (requester != null)
|
||||
{
|
||||
Vector2 origin = requester.LOSOrigin;
|
||||
Vector2 target = requester.LOSTarget;
|
||||
Vector2 direction = target - origin;
|
||||
float distance = direction.magnitude;
|
||||
|
||||
if (distance > 0.01f)
|
||||
{
|
||||
// direction / distance == direction.normalized,避免重复开方
|
||||
var hit = Physics2D.Raycast(origin, direction / distance, distance, requester.LOSBlockingMask);
|
||||
// 若无遮挡物(hit.collider == null),则视线畅通
|
||||
hasLOS = hit.collider == null;
|
||||
}
|
||||
|
||||
|
||||
requester.ReceiveLOSResult(hasLOS);
|
||||
}
|
||||
}
|
||||
|
||||
_currentOffset = (_currentOffset + count) % Mathf.Max(1, _requesters.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a482b11f99a870f4ea28cd36b716a69b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user