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(true); if (grabInteractable == null && !warnedMissingGrabInteractable) { warnedMissingGrabInteractable = true; Debug.LogWarning("[SteeringKeyXR] XRGrabInteractable 참조가 없어 키를 잡아도 조향 이벤트를 받을 수 없습니다.", this); } } }