diff --git a/Assets/Scripts/Mechanics/GameOptimizer.cs b/Assets/Scripts/Mechanics/GameOptimizer.cs
index 30de1fb..32d5b70 100644
--- a/Assets/Scripts/Mechanics/GameOptimizer.cs
+++ b/Assets/Scripts/Mechanics/GameOptimizer.cs
@@ -16,30 +16,29 @@ namespace Platformer.Mechanics
QualitySettings.vSyncCount = 0;
Time.fixedDeltaTime = 1f / 60f;
- // BT5-Dev #22 — Layer 영역 분리: Player(13) ↔ Enemy(14) 충돌 영구 OFF.
- // IgnoreCollision 영역 instance 의존 X = 모든 Player·Enemy 영역 자동 통과 영역.
+ // BT5-Dev #22 — Player(13) ↔ Enemy(14) 충돌 영구 OFF
Physics2D.IgnoreLayerCollision(13, 14, true);
- Debug.Log($"[BT22-LayerSep] Player(13) ↔ Enemy(14) collision OFF");
+ // BT5-Dev #27 — Player(13) ↔ JumpThrough(8) 충돌 기본 OFF (Raycast 시점만 동적 활성)
+ Physics2D.IgnoreLayerCollision(13, 8, true);
+ Debug.Log($"[BT27-LayerSep] Player(13) ↔ Enemy(14)·JumpThrough(8) collision OFF");
}
///
- /// PD 지시 2026-05-07 — 모든 지형 Collider2D를 One-Way Platform으로 자동 변환.
- /// 위에서만 착지·측면·아래 통과. Player·Enemy·DeathZone·VictoryZone·TokenInstance·Trigger Collider 영역 제외.
+ /// BT5-Dev #27 — PD 제안: Layer 8(JumpThrough) + Raycast 동적 충돌.
+ /// PlatformEffector2D 폐기. 모든 일반 BoxCollider2D를 Layer 8로 변환 → 기본 통과 + Player 발 Raycast 시점만 충돌 활성.
+ /// 제외: Tilemap·Player·Enemy·DeathZone·VictoryZone·TokenInstance·AttackHitbox·Trigger Collider.
///
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
- static void SetupOneWayPlatforms()
+ static void SetupJumpThroughPlatforms()
{
- // BT5-Dev #25 — Tilemap 영역 OneWay 영역 폐기 (모든 Tile 영역 OneWay = 일반 지면 영역도 영역 = 게임 영역 깨짐)
- // 별개 BoxCollider2D 영역 영역 영역 영역 영역 영역 발판 영역 영역 OneWay 영역 영역 적용
- int applied = 0;
- int excludedTrigger = 0, excludedActor = 0, excludedTilemap = 0;
+ int applied = 0, excluded = 0;
var allColliders = Object.FindObjectsByType(FindObjectsSortMode.None);
var appliedNames = new System.Collections.Generic.List();
foreach (var c in allColliders)
{
if (c == null) continue;
- if (c.isTrigger) { excludedTrigger++; continue; }
- if (c.GetComponent() != null) { excludedTilemap++; continue; } // BT25 — Tilemap 영역 영역 영역 일반 충돌 영역
+ if (c.isTrigger) { excluded++; continue; }
+ if (c.GetComponent() != null) { excluded++; continue; }
if (c.GetComponent() != null
|| c.GetComponent() != null
|| c.GetComponent() != null
@@ -47,23 +46,22 @@ namespace Platformer.Mechanics
|| c.GetComponent() != null
|| c.GetComponent() != null)
{
- excludedActor++;
+ excluded++;
continue;
}
- c.usedByEffector = true;
+ // PlatformEffector2D 잔존 영역 제거 (BT25 누적 영향 차단)
var effector = c.GetComponent();
- if (effector == null) effector = c.gameObject.AddComponent();
- effector.useOneWay = true;
- effector.surfaceArc = 180f;
- effector.rotationalOffset = 0f;
- effector.useSideFriction = false;
- effector.useSideBounce = false;
+ if (effector != null) Object.Destroy(effector);
+ c.usedByEffector = false;
+
+ // Layer 8 변환 → IgnoreLayerCollision로 기본 통과
+ c.gameObject.layer = 8;
applied++;
if (appliedNames.Count < 8) appliedNames.Add($"{c.gameObject.name}({c.GetType().Name})");
}
- Debug.Log($"[BT25-OneWay] applied={applied} trigger={excludedTrigger} tilemap={excludedTilemap} actor={excludedActor} total={allColliders.Length}");
- Debug.Log($"[BT25-OneWay] appliedSamples=[{string.Join(", ", appliedNames)}]");
+ Debug.Log($"[BT27-JumpThrough] applied={applied} excluded={excluded} total={allColliders.Length}");
+ Debug.Log($"[BT27-JumpThrough] appliedSamples=[{string.Join(", ", appliedNames)}]");
}
}
}
diff --git a/Assets/Scripts/Mechanics/PlatformDropThrough.cs b/Assets/Scripts/Mechanics/PlatformDropThrough.cs
new file mode 100644
index 0000000..34ae2e8
--- /dev/null
+++ b/Assets/Scripts/Mechanics/PlatformDropThrough.cs
@@ -0,0 +1,75 @@
+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 거리. 0.05~0.15 권장.")]
+ public float footRayDistance = 0.1f;
+
+ Collider2D _self;
+ KinematicObject _ko;
+ Collider2D _activePlatform;
+ int _jumpThroughMask;
+
+ void Awake()
+ {
+ _self = GetComponent();
+ _ko = GetComponent();
+ _jumpThroughMask = 1 << JUMP_THROUGH_LAYER;
+ }
+
+ void Update()
+ {
+ if (_self == null || _ko == null) return;
+
+ // Player 발 위치 (collider 하단 중심)
+ Vector2 footPos = new Vector2(_self.bounds.center.x, _self.bounds.min.y + 0.02f);
+
+ // Layer 8 발판 raycast (footRayDistance 영역만 검사)
+ RaycastHit2D hit = Physics2D.Raycast(footPos, Vector2.down, footRayDistance, _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);
+ }
+ }
+}
diff --git a/Assets/Scripts/Mechanics/PlayerController.cs b/Assets/Scripts/Mechanics/PlayerController.cs
index a8a60c1..73b1591 100644
--- a/Assets/Scripts/Mechanics/PlayerController.cs
+++ b/Assets/Scripts/Mechanics/PlayerController.cs
@@ -82,6 +82,8 @@ namespace Platformer.Mechanics
// PD 지시 2026-05-07 — 동반 컴포넌트 자동 부착 (Inspector 부착 불요)
if (GetComponent() == null) gameObject.AddComponent();
if (GetComponent() == null) gameObject.AddComponent();
+ // BT5-Dev #27 — Layer 8 발판 동적 충돌 (PD 제안 Raycast 방식)
+ if (GetComponent() == null) gameObject.AddComponent();
// PD 지시 2026-05-07 — 사망 시 입력 차단 / 부활 시 입력 복원
if (health != null)