using UnityEngine; namespace Colosseum.Player { /// /// 보스가 플레이어를 가릴 때 플레이어 외곽선을 표시하는 컨트롤러 /// public class PlayerOcclusionOutline : MonoBehaviour { [Header("Settings")] [Min(0.001f)] [SerializeField] private float outlineWidth = 0.02f; [SerializeField] private Color outlineColor = Color.white; [Min(0.01f)] [SerializeField] private float checkRadius = 0.3f; private Transform cameraTransform; private Transform playerTransform; private SkinnedMeshRenderer originalRenderer; private GameObject outlineHost; private SkinnedMeshRenderer outlineRenderer; private Material outlineMaterial; private bool isShowingOutline; public void Initialize(Transform camera, Transform player) { cameraTransform = camera; playerTransform = player; originalRenderer = player.GetComponentInChildren(); if (originalRenderer == null) return; var shader = Shader.Find("Hidden/Colosseum/Outline"); if (shader == null) { Debug.LogError("[PlayerOcclusionOutline] Shader 'Hidden/Colosseum/Outline' not found"); return; } outlineMaterial = new Material(shader); outlineMaterial.SetFloat("_OutlineWidth", outlineWidth); outlineMaterial.SetColor("_OutlineColor", outlineColor); if (originalRenderer.sharedMesh == null) { Debug.LogError("[PlayerOcclusionOutline] originalRenderer has no sharedMesh"); return; } outlineHost = new GameObject("OutlineRenderer"); outlineHost.transform.SetParent(originalRenderer.transform, false); outlineRenderer = outlineHost.AddComponent(); outlineRenderer.sharedMesh = originalRenderer.sharedMesh; outlineRenderer.bones = originalRenderer.bones; outlineRenderer.rootBone = originalRenderer.rootBone; outlineRenderer.sharedMaterials = new[] { outlineMaterial }; outlineRenderer.updateWhenOffscreen = true; outlineRenderer.enabled = false; Debug.Log($"[PlayerOcclusionOutline] Initialize: outlineRenderer on child {outlineHost.name}"); } private void LateUpdate() { if (cameraTransform == null || playerTransform == null || outlineRenderer == null) return; outlineMaterial.SetFloat("_OutlineWidth", outlineWidth); outlineMaterial.SetColor("_OutlineColor", outlineColor); bool isOccluded = CheckBossOcclusion(); if (isOccluded && !isShowingOutline) { outlineRenderer.enabled = true; isShowingOutline = true; } else if (!isOccluded && isShowingOutline) { outlineRenderer.enabled = false; isShowingOutline = false; } } private bool CheckBossOcclusion() { Vector3 origin = cameraTransform.position; Vector3 direction = playerTransform.position - origin; float distance = direction.magnitude; if (distance < 0.01f) return false; int enemyLayer = LayerMask.GetMask("Enemy"); if (Physics.SphereCast(origin, checkRadius, direction.normalized, out RaycastHit hit, distance, enemyLayer, QueryTriggerInteraction.Ignore)) { return hit.collider.transform.IsChildOf(playerTransform) == false; } return false; } private void OnDestroy() { if (outlineHost != null) Destroy(outlineHost); if (outlineMaterial != null) Destroy(outlineMaterial); } } }