Files
2026-07-22 05:35:53 +08:00

97 lines
4.3 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
/// <summary>
/// 判定区几何工具:把"音符 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"。
/// </summary>
public static class JudgeZoneGeometry
{
private static bool _lineCaptured;
private static float _lineTopY; // 判定线 collider 世界上边缘
private static float _lineBottomY; // 判定线 collider 世界下边缘
public static bool LineReady => _lineCaptured;
/// <summary>
/// 捕获判定线 collider 的世界 Y 范围(只需一次,判定线静态不动)。
/// 传入任一带 tag "JudgmentLine" 的 Collider2D。
/// </summary>
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;
}
/// <summary>切场景/重开时清空,让新判定线重新捕获。</summary>
public static void Reset()
{
_lineCaptured = false;
}
/// <summary>
/// 从音符自身 Collider 计算它的进入/离开阈值(以 transform.position.y 表示)。
/// halfH = collider 世界半高;offY = collider 中心相对 transform 原点的 Y 偏移(世界)。
/// </summary>
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=falsebounds 不随 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;
}
}