using System.Collections.Generic; using UnityEngine; using QualityLevel = HighlightPlus.QualityLevel; using HighlightPlus; namespace DDD { public enum InteractionOutlineType { None, Focused, Available, Unavailable, Objective } public struct InteractionOutlineData { public Color Color; public float Width; public float Opacity; } [RequireComponent(typeof(HighlightEffect))] [RequireComponent(typeof(RestaurantInteractionComponent))] [AddComponentMenu("DDD/Interaction/InteractableHighlight")] public class InteractableHighlight : MonoBehaviour { public static Dictionary OutlineData = new() { {InteractionOutlineType.Available, new InteractionOutlineData() {Color = Color.white, Width = 0.5f, Opacity = 1f}}, {InteractionOutlineType.Focused, new InteractionOutlineData() {Color = Color.yellow, Width = 0.5f, Opacity = 1f}}, {InteractionOutlineType.Unavailable, new InteractionOutlineData() {Color = Color.gray, Width = 0.5f, Opacity = 1f}}, {InteractionOutlineType.Objective, new InteractionOutlineData() {Color = Color.cyan, Width = 0.5f, Opacity = 1f}}, {InteractionOutlineType.None, new InteractionOutlineData() {Color = Color.clear, Width = 0.5f, Opacity = 0f}} }; private float OpacityMultiply = 1.0f; private HighlightEffect highlight; private RestaurantInteractionComponent interaction; private void Awake() { // Cache HighlightEffect highlight = GetComponent(); interaction = GetComponent(); // highlightEffect에 alphaCutoff, constantWidth, combineMeshes, outlineQuality, outlineIndependent 등의 필수 옵션이 켜져있는지 확인 highlight.alphaCutOff = 0.5f; highlight.combineMeshes = true; highlight.constantWidth = true; highlight.outlineQuality = QualityLevel.Highest; highlight.outlineIndependent = true; highlight.outlineBlurPasses = 1; highlight.outlineSharpness = 8; } private void Update() { var currentType = GetCurrentOutlineType(); ApplyOutlineType(currentType); } private InteractionOutlineType GetCurrentOutlineType() { // interaction이 null이거나 컴포넌트가 비활성화된 경우 if (interaction == null || !interaction.enabled) return InteractionOutlineType.None; // IInteractable 인터페이스로 캐스팅하여 상태 확인 var interactable = interaction as IInteractable; if (interactable == null) return InteractionOutlineType.None; try { // 상호작용 불가능한 경우 if (!interactable.CanInteract()) return InteractionOutlineType.Unavailable; // TODO: 여기에 추가 상태 로직을 구현 // - isHovered, isFocused 등의 상태를 체크 // - isObjective 등의 퀘스트 상태를 체크 // 플레이어가 현재 이 오브젝트를 포커스 중인지 확인 if (IsPlayerFocusing()) { return InteractionOutlineType.Focused; } // 기본적으로 상호작용 가능한 상태 return InteractionOutlineType.Available; } catch { // CanInteract() 호출 중 예외 발생 시 안전하게 처리 return InteractionOutlineType.Unavailable; } } private InteractionOutlineType lastAppliedType = InteractionOutlineType.None; private void ApplyOutlineType(InteractionOutlineType type) { // 같은 타입이면 불필요한 프로퍼티 세팅을 피함 if (lastAppliedType == type) return; lastAppliedType = type; if (highlight == null) return; // OutlineData에서 해당 타입의 스타일 가져오기 if (!OutlineData.TryGetValue(type, out var data)) { // 데이터가 없으면 None 타입 적용 data = OutlineData[InteractionOutlineType.None]; } // HighlightEffect에 적용 if (type == InteractionOutlineType.None) { highlight.highlighted = false; highlight.outline = 0; } else { highlight.highlighted = true; highlight.outlineColor = data.Color; highlight.outlineWidth = data.Width; highlight.outline = data.Opacity * OpacityMultiply; } } private bool IsPlayerFocusing() { // 방법 1: 싱글톤 패턴의 플레이어 매니저 사용 // if (PlayerManager.Instance != null) // { // return PlayerManager.Instance.CurrentFocusTarget == gameObject; // } // 방법 3: 정적 참조를 통한 현재 포커스 대상 확인 // if (InteractionSystem.CurrentFocusedObject == gameObject) // { // return true; // } return false; } } }