156 lines
4.1 KiB
C#
156 lines
4.1 KiB
C#
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<DamageObstacle, float> lastDamageTimeByObstacle = new();
|
|
private float lastGlobalDamageTime = -999f;
|
|
|
|
private readonly List<InputDevice> hapticDevices = new();
|
|
|
|
private void Awake()
|
|
{
|
|
if (raftHealth == null)
|
|
{
|
|
raftHealth = GetComponentInParent<RaftHealth>();
|
|
}
|
|
|
|
if (raftHealth == null)
|
|
{
|
|
raftHealth = FindFirstObjectByType<RaftHealth>();
|
|
}
|
|
}
|
|
|
|
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<DamageObstacle>();
|
|
|
|
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);
|
|
}
|
|
} |