using System.Collections.Generic; using UnityEngine; using UnityEngine.XR; public class RaftDamageReceiver : MonoBehaviour { [Header("References")] [SerializeField] private RaftHealth raftHealth; [Header("Damage Cooldown")] [SerializeField] private float sameObstacleDamageCooldown = 1.0f; [SerializeField] private float globalDamageCooldown = 0.2f; [Header("Haptic Feedback")] [SerializeField] private bool useHapticFeedback = true; [Range(0f, 1f)] [SerializeField] private float hapticAmplitude = 0.6f; [SerializeField] private float hapticDuration = 0.15f; [Tooltip("체력 데미지 수치에 따라 햅틱 세기를 살짝 키웁니다.")] [SerializeField] private bool scaleHapticByDamage = true; [Header("Debug")] [SerializeField] private bool showDebugLog = true; private readonly Dictionary lastDamageTimeByObstacle = new(); private float lastGlobalDamageTime = -999f; private readonly List hapticDevices = new(); private void Awake() { if (raftHealth == null) { raftHealth = GetComponentInParent(); } if (raftHealth == null) { raftHealth = FindFirstObjectByType(); } } private void OnTriggerEnter(Collider other) { TryDamage(other); } private void OnTriggerStay(Collider other) { TryDamage(other); } private void TryDamage(Collider other) { if (raftHealth == null) { if (showDebugLog) Debug.LogWarning("[RaftDamageReceiver] RaftHealth가 연결되지 않았습니다.", this); return; } DamageObstacle obstacle = other.GetComponentInParent(); if (obstacle == null) return; if (!obstacle.CanDamage) return; if (raftHealth.IsDead) return; float now = Time.time; if (now < lastGlobalDamageTime + globalDamageCooldown) return; if (lastDamageTimeByObstacle.TryGetValue(obstacle, out float lastObstacleTime)) { if (now < lastObstacleTime + sameObstacleDamageCooldown) return; } int damage = obstacle.Damage; if (damage <= 0) return; raftHealth.TakeDamage(damage); lastGlobalDamageTime = now; lastDamageTimeByObstacle[obstacle] = now; PlayHaptic(damage); if (showDebugLog) { Debug.Log($"[RaftDamageReceiver] {obstacle.name} 충돌. 데미지: {damage}"); } } private void PlayHaptic(int damage) { if (!useHapticFeedback) return; float amplitude = hapticAmplitude; if (scaleHapticByDamage) { // 10 데미지 = 기본값, 15 이상 = 조금 더 강하게 float damageScale = Mathf.Clamp(damage / 10f, 1f, 1.5f); amplitude *= damageScale; } amplitude = Mathf.Clamp01(amplitude); SendHapticToDevice(XRNode.LeftHand, amplitude, hapticDuration); SendHapticToDevice(XRNode.RightHand, amplitude, hapticDuration); } private void SendHapticToDevice(XRNode node, float amplitude, float duration) { InputDevice device = InputDevices.GetDeviceAtXRNode(node); if (!device.isValid) { if (showDebugLog) Debug.Log($"[RaftDamageReceiver] {node} 컨트롤러를 찾지 못했습니다."); return; } if (!device.TryGetHapticCapabilities(out HapticCapabilities capabilities)) { if (showDebugLog) Debug.Log($"[RaftDamageReceiver] {node} 햅틱 기능을 확인할 수 없습니다."); return; } if (!capabilities.supportsImpulse) { if (showDebugLog) Debug.Log($"[RaftDamageReceiver] {node} 컨트롤러가 impulse 햅틱을 지원하지 않습니다."); return; } device.SendHapticImpulse(0u, amplitude, duration); } }