using System.Collections.Generic;
using UnityEngine;
namespace BaseGames.Enemies.Perception
{
///
/// 场景级 Sight 槽位批量调度器(Phase 3 性能优化)。
///
/// 设计意图:
/// Sight 槽位内置多点 LOS 射线检测,当场景中存在大量敌人时(10+ 个),
/// 每帧同步刷新所有 Sight 槽位会产生明显的 CPU 峰值。
/// 本组件将全局 Sight 更新量限制在 个/帧,
/// 通过轮询方式公平分配每帧的 Sight 预算,把瞬时峰值摊平到多帧。
///
/// 使用方式:
/// 1. 在场景中添加一个空 GameObject,挂载本组件(建议放在 Managers 节点下)。
/// 2. 本组件自动在 DefaultExecutionOrder(-50) 执行,比默认 PhysicsPerceptionSystem 早。
/// 3. 当本组件存在时,PhysicsPerceptionSystem.FixedUpdate() 会跳过所有 Sight 槽位的更新,
/// 由本组件统一调度(优雅降级:本组件不存在时 Sight 每帧正常更新)。
///
/// 性能参考(测试用):
/// • maxSystemsPerFrame = 4,60fps,20 个敌人 → 平均每帧 4 × losRayCount 次射线,
/// 每个敌人 Sight 刷新周期约 5 帧(~83ms),适合慢速视线感应。
/// • 增大此值可减少延迟,减小此值可进一步降低每帧开销。
///
[AddComponentMenu("BaseGames/Enemies/Sight Batch System")]
[DefaultExecutionOrder(-50)]
public sealed class SightBatchSystem : MonoBehaviour
{
// ── 单例 ──────────────────────────────────────────────────────────────
public static SightBatchSystem Instance { get; private set; }
// ── 配置 ──────────────────────────────────────────────────────────────
[Min(1)]
[Tooltip("每帧最多更新多少个 PhysicsPerceptionSystem 的 Sight 槽位。\n" +
"建议值:场景敌人数 / 5(使平均刷新延迟 ≈ 5 帧)。\n" +
"值越大 = 延迟越低但 CPU 峰值越高;值越小反之。")]
[SerializeField] private int maxSystemsPerFrame = 4;
// ── 内部状态 ──────────────────────────────────────────────────────────
private readonly List _registrants =
new List(32);
private int _offset;
// ── 单例生命周期 ──────────────────────────────────────────────────────
private void Awake()
{
if (Instance != null && Instance != this)
{
Debug.LogWarning("[SightBatchSystem] 场景中存在多个 SightBatchSystem,将销毁多余实例。", this);
Destroy(gameObject);
return;
}
Instance = this;
}
private void OnDestroy()
{
if (Instance == this) Instance = null;
}
// ── 注册 / 注销 ───────────────────────────────────────────────────────
/// PhysicsPerceptionSystem.Awake() 自动调用。
public void Register(PhysicsPerceptionSystem system)
{
if (system == null || _registrants.Contains(system)) return;
_registrants.Add(system);
}
/// PhysicsPerceptionSystem.OnDestroy() 自动调用。O(1) 交换删除。
public void Unregister(PhysicsPerceptionSystem system)
{
int idx = _registrants.IndexOf(system);
if (idx < 0) return;
int last = _registrants.Count - 1;
_registrants[idx] = _registrants[last];
_registrants.RemoveAt(last);
// 防止 _offset 越界
if (_offset >= _registrants.Count)
_offset = 0;
}
// ── 调度 ──────────────────────────────────────────────────────────────
private void FixedUpdate()
{
int total = _registrants.Count;
if (total == 0) return;
int budget = Mathf.Min(maxSystemsPerFrame, total);
for (int i = 0; i < budget; i++)
{
int idx = (_offset + i) % total;
_registrants[idx].ExecuteSightSlots();
}
_offset = (_offset + budget) % total;
}
}
}