/* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * Licensed under the Oculus SDK License Agreement (the "License"); * you may not use the Oculus SDK except in compliance with the License, * which is provided at the time of installation or download, or which * otherwise accompanies this software in either electronic or hard copy form. * * You may obtain a copy of the License at * * https://developer.oculus.com/licenses/oculussdk/ * * Unless required by applicable law or agreed to in writing, the Oculus SDK * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using UnityEngine; namespace Oculus.Interaction.Samples.PalmMenu { /// /// Example of a bespoke behavior created to control a particular palm menu. This menu primarily controls the swiping behavior, /// showing and hiding various options and controlling the pagination dots depending on the state of the menu. Note that, for /// buttons with several possible icons, the states of those buttons are controlled by the PalmMenuExampleButtonHandlers script, /// which manages the state of the various handlers. /// public class PalmMenuExample : MonoBehaviour { [SerializeField] private PokeInteractable _menuInteractable; [SerializeField] private GameObject _menuParent; [SerializeField] private RectTransform _menuPanel; [SerializeField] private RectTransform[] _buttons; [SerializeField] private RectTransform[] _paginationDots; [SerializeField] private RectTransform _selectionIndicatorDot; [SerializeField] private AnimationCurve _paginationButtonScaleCurve; [SerializeField] private float _defaultButtonDistance = 50f; [SerializeField] private AudioSource _paginationSwipeAudio; [SerializeField] private AudioSource _showMenuAudio; [SerializeField] private AudioSource _hideMenuAudio; private int _currentSelectedButtonIdx; private void Start() { _currentSelectedButtonIdx = CalculateNearestButtonIdx(); _selectionIndicatorDot.position = _paginationDots[_currentSelectedButtonIdx].position; } private void Update() { var nearestButtonIdx = CalculateNearestButtonIdx(); if (nearestButtonIdx != _currentSelectedButtonIdx) { _currentSelectedButtonIdx = nearestButtonIdx; _paginationSwipeAudio.Play(); _selectionIndicatorDot.position = _paginationDots[_currentSelectedButtonIdx].position; } if (_menuInteractable.State != InteractableState.Select) { LerpToButton(); } } private int CalculateNearestButtonIdx() { var nearestButtonIdx = 0; var nearestDistance = float.PositiveInfinity; for (int idx = 0; idx < _buttons.Length; ++idx) { var deltaX = _buttons[idx].localPosition.x + _menuPanel.anchoredPosition.x; var adjacentIdx = deltaX < 0f ? idx + 1 : idx - 1; var distanceX = Mathf.Abs(deltaX); if (distanceX < nearestDistance) { nearestButtonIdx = idx; nearestDistance = distanceX; } var normalizingDistance = _defaultButtonDistance; if (adjacentIdx >= 0 && adjacentIdx < _buttons.Length) { normalizingDistance = Mathf.Abs(_buttons[adjacentIdx].localPosition.x - _buttons[idx].localPosition.x); } var scale = _paginationButtonScaleCurve.Evaluate(distanceX / normalizingDistance); _buttons[idx].localScale = scale * Vector3.one; } return nearestButtonIdx; } private void LerpToButton() { var nearestX = _buttons[0].localPosition.x; var nearestDistance = Mathf.Abs(nearestX + _menuPanel.anchoredPosition.x); for (int idx = 1; idx < _buttons.Length; ++idx) { var x = _buttons[idx].localPosition.x; var distance = Mathf.Abs(x + _menuPanel.anchoredPosition.x); if (distance < nearestDistance) { nearestX = x; nearestDistance = distance; } } const float t = 0.2f; _menuPanel.anchoredPosition = Vector2.Lerp(_menuPanel.anchoredPosition, new Vector2(-nearestX, 0f), t); } /// /// Show/hide the menu. /// public void ToggleMenu() { if (_menuParent.activeSelf) { _hideMenuAudio.Play(); _menuParent.SetActive(false); } else { _showMenuAudio.Play(); _menuParent.SetActive(true); } } } }