242 lines
6.3 KiB
C#
242 lines
6.3 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.XR.Interaction.Toolkit.Interactables;
|
|
|
|
public class ClamBiteDetector : MonoBehaviour
|
|
{
|
|
[Header("References")]
|
|
[SerializeField] private ClamOpenClose clam;
|
|
[SerializeField] private RaftHealth health;
|
|
[SerializeField] private MemoryFragmentReset memoryFragment;
|
|
|
|
[Header("Bite Damage")]
|
|
[SerializeField] private int biteDamage = 20;
|
|
|
|
[Header("Bite Zone")]
|
|
[SerializeField] private Collider biteZoneCollider;
|
|
|
|
[Tooltip("조개 미션이 시작되었을 때만 물림 판정을 합니다.")]
|
|
[SerializeField] private bool missionActiveOnStart = false;
|
|
|
|
[Tooltip("조개가 닫히는 동안 이미 한 번 물렸으면 추가 판정을 막습니다.")]
|
|
[SerializeField] private bool biteOncePerClose = true;
|
|
|
|
[Header("Target Detection")]
|
|
[Tooltip("손 오브젝트에 붙일 태그입니다. 태그를 안 쓰면 XRHandMarker로도 판정합니다.")]
|
|
[SerializeField] private string handTag = "PlayerHand";
|
|
|
|
[Tooltip("기억의 조각 태그입니다. 단, 조각은 잡힌 상태일 때만 물림 대상으로 봅니다.")]
|
|
[SerializeField] private string fragmentTag = "MemoryFragment";
|
|
|
|
[Tooltip("기억의 조각은 플레이어가 잡고 있을 때만 물림 판정합니다.")]
|
|
[SerializeField] private bool biteFragmentOnlyWhenGrabbed = true;
|
|
|
|
[Header("Debug")]
|
|
[SerializeField] private bool showDebugLog = true;
|
|
|
|
private bool missionActive;
|
|
private bool hasBittenThisClose;
|
|
|
|
private readonly HashSet<Collider> collidersInside = new();
|
|
|
|
private void Awake()
|
|
{
|
|
if (biteZoneCollider == null)
|
|
biteZoneCollider = GetComponent<Collider>();
|
|
|
|
if (biteZoneCollider != null)
|
|
{
|
|
biteZoneCollider.isTrigger = true;
|
|
biteZoneCollider.enabled = false;
|
|
}
|
|
|
|
if (clam == null)
|
|
clam = GetComponentInParent<ClamOpenClose>();
|
|
|
|
if (health == null)
|
|
health = FindFirstObjectByType<RaftHealth>();
|
|
|
|
if (memoryFragment == null)
|
|
memoryFragment = FindFirstObjectByType<MemoryFragmentReset>();
|
|
|
|
missionActive = missionActiveOnStart;
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
if (clam != null)
|
|
{
|
|
clam.onCloseStarted.AddListener(EnableBiteWindow);
|
|
clam.onClosed.AddListener(DisableBiteWindow);
|
|
clam.onOpened.AddListener(ResetBiteState);
|
|
}
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
if (clam != null)
|
|
{
|
|
clam.onCloseStarted.RemoveListener(EnableBiteWindow);
|
|
clam.onClosed.RemoveListener(DisableBiteWindow);
|
|
clam.onOpened.RemoveListener(ResetBiteState);
|
|
}
|
|
}
|
|
|
|
private void OnTriggerEnter(Collider other)
|
|
{
|
|
collidersInside.Add(other);
|
|
|
|
if (IsBiteWindowOpen())
|
|
TryBite(other);
|
|
}
|
|
|
|
private void OnTriggerStay(Collider other)
|
|
{
|
|
collidersInside.Add(other);
|
|
|
|
if (IsBiteWindowOpen())
|
|
TryBite(other);
|
|
}
|
|
|
|
private void OnTriggerExit(Collider other)
|
|
{
|
|
collidersInside.Remove(other);
|
|
}
|
|
|
|
public void StartBiteMission()
|
|
{
|
|
missionActive = true;
|
|
hasBittenThisClose = false;
|
|
collidersInside.Clear();
|
|
|
|
if (showDebugLog)
|
|
Debug.Log("[ClamBiteDetector] 조개 미션 시작. 물림 판정 활성 준비.", this);
|
|
}
|
|
|
|
public void StopBiteMission()
|
|
{
|
|
missionActive = false;
|
|
hasBittenThisClose = false;
|
|
collidersInside.Clear();
|
|
|
|
if (biteZoneCollider != null)
|
|
biteZoneCollider.enabled = false;
|
|
|
|
if (showDebugLog)
|
|
Debug.Log("[ClamBiteDetector] 조개 미션 정지. 물림 판정 비활성.", this);
|
|
}
|
|
|
|
private void EnableBiteWindow()
|
|
{
|
|
if (!missionActive)
|
|
return;
|
|
|
|
hasBittenThisClose = false;
|
|
|
|
if (biteZoneCollider != null)
|
|
biteZoneCollider.enabled = true;
|
|
|
|
if (showDebugLog)
|
|
Debug.Log("[ClamBiteDetector] 조개 물림 판정 ON", this);
|
|
|
|
foreach (Collider col in collidersInside)
|
|
{
|
|
if (col != null)
|
|
TryBite(col);
|
|
}
|
|
}
|
|
|
|
private void DisableBiteWindow()
|
|
{
|
|
if (biteZoneCollider != null)
|
|
biteZoneCollider.enabled = false;
|
|
|
|
collidersInside.Clear();
|
|
|
|
if (showDebugLog && missionActive)
|
|
Debug.Log("[ClamBiteDetector] 조개 물림 판정 OFF", this);
|
|
}
|
|
|
|
private void ResetBiteState()
|
|
{
|
|
hasBittenThisClose = false;
|
|
}
|
|
|
|
private bool IsBiteWindowOpen()
|
|
{
|
|
if (!missionActive)
|
|
return false;
|
|
|
|
if (biteZoneCollider == null)
|
|
return false;
|
|
|
|
return biteZoneCollider.enabled;
|
|
}
|
|
|
|
private void TryBite(Collider other)
|
|
{
|
|
if (other == null)
|
|
return;
|
|
|
|
if (!missionActive)
|
|
return;
|
|
|
|
if (biteOncePerClose && hasBittenThisClose)
|
|
return;
|
|
|
|
bool isHand = IsHandCollider(other);
|
|
bool isGrabbedFragment = IsGrabbedMemoryFragment(other);
|
|
|
|
if (!isHand && !isGrabbedFragment)
|
|
return;
|
|
|
|
hasBittenThisClose = true;
|
|
|
|
if (health != null)
|
|
health.TakeDamage(biteDamage);
|
|
|
|
if (memoryFragment != null)
|
|
memoryFragment.ResetFragment();
|
|
|
|
if (showDebugLog)
|
|
{
|
|
Debug.Log($"[ClamBiteDetector] 조개에게 물림. 데미지 {biteDamage}, 기억의 조각 리셋", this);
|
|
}
|
|
}
|
|
|
|
private bool IsHandCollider(Collider other)
|
|
{
|
|
if (other.CompareTag(handTag))
|
|
return true;
|
|
|
|
XRHandMarker marker = other.GetComponentInParent<XRHandMarker>();
|
|
|
|
return marker != null;
|
|
}
|
|
|
|
private bool IsGrabbedMemoryFragment(Collider other)
|
|
{
|
|
MemoryFragmentReset fragment = other.GetComponentInParent<MemoryFragmentReset>();
|
|
|
|
if (fragment == null)
|
|
{
|
|
if (!other.CompareTag(fragmentTag))
|
|
return false;
|
|
|
|
fragment = memoryFragment;
|
|
}
|
|
|
|
if (fragment == null)
|
|
return false;
|
|
|
|
XRGrabInteractable grab = fragment.GetComponent<XRGrabInteractable>();
|
|
|
|
if (grab == null)
|
|
return !biteFragmentOnlyWhenGrabbed;
|
|
|
|
if (biteFragmentOnlyWhenGrabbed)
|
|
return grab.isSelected;
|
|
|
|
return true;
|
|
}
|
|
} |