using UnityEngine;
///
/// 判定区几何工具:把"音符 Collider 与判定线 Collider 在 Y 轴是否重叠"这一原本由
/// Physics2D 触发器(OnTriggerEnter2D/Exit2D)判断的条件,改为纯几何/时间计算,
/// 用于去掉每帧移动 Collider 触发的 Physics2D 开销,同时保持判定"进入/离开判定区"
/// 的时刻与原物理触发**完全一致**。
///
/// 原理:判定线静态不动(世界 Y 固定),音符沿 -Y 匀速下落。两个 AxisAligned Box 在 Y 轴
/// 重叠 ⇔ 音符 collider 上边缘 ≥ 判定线下边缘 且 音符 collider 下边缘 ≤ 判定线上边缘。
/// 用音符 transform.position.y 表达为一对阈值:
/// 进入(overlap 开始): noteY ≤ lineTop + noteHalfH - noteOffY
/// 离开(overlap 结束): noteY < lineBottom - noteHalfH - noteOffY
/// 这与物理 trigger 的 enter/exit 触发时刻等价(同一 AABB 重叠判据)。
///
/// 判定结果本身仍由 hitTime(时间)决定,本类只复现"何时注册/注销到 TrackKeyManager"。
///
public static class JudgeZoneGeometry
{
private static bool _lineCaptured;
private static float _lineTopY; // 判定线 collider 世界上边缘
private static float _lineBottomY; // 判定线 collider 世界下边缘
public static bool LineReady => _lineCaptured;
///
/// 捕获判定线 collider 的世界 Y 范围(只需一次,判定线静态不动)。
/// 传入任一带 tag "JudgmentLine" 的 Collider2D。
///
public static void CaptureLine(Collider2D lineCollider)
{
if (_lineCaptured || lineCollider == null) return;
Transform t = lineCollider.transform;
if (lineCollider is BoxCollider2D box)
{
// 直接由 size/offset/世界缩放算,避免依赖 bounds 的物理同步时机。
float scaleY = Mathf.Abs(t.lossyScale.y);
float centerY = t.position.y + box.offset.y * scaleY;
float halfH = box.size.y * 0.5f * scaleY;
_lineTopY = centerY + halfH;
_lineBottomY = centerY - halfH;
}
else
{
Physics2D.SyncTransforms();
Bounds b = lineCollider.bounds;
_lineTopY = b.max.y;
_lineBottomY = b.min.y;
}
_lineCaptured = true;
}
/// 切场景/重开时清空,让新判定线重新捕获。
public static void Reset()
{
_lineCaptured = false;
}
///
/// 从音符自身 Collider 计算它的进入/离开阈值(以 transform.position.y 表示)。
/// halfH = collider 世界半高;offY = collider 中心相对 transform 原点的 Y 偏移(世界)。
///
public static bool TryGetThresholds(Collider2D noteCollider, Transform noteTf,
out float enterY, out float exitY)
{
enterY = 0f; exitY = 0f;
if (!_lineCaptured || noteCollider == null || noteTf == null) return false;
float halfH, offY;
// 关键:不能用 Collider2D.bounds——本工程 Physics2D AutoSyncTransforms=0,
// 且音符 Rigidbody2D.simulated=false,bounds 不随 transform 主动重算而滞后。
// 对 hold 的 middle/end 段(被 baseSpawnYOffset 大幅上移)会算出错误的 offY,
// 导致 enterY/exitY 整体错位、中尾段几乎进不了判定区。
// 改为直接用 BoxCollider2D 的 size/offset + 世界缩放计算(与物理同步时机无关)。
if (noteCollider is BoxCollider2D box)
{
float scaleY = Mathf.Abs(noteTf.lossyScale.y);
halfH = box.size.y * 0.5f * scaleY; // collider 世界半高
offY = box.offset.y * scaleY; // collider 中心相对 transform 原点的世界 Y 偏移
}
else
{
// 其它 collider 类型:先强制同步一次再读 bounds,尽量准确。
Physics2D.SyncTransforms();
Bounds b = noteCollider.bounds;
halfH = b.extents.y;
offY = b.center.y - noteTf.position.y;
}
// overlap 开始:音符下边缘(noteY+offY-halfH) ≤ 判定线上边缘。
enterY = _lineTopY + halfH - offY;
// overlap 结束:音符上边缘(noteY+offY+halfH) < 判定线下边缘。
exitY = _lineBottomY - halfH - offY;
return true;
}
}