using UnityEngine;
namespace Platformer.Mechanics
{
///
/// BT5-Dev #27 — PD 제안 Layer + Raycast 동적 충돌.
/// Layer 8(JumpThrough) 발판은 기본 통과(IgnoreLayerCollision). Player 하강 + 발 Raycast 시점만 IgnoreCollision(false)로 충돌 활성 → 착지.
/// 점프(상승) 시 IgnoreCollision(true) 복구 → 다시 통과.
/// PlayerController.Awake에서 자동 부착.
///
[RequireComponent(typeof(Collider2D))]
public class PlatformDropThrough : MonoBehaviour
{
const int JUMP_THROUGH_LAYER = 8;
[Tooltip("발 raycast 기본 거리. velocity 영역에 따라 동적 확대.")]
public float footRayDistance = 0.5f;
Collider2D _self;
KinematicObject _ko;
Collider2D _activePlatform;
int _jumpThroughMask;
void Awake()
{
_self = GetComponent();
_ko = GetComponent();
_jumpThroughMask = 1 << JUMP_THROUGH_LAYER;
Debug.Log($"[BT32-DropThrough] Awake — self={(_self != null ? "OK" : "NULL")} ko={(_ko != null ? "OK" : "NULL")} mask={_jumpThroughMask}");
}
void Start()
{
Debug.Log($"[BT32-DropThrough] Start called");
if (_self == null) return;
Vector2 footPos = new Vector2(_self.bounds.center.x, _self.bounds.min.y + 0.02f);
// BT32 — 거리 무한대 = Player 시작 위치 영역 발판 영역 영역 식별
RaycastHit2D startHit = Physics2D.Raycast(footPos, Vector2.down, Mathf.Infinity, _jumpThroughMask);
if (startHit.collider != null)
{
Physics2D.IgnoreCollision(_self, startHit.collider, false);
_activePlatform = startHit.collider;
Debug.Log($"[BT32-StartHit] dist={startHit.distance:F2} platform='{startHit.collider.gameObject.name}' layer={startHit.collider.gameObject.layer}");
}
else
{
Debug.Log($"[BT32-StartHit] no Layer 8 platform below footPos={footPos} (mask={_jumpThroughMask})");
// 마지막 수단 — 모든 Layer raycast해서 가장 가까운 영역 식별
RaycastHit2D anyHit = Physics2D.Raycast(footPos, Vector2.down, Mathf.Infinity);
if (anyHit.collider != null)
Debug.Log($"[BT32-AnyHit] anyLayer dist={anyHit.distance:F2} platform='{anyHit.collider.gameObject.name}' layer={anyHit.collider.gameObject.layer}");
else
Debug.Log($"[BT32-AnyHit] NOTHING below player");
}
}
void Update()
{
if (_self == null || _ko == null) return;
// Player 발 위치 (collider 하단 중심)
Vector2 footPos = new Vector2(_self.bounds.center.x, _self.bounds.min.y + 0.02f);
// velocity 기반 동적 거리 (빠른 하강 시 raycast miss 차단)
float dist = Mathf.Max(footRayDistance, Mathf.Abs(_ko.velocity.y) * Time.deltaTime + 0.1f);
RaycastHit2D hit = Physics2D.Raycast(footPos, Vector2.down, dist, _jumpThroughMask);
bool falling = _ko.velocity.y <= 0.01f; // 하강 또는 정지
bool rising = _ko.velocity.y > 0.5f; // 점프 상승
if (rising)
{
// 점프 → 활성 발판 통과 복구
if (_activePlatform != null)
{
Physics2D.IgnoreCollision(_self, _activePlatform, true);
_activePlatform = null;
}
}
else if (falling && hit.collider != null && hit.collider != _activePlatform)
{
// 하강 + 발판 raycast hit → 충돌 활성 (착지)
Physics2D.IgnoreCollision(_self, hit.collider, false);
_activePlatform = hit.collider;
}
else if (falling && hit.collider == null && _activePlatform != null)
{
// 발판 떠남 (낙하) → 활성 해제
Physics2D.IgnoreCollision(_self, _activePlatform, true);
_activePlatform = null;
}
}
void OnDrawGizmosSelected()
{
if (_self == null) return;
Vector2 footPos = new Vector2(_self.bounds.center.x, _self.bounds.min.y + 0.02f);
Gizmos.color = Color.cyan;
Gizmos.DrawLine(footPos, footPos + Vector2.down * footRayDistance);
}
}
}