- PlayerOcclusionOutline: 보스(Enemy 레이어)가 플레이어를 가릴 때 외곽선 활성화 - Outline.shader: URP 법선 확장 외곽선 셰이더 (Fresnel 기반 알파) - 외곽선용 별도 SkinnedMeshRenderer를 자식 GameObject에 생성 - ObstacleTransparencyController: Enemy 레이어 장애물 숨김 제외 - PlayerCamera: PlayerOcclusionOutline 초기화 연동 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
108 lines
3.3 KiB
C#
108 lines
3.3 KiB
C#
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>();
|
|
int enemyLayer = LayerMask.GetMask("Enemy");
|
|
int mask = occlusionMask & ~enemyLayer;
|
|
RaycastHit[] rayHits = Physics.SphereCastAll(origin, checkRadius, direction, maxDistance, mask, 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();
|
|
}
|
|
}
|
|
}
|