131 lines
3.5 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|