using System.Collections.Generic; using Unity.XR.CoreUtils; using UnityEngine; using UnityEngine.Animations; using UnityEngine.XR.Interaction.Toolkit; using UnityEngine.XR.Interaction.Toolkit.Interactables; public class RideController : MonoBehaviour { [SerializeField] private Transform _itemRoot; [SerializeField, Min(0f)] private float _settleVelocity = 0.1f; [SerializeField, Min(0f)] private float _settleDuration = 0.3f; private ParentConstraint _parentConstraint; private readonly Dictionary _settleTimer = new(); private void Awake() { _parentConstraint = GetComponent(); if (_parentConstraint.sourceCount == 0 || _parentConstraint.GetSource(0).sourceTransform == null) { var xrOrigin = FindFirstObjectByType(); if (xrOrigin == null) { Debug.LogWarning("RideController: 씬에서 XROrigin을 찾지 못했습니다."); return; } var source = new ConstraintSource { sourceTransform = xrOrigin.transform, weight = 1f }; if (_parentConstraint.sourceCount == 0) _parentConstraint.AddSource(source); else _parentConstraint.SetSource(0, source); } } public void ActiveRide() { _parentConstraint.constraintActive = true; } public void DeactiveRide() { _parentConstraint.constraintActive = false; } public void OnDetectionStay(Collider other) { var rb = other.attachedRigidbody; if (rb == null || rb.isKinematic) return; if (!rb.TryGetComponent(out XRGrabInteractable grab)) return; if (grab.isSelected) { _settleTimer.Remove(rb); return; } if (rb.linearVelocity.magnitude > _settleVelocity) { _settleTimer[rb] = 0f; return; } float elapsed = _settleTimer.GetValueOrDefault(rb, 0f) + Time.deltaTime; if (elapsed >= _settleDuration) { AttachToCart(grab, rb); _settleTimer.Remove(rb); } else { _settleTimer[rb] = elapsed; } } public void OnDetectionExit(Collider other) { var rb = other.attachedRigidbody; if (rb != null) _settleTimer.Remove(rb); } private void AttachToCart(XRGrabInteractable grab, Rigidbody rb) { grab.transform.SetParent(_itemRoot); rb.linearVelocity = Vector3.zero; rb.angularVelocity = Vector3.zero; rb.isKinematic = true; // 잡았다 놓을 때 XRGrabInteractable이 parent를 ItemRoot로 되돌리는 것 방지 grab.retainTransformParent = false; grab.selectEntered.AddListener(OnPickedUpFromCart); } private void OnPickedUpFromCart(SelectEnterEventArgs args) { if (args.interactableObject is not XRGrabInteractable grab) return; grab.transform.SetParent(null); grab.selectEntered.RemoveListener(OnPickedUpFromCart); grab.selectExited.AddListener(OnReleasedAfterPickup); } private void OnReleasedAfterPickup(SelectExitEventArgs args) { if (args.interactableObject is not XRGrabInteractable grab) return; // XRGrabInteractable이 original(kinematic=true)로 복원한 것을 덮어씀 if (grab.TryGetComponent(out Rigidbody rb)) rb.isKinematic = false; grab.selectExited.RemoveListener(OnReleasedAfterPickup); } }