From c88487ef4c911464f0841dcedf4b09940b444248 Mon Sep 17 00:00:00 2001 From: dal4segno Date: Sat, 4 Apr 2026 08:20:35 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=B9=B4=EB=A9=94=EB=9D=BC-=ED=94=8C?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=96=B4=20=EC=82=AC=EC=9D=B4=20=EC=9E=A5?= =?UTF-8?q?=EC=95=A0=EB=AC=BC=20=EC=88=A8=EA=B9=80=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SphereCast 충돌 당기기 로직 제거로 카메라가 항상 원래 거리 유지 - ObstacleTransparencyController 신규: 카메라-타겟 사이 장애물을 renderer.enabled로 숨김 - 플레이어 발 위치 이하의 바닥/지형은 숨김 대상에서 제외 - 파티클/트레일 렌더러는 숨김 스킵 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../Player/ObstacleTransparencyController.cs | 105 ++++++++++++++++++ .../ObstacleTransparencyController.cs.meta | 2 + Assets/_Game/Scripts/Player/PlayerCamera.cs | 21 ++-- 3 files changed, 114 insertions(+), 14 deletions(-) create mode 100644 Assets/_Game/Scripts/Player/ObstacleTransparencyController.cs create mode 100644 Assets/_Game/Scripts/Player/ObstacleTransparencyController.cs.meta 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);