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 = 1.0f; 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) { // BT5-Dev #33 — 활성 해제 = 점프(상승) 시점만. 발판 떠남(낙하)은 활성 유지 (Player 거리 영역 벗어나도 충돌 유지·복구) 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; } // BT33 — falling + hit X 영역 활성 해제 영역 폐기 (raycast 거리 일시 벗어남 영역 통과 복구 차단) } 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); } } }