using System.Collections; using System.Collections.Generic; using UnityEngine; namespace BlockSpace.Voxels { [DisallowMultipleComponent] public sealed class BrowserButton : MonoBehaviour { private const float ButtonPressCooldown = 0.2f; private const float ButtonShrinkDuration = 0.08f; private const float ButtonSettleDuration = 0.12f; private const float ButtonPressedScale = 0.84f; private const float ButtonOvershootScale = 1.04f; private const string ButtonThockResourcePath = "Thock"; [Header("Button")] [SerializeField] private Collider buttonCollider; [SerializeField] private VRVoxelBrush voxelBrush; [Header("Browser Login")] [SerializeField] private string loginUrl = "https://example.com/login"; [Header("Audio")] [SerializeField] private AudioClip buttonThockClip; private readonly Dictionary buttonBaseScales = new Dictionary(); private readonly Dictionary buttonAnimationCoroutines = new Dictionary(); private bool insidePrevious; private float nextButtonPressTime; private void Awake() { ResolveReferences(); EnsureButtonAudioAssigned(); } private void OnDisable() { StopAllCoroutines(); ResetAllButtonScales(); } private void Update() { ResolveReferences(); HandleButton(); } private void ResolveReferences() { if (buttonCollider == null) { buttonCollider = GetComponent(); } if (voxelBrush == null) { voxelBrush = ((VRVoxelBrush.Instance != null) ? VRVoxelBrush.Instance : Object.FindObjectOfType()); } } private void HandleButton() { bool flag = IsHandInside(buttonCollider, (voxelBrush != null) ? voxelBrush.LeftHandAnchor : null) || IsHandInside(buttonCollider, (voxelBrush != null) ? voxelBrush.RightHandAnchor : null); if (flag && !insidePrevious) { PlayButtonEnterAnimation(buttonCollider); if (Time.unscaledTime >= nextButtonPressTime) { nextButtonPressTime = Time.unscaledTime + 0.2f; PlayButtonClickFeedback(buttonCollider); OpenLoginUrl(); } } insidePrevious = flag; } private void OpenLoginUrl() { if (string.IsNullOrWhiteSpace(loginUrl)) { Debug.LogWarning("[BrowserLoginButton] Login URL is empty."); } else { Application.OpenURL(loginUrl); } } private void PlayButtonEnterAnimation(Collider button) { if (!(button == null)) { Transform transform = button.transform; if (!buttonBaseScales.TryGetValue(transform, out var value)) { value = transform.localScale; buttonBaseScales.Add(transform, value); } if (buttonAnimationCoroutines.TryGetValue(transform, out var value2) && value2 != null) { StopCoroutine(value2); } transform.localScale = value; buttonAnimationCoroutines[transform] = StartCoroutine(AnimateButtonScale(transform, value)); } } private void PlayButtonClickFeedback(Collider button) { if (!(buttonThockClip == null) && !(button == null)) { AudioSource.PlayClipAtPoint(buttonThockClip, button.bounds.center, 1f); } } private IEnumerator AnimateButtonScale(Transform buttonTransform, Vector3 baseScale) { Vector3 pressedScale = baseScale * 0.84f; Vector3 overshootScale = baseScale * 1.04f; float elapsed = 0f; while (elapsed < 0.08f) { if (buttonTransform == null) { yield break; } elapsed += Time.unscaledDeltaTime; float t = Mathf.Clamp01(elapsed / 0.08f); buttonTransform.localScale = Vector3.LerpUnclamped(baseScale, pressedScale, t); yield return null; } elapsed = 0f; while (elapsed < 0.12f) { if (buttonTransform == null) { yield break; } elapsed += Time.unscaledDeltaTime; float num = Mathf.Clamp01(elapsed / 0.12f); float num2 = num * num * (3f - 2f * num); Vector3 localScale = ((num2 < 0.5f) ? Vector3.LerpUnclamped(pressedScale, overshootScale, num2 / 0.5f) : Vector3.LerpUnclamped(overshootScale, baseScale, (num2 - 0.5f) / 0.5f)); buttonTransform.localScale = localScale; yield return null; } if (buttonTransform != null) { buttonTransform.localScale = baseScale; buttonAnimationCoroutines[buttonTransform] = null; } } private void ResetAllButtonScales() { foreach (KeyValuePair buttonBaseScale in buttonBaseScales) { if (buttonBaseScale.Key != null) { buttonBaseScale.Key.localScale = buttonBaseScale.Value; } } buttonAnimationCoroutines.Clear(); } private void EnsureButtonAudioAssigned() { if (buttonThockClip == null) { buttonThockClip = Resources.Load("Thock"); } } private static bool IsHandInside(Collider button, Transform handAnchor) { if (button == null || handAnchor == null) { return false; } Vector3 position = handAnchor.position; return (button.ClosestPoint(position) - position).sqrMagnitude <= 1E-06f; } } }