diff --git a/Assets/_Game/Scripts/Player/ObstacleTransparencyController.cs b/Assets/_Game/Scripts/Player/ObstacleTransparencyController.cs
new file mode 100644
index 00000000..e6536005
--- /dev/null
+++ b/Assets/_Game/Scripts/Player/ObstacleTransparencyController.cs
@@ -0,0 +1,105 @@
+using System.Collections.Generic;
+
+using UnityEngine;
+
+namespace Colosseum.Player
+{
+ ///
+ /// 카메라와 타겟 사이의 장애물을 숨기는 컨트롤러
+ ///
+ public class ObstacleTransparencyController : MonoBehaviour
+ {
+ [Header("Occlusion Settings")]
+ [Min(0.01f)] [SerializeField] private float checkRadius = 0.3f;
+
+ private Transform cameraTransform;
+ private Transform targetTransform;
+ private LayerMask occlusionMask;
+ private readonly HashSet hiddenRenderers = new();
+
+ public void Initialize(Transform target, LayerMask mask)
+ {
+ cameraTransform = GetComponent().transform;
+ targetTransform = target;
+ occlusionMask = mask;
+ }
+
+ private void LateUpdate()
+ {
+ if (cameraTransform == null || targetTransform == null) return;
+
+ Vector3 origin = cameraTransform.position;
+ Vector3 direction = targetTransform.position - origin;
+ float maxDistance = direction.magnitude;
+
+ if (maxDistance < 0.01f) return;
+
+ direction.Normalize();
+
+ HashSet currentHits = CollectHits(origin, direction, maxDistance);
+
+ foreach (Renderer renderer in currentHits)
+ {
+ if (!hiddenRenderers.Contains(renderer))
+ {
+ renderer.enabled = false;
+ hiddenRenderers.Add(renderer);
+ }
+ }
+
+ List toShow = null;
+ foreach (Renderer renderer in hiddenRenderers)
+ {
+ if (renderer == null || !currentHits.Contains(renderer))
+ {
+ if (renderer != null)
+ renderer.enabled = true;
+ (toShow ??= new List()).Add(renderer);
+ }
+ }
+
+ if (toShow != null)
+ {
+ foreach (Renderer renderer in toShow)
+ hiddenRenderers.Remove(renderer);
+ }
+ }
+
+ private HashSet CollectHits(Vector3 origin, Vector3 direction, float maxDistance)
+ {
+ var hits = new HashSet();
+ RaycastHit[] rayHits = Physics.SphereCastAll(origin, checkRadius, direction, maxDistance, occlusionMask, QueryTriggerInteraction.Ignore);
+
+ float targetY = targetTransform.position.y;
+
+ foreach (RaycastHit hit in rayHits)
+ {
+ if (hit.point.y <= targetY) continue;
+
+ Renderer renderer = hit.collider.GetComponent();
+ if (renderer == null) continue;
+ if (renderer is ParticleSystemRenderer || renderer is TrailRenderer) continue;
+
+ hits.Add(renderer);
+ }
+
+ return hits;
+ }
+
+ public void ClearHidden()
+ {
+ foreach (Renderer renderer in hiddenRenderers)
+ {
+ if (renderer != null)
+ renderer.enabled = true;
+ }
+
+ hiddenRenderers.Clear();
+ }
+
+ private void OnDestroy()
+ {
+ ClearHidden();
+ }
+ }
+}
diff --git a/Assets/_Game/Scripts/Player/ObstacleTransparencyController.cs.meta b/Assets/_Game/Scripts/Player/ObstacleTransparencyController.cs.meta
new file mode 100644
index 00000000..c7e35de4
--- /dev/null
+++ b/Assets/_Game/Scripts/Player/ObstacleTransparencyController.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 2e94a9a820149c5589b02955f59d465e
\ No newline at end of file
diff --git a/Assets/_Game/Scripts/Player/PlayerCamera.cs b/Assets/_Game/Scripts/Player/PlayerCamera.cs
index fc025a3d..94aafa82 100644
--- a/Assets/_Game/Scripts/Player/PlayerCamera.cs
+++ b/Assets/_Game/Scripts/Player/PlayerCamera.cs
@@ -17,9 +17,7 @@ namespace Colosseum.Player
[SerializeField] private float maxPitch = 60f;
[Header("Collision")]
- [SerializeField] private float collisionRadius = 0.3f;
[SerializeField] private LayerMask collisionMask;
- [SerializeField] private float minDistance = 0.5f;
[SerializeField] private float minHeightAboveGround = 0.3f; // 바닥 위 최소 카메라 높이
[Header("Smooth Distance")]
@@ -87,6 +85,12 @@ namespace Colosseum.Player
currentDistance = distance;
distanceSmoothVelocity = 0f;
+ // 장애물 반투명 컨트롤러 연동
+ var transparencyController = cameraInstance.gameObject.GetComponent();
+ if (transparencyController == null)
+ transparencyController = cameraInstance.gameObject.AddComponent();
+ transparencyController.Initialize(target, collisionMask);
+
// 카메라 위치를 즉시 타겟 위치로 초기화
SnapToTarget();
}
@@ -177,19 +181,8 @@ namespace Colosseum.Player
Vector3 pivot = target.position + Vector3.up * height * 0.5f;
Vector3 desiredPos = target.position + offset;
- // pivot → desiredPos 방향으로 SphereCast해서 지형 충돌 감지
- Vector3 dir = desiredPos - pivot;
- float maxDist = dir.magnitude;
- float targetDistance = distance;
- if (Physics.SphereCast(pivot, collisionRadius, dir.normalized, out RaycastHit hit, maxDist, collisionMask, QueryTriggerInteraction.Ignore))
- {
- // 충돌 지점에서 collisionRadius만큼 pivot 쪽으로 당긴 거리
- targetDistance = Mathf.Max(minDistance, hit.distance - collisionRadius);
- }
-
// 부드러운 거리 보간
- currentDistance = Mathf.SmoothDamp(currentDistance, targetDistance, ref distanceSmoothVelocity, distanceSmoothTime);
- currentDistance = Mathf.Max(minDistance, currentDistance);
+ currentDistance = Mathf.SmoothDamp(currentDistance, distance, ref distanceSmoothVelocity, distanceSmoothTime);
// 보간된 거리로 최종 오프셋 재계산
offset = rotation * new Vector3(0f, 0f, -currentDistance);