97 lines
4.3 KiB
C#
97 lines
4.3 KiB
C#
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=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;
|
||
}
|
||
}
|