Files
WhaleAdventure_VR/Assets/02_Scripts/Cave/SteeringKeyXR.cs
2026-06-18 17:47:41 +09:00

131 lines
3.5 KiB
C#

using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.XR.Interaction.Toolkit.Interactables;
public class SteeringKeyXR : MonoBehaviour
{
[Header("References")]
[SerializeField] private XRGrabInteractable grabInteractable;
[SerializeField] private Transform keyPivot;
[Header("Steering")]
[SerializeField] private float maxAngle = 45f;
[Header("Return")]
[SerializeField] private bool returnToCenter = true;
[SerializeField] private float returnSpeed = 5f;
private Transform currentInteractor;
private bool isGrabbed;
private bool warnedMissingGrabInteractable;
private Quaternion initialPivotRotation;
private float currentAngle;
public float SteeringValue
{
get
{
if (Mathf.Approximately(maxAngle, 0f))
return 0f;
return Mathf.Clamp(currentAngle / maxAngle, -1f, 1f);
}
}
private void Awake()
{
if (keyPivot == null)
keyPivot = transform;
ResolveGrabInteractable();
initialPivotRotation = keyPivot.localRotation;
}
private void OnEnable()
{
ResolveGrabInteractable();
if (grabInteractable != null)
{
grabInteractable.selectEntered.AddListener(OnGrab);
grabInteractable.selectExited.AddListener(OnRelease);
}
}
private void OnDisable()
{
if (grabInteractable != null)
{
grabInteractable.selectEntered.RemoveListener(OnGrab);
grabInteractable.selectExited.RemoveListener(OnRelease);
}
}
private void Update()
{
if (isGrabbed && currentInteractor != null)
{
UpdateAngleFromHand();
}
else if (returnToCenter)
{
currentAngle = Mathf.Lerp(currentAngle, 0f, returnSpeed * Time.deltaTime);
ApplyRotation();
}
}
private void OnGrab(SelectEnterEventArgs args)
{
isGrabbed = true;
currentInteractor = args.interactorObject.transform;
}
private void OnRelease(SelectExitEventArgs args)
{
isGrabbed = false;
currentInteractor = null;
}
private void UpdateAngleFromHand()
{
Vector3 worldDir = currentInteractor.position - keyPivot.position;
Vector3 localDir = GetInitialPivotSpaceDirection(worldDir);
float targetAngle = Mathf.Atan2(localDir.x, localDir.z) * Mathf.Rad2Deg;
currentAngle = Mathf.Clamp(targetAngle, -maxAngle, maxAngle);
ApplyRotation();
}
private void ApplyRotation()
{
keyPivot.localRotation =
initialPivotRotation * Quaternion.Euler(0f, currentAngle, 0f);
}
private Vector3 GetInitialPivotSpaceDirection(Vector3 worldDirection)
{
Quaternion referenceRotation = keyPivot.parent != null
? keyPivot.parent.rotation * initialPivotRotation
: initialPivotRotation;
return Quaternion.Inverse(referenceRotation) * worldDirection;
}
private void ResolveGrabInteractable()
{
if (grabInteractable != null)
return;
grabInteractable = GetComponentInChildren<XRGrabInteractable>(true);
if (grabInteractable == null && !warnedMissingGrabInteractable)
{
warnedMissingGrabInteractable = true;
Debug.LogWarning("[SteeringKeyXR] XRGrabInteractable 참조가 없어 키를 잡아도 조향 이벤트를 받을 수 없습니다.", this);
}
}
}