feat: 카메라-플레이어 사이 장애물 숨김 처리
- SphereCast 충돌 당기기 로직 제거로 카메라가 항상 원래 거리 유지 - ObstacleTransparencyController 신규: 카메라-타겟 사이 장애물을 renderer.enabled로 숨김 - 플레이어 발 위치 이하의 바닥/지형은 숨김 대상에서 제외 - 파티클/트레일 렌더러는 숨김 스킵 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
105
Assets/_Game/Scripts/Player/ObstacleTransparencyController.cs
Normal file
105
Assets/_Game/Scripts/Player/ObstacleTransparencyController.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Colosseum.Player
|
||||
{
|
||||
/// <summary>
|
||||
/// 카메라와 타겟 사이의 장애물을 숨기는 컨트롤러
|
||||
/// </summary>
|
||||
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<Renderer> hiddenRenderers = new();
|
||||
|
||||
public void Initialize(Transform target, LayerMask mask)
|
||||
{
|
||||
cameraTransform = GetComponent<Camera>().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<Renderer> currentHits = CollectHits(origin, direction, maxDistance);
|
||||
|
||||
foreach (Renderer renderer in currentHits)
|
||||
{
|
||||
if (!hiddenRenderers.Contains(renderer))
|
||||
{
|
||||
renderer.enabled = false;
|
||||
hiddenRenderers.Add(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
List<Renderer> toShow = null;
|
||||
foreach (Renderer renderer in hiddenRenderers)
|
||||
{
|
||||
if (renderer == null || !currentHits.Contains(renderer))
|
||||
{
|
||||
if (renderer != null)
|
||||
renderer.enabled = true;
|
||||
(toShow ??= new List<Renderer>()).Add(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
if (toShow != null)
|
||||
{
|
||||
foreach (Renderer renderer in toShow)
|
||||
hiddenRenderers.Remove(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
private HashSet<Renderer> CollectHits(Vector3 origin, Vector3 direction, float maxDistance)
|
||||
{
|
||||
var hits = new HashSet<Renderer>();
|
||||
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<Renderer>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e94a9a820149c5589b02955f59d465e
|
||||
@@ -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<ObstacleTransparencyController>();
|
||||
if (transparencyController == null)
|
||||
transparencyController = cameraInstance.gameObject.AddComponent<ObstacleTransparencyController>();
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user