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)