Untitled

mail@pastecode.io avatar
unknown
csharp
a year ago
3.1 kB
4
Indexable
Never
using System.Threading;
using Cysharp.Threading.Tasks;
using HurricaneVR.Framework.Core;
using Sirenix.OdinInspector;
using UniRx;
using UnityEngine;

[DisallowMultipleComponent]
public class GrabbableReturner : MonoBehaviour
{
    [SerializeField] [Required] private HVRGrabbable grabbable;
    [SerializeField] private float returnTime = 1f;
    [SerializeField] private bool autoSetValues = true;
    [SerializeField] [HideIf("autoSetValues")] private Vector3 startPosition;
    [SerializeField] [HideIf("autoSetValues")] private Vector3 startRotation;
    
    private Rigidbody rigidbody;
    private CancellationTokenSource cts;

    private float timer = -1f;

    private void Awake()
    {
        if (autoSetValues)
        {
            startPosition = transform.position;
            startRotation = transform.eulerAngles;
        }
        rigidbody = grabbable.Rigidbody;
        grabbable.HandGrabbed.AsObservable().Subscribe(_ => CancelUpdate()).AddTo(this);
        grabbable.HandFullReleased.AsObservable().Subscribe(_ => SubscribeUpdate()).AddTo(this);
    }

    private void OnEnable()
    {
        SubscribeUpdate();
    }

    private void OnDisable()
    {
        CancelUpdate();
    }

    private void CancelUpdate()
    {
        cts?.Cancel();
        cts = null;
    }

    private void SubscribeUpdate()
    {
        CancelUpdate();
        cts = new CancellationTokenSource();
        var subDelay = Random.Range(0, 72);
        UniTask.DelayFrame(subDelay, cancellationToken: cts.Token)
            .ContinueWith(() => { ScheduledUpdate(cts).SuppressCancellationThrow().Forget(); })
            .SuppressCancellationThrow()
            .Forget();
    }

    private async UniTask ScheduledUpdate(CancellationTokenSource cancellationTokenSource)
    {
        while (true)
        {
            if (cancellationTokenSource.IsCancellationRequested)
                break;

            if (grabbable.IsHandGrabbed)
                break;

            if (transform.hasChanged)
            {
                timer = returnTime;
                await UniTask.NextFrame();
                continue;
            }

            if (timer >= 0f)
            {
                timer -= Time.deltaTime;
                if (timer > 0f || !rigidbody.IsSleeping())
                {
                    await UniTask.NextFrame();
                    continue;
                }
                else
                {
                    ReturnToStartPosition();
                    break;
                }
            }

            await UniTask.NextFrame();
        }
    }

    private void ReturnToStartPosition()
    {
        transform.position = startPosition;
        transform.rotation = Quaternion.Euler(startRotation);
        rigidbody.position = startPosition;
        rigidbody.rotation = Quaternion.Euler(startRotation);
        rigidbody.velocity = Vector3.zero;
        rigidbody.angularVelocity = Vector3.zero;
        rigidbody.Sleep();
    }
}