Initialer Upload neues Unity-Projekt
This commit is contained in:
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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 Oculus.Interaction.Input;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.Samples.PalmMenu
|
||||
{
|
||||
/// <summary>
|
||||
/// Filters to one set of GameObjects or the other, depending on which hand is the user's dominant hand.
|
||||
/// </summary>
|
||||
public class DominantHandGameObjectFilter : MonoBehaviour, IGameObjectFilter
|
||||
{
|
||||
[SerializeField, Interface(typeof(IHand))]
|
||||
private Object _leftHand;
|
||||
|
||||
[SerializeField]
|
||||
private GameObject[] _leftHandedGameObjects;
|
||||
|
||||
[SerializeField]
|
||||
private GameObject[] _rightHandedGameObjects;
|
||||
|
||||
private IHand LeftHand { get; set; }
|
||||
|
||||
private readonly HashSet<GameObject> _leftHandedGameObjectSet =
|
||||
new HashSet<GameObject>();
|
||||
private readonly HashSet<GameObject> _rightHandedGameObjectSet =
|
||||
new HashSet<GameObject>();
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
foreach (var go in _leftHandedGameObjects)
|
||||
{
|
||||
_leftHandedGameObjectSet.Add(go);
|
||||
}
|
||||
|
||||
foreach (var go in _rightHandedGameObjects)
|
||||
{
|
||||
_rightHandedGameObjectSet.Add(go);
|
||||
}
|
||||
|
||||
LeftHand = _leftHand as IHand;
|
||||
}
|
||||
|
||||
public bool Filter(GameObject go)
|
||||
{
|
||||
if (LeftHand.IsDominantHand)
|
||||
{
|
||||
return _leftHandedGameObjectSet.Contains(go);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _rightHandedGameObjectSet.Contains(go);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f87c900294bbee44b7f74920f84bb29
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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 Oculus.Interaction.Input;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.Samples.PalmMenu
|
||||
{
|
||||
/// <summary>
|
||||
/// Matches the position and rotation of the user's dominant hand in world space. Normally this
|
||||
/// sort of behavior is done using the transform hierarchy, but in this case doing it via a script
|
||||
/// is cleaner as it (1) allows us to avoid nesting elements under the hand prefab itself and (2)
|
||||
/// allows the behavior to easily swap between hands depending on which hand is dominant. This
|
||||
/// also provides a convenient location for the rotation math that keeps the menu aligned "y-up"
|
||||
/// while still facing the "aim point" and located at the "anchor point." The default anchor- and
|
||||
/// aim-point values roughly center the menu just above the palms, facing away from the hands.
|
||||
/// </summary>
|
||||
public class MatchNonDominantPalmWorldSpaceTransform : MonoBehaviour
|
||||
{
|
||||
[SerializeField, Interface(typeof(IHand))]
|
||||
private Object _leftHand;
|
||||
|
||||
[SerializeField, Interface(typeof(IHand))]
|
||||
private Object _rightHand;
|
||||
|
||||
[SerializeField]
|
||||
private Vector3 _leftAnchorPoint = new Vector3(-0.0608603321f, 0.00953984447f, 0.000258127693f);
|
||||
|
||||
[SerializeField]
|
||||
private Vector3 _leftAimPoint = new Vector3(-0.0749258399f, 0.0893092677f, 0.000258127693f);
|
||||
|
||||
[SerializeField]
|
||||
private Vector3 _rightAnchorPoint = new Vector3(0.0652603358f, -0.011439844f, -0.00455812784f);
|
||||
|
||||
[SerializeField]
|
||||
private Vector3 _rightAimPoint = new Vector3(0.0793258473f, -0.0912092775f, -0.00455812784f);
|
||||
|
||||
private IHand LeftHand { get; set; }
|
||||
private IHand RightHand { get; set; }
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
LeftHand = _leftHand as IHand;
|
||||
RightHand = _rightHand as IHand;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
var anchor = LeftHand.IsDominantHand ? _rightAnchorPoint : _leftAnchorPoint;
|
||||
var aim = LeftHand.IsDominantHand ? _rightAimPoint : _leftAimPoint;
|
||||
var hand = LeftHand.IsDominantHand ? RightHand : LeftHand;
|
||||
Pose wristPose;
|
||||
if (hand.GetJointPose(HandJointId.HandWristRoot, out wristPose))
|
||||
{
|
||||
var anchorPose = new Pose(anchor, Quaternion.identity).GetTransformedBy(wristPose);
|
||||
var aimPose = new Pose(aim, Quaternion.identity).GetTransformedBy(wristPose);
|
||||
this.transform.SetPositionAndRotation(anchorPose.position, Quaternion.LookRotation((aimPose.position - anchorPose.position).normalized));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f9e662d52fa48c44a6c9630c20080c0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
_menuParent.SetActive(false);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show/hide the menu.
|
||||
/// </summary>
|
||||
public void ToggleMenu()
|
||||
{
|
||||
if (_menuParent.activeSelf)
|
||||
{
|
||||
_hideMenuAudio.Play();
|
||||
_menuParent.SetActive(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_showMenuAudio.Play();
|
||||
_menuParent.SetActive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c8ecef668d3a67a469961fc24349d6df
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* 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 TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.Samples.PalmMenu
|
||||
{
|
||||
/// <summary>
|
||||
/// Example of a bespoke behavior created to react to a particular palm menu. This controls the state
|
||||
/// of the object that responds to the menu, but also parts of the menu itself, specifically those
|
||||
/// which depend on the state of the controlled object (swappable icons, various text boxes, etc.).
|
||||
/// </summary>
|
||||
public class PalmMenuExampleButtonHandlers : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
private GameObject _controlledObject;
|
||||
|
||||
[SerializeField]
|
||||
private Color[] _colors;
|
||||
|
||||
[SerializeField]
|
||||
private GameObject _rotationEnabledIcon;
|
||||
|
||||
[SerializeField]
|
||||
private GameObject _rotationDisabledIcon;
|
||||
|
||||
[SerializeField]
|
||||
private float _rotationLerpSpeed = 1f;
|
||||
|
||||
[SerializeField]
|
||||
private TMP_Text _rotationDirectionText;
|
||||
|
||||
[SerializeField]
|
||||
private string[] _rotationDirectionNames;
|
||||
|
||||
[SerializeField]
|
||||
private GameObject[] _rotationDirectionIcons;
|
||||
|
||||
[SerializeField]
|
||||
private Quaternion[] _rotationDirections;
|
||||
|
||||
[SerializeField]
|
||||
private TMP_Text _elevationText;
|
||||
|
||||
[SerializeField]
|
||||
private float _elevationChangeIncrement;
|
||||
|
||||
[SerializeField]
|
||||
private float _elevationChangeLerpSpeed = 1f;
|
||||
|
||||
[SerializeField]
|
||||
private TMP_Text _shapeNameText;
|
||||
|
||||
[SerializeField]
|
||||
private string[] _shapeNames;
|
||||
|
||||
[SerializeField]
|
||||
private Mesh[] _shapes;
|
||||
|
||||
private int _currentColorIdx;
|
||||
private bool _rotationEnabled;
|
||||
private int _currentRotationDirectionIdx;
|
||||
private Vector3 _targetPosition;
|
||||
private int _currentShapeIdx;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_currentColorIdx = _colors.Length;
|
||||
CycleColor();
|
||||
|
||||
_rotationEnabled = false;
|
||||
ToggleRotationEnabled();
|
||||
|
||||
_currentRotationDirectionIdx = _rotationDirections.Length;
|
||||
CycleRotationDirection();
|
||||
|
||||
_targetPosition = _controlledObject.transform.position;
|
||||
IncrementElevation(true);
|
||||
IncrementElevation(false);
|
||||
|
||||
_currentShapeIdx = _shapes.Length;
|
||||
CycleShape(true);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (_rotationEnabled)
|
||||
{
|
||||
var rotation = Quaternion.Slerp(Quaternion.identity, _rotationDirections[_currentRotationDirectionIdx], _rotationLerpSpeed * Time.deltaTime);
|
||||
_controlledObject.transform.rotation = rotation * _controlledObject.transform.rotation;
|
||||
}
|
||||
|
||||
_controlledObject.transform.position = Vector3.Lerp(_controlledObject.transform.position, _targetPosition, _elevationChangeLerpSpeed * Time.deltaTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the color of the controlled object to the next in the list of allowed colors, looping if the end of the list is reached.
|
||||
/// </summary>
|
||||
public void CycleColor()
|
||||
{
|
||||
_currentColorIdx += 1;
|
||||
if (_currentColorIdx >= _colors.Length)
|
||||
{
|
||||
_currentColorIdx = 0;
|
||||
}
|
||||
|
||||
_controlledObject.GetComponent<Renderer>().material.SetColor("_Color", _colors[_currentColorIdx]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggle whether or not rotation is enabled, and set the icon of the controlling button to display what will happen next time the button is pressed.
|
||||
/// </summary>
|
||||
public void ToggleRotationEnabled()
|
||||
{
|
||||
_rotationEnabled = !_rotationEnabled;
|
||||
_rotationEnabledIcon.SetActive(!_rotationEnabled);
|
||||
_rotationDisabledIcon.SetActive(_rotationEnabled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the rotation direction of the controlled object to the next in the list of allowed directions, looping if the end of the list is reached.
|
||||
/// Set the icon of the controlling button to display what will happen next time the button is pressed.
|
||||
/// </summary>
|
||||
public void CycleRotationDirection()
|
||||
{
|
||||
Debug.Assert(_rotationDirectionNames.Length == _rotationDirections.Length);
|
||||
Debug.Assert(_rotationDirectionNames.Length == _rotationDirectionIcons.Length);
|
||||
|
||||
_currentRotationDirectionIdx += 1;
|
||||
if (_currentRotationDirectionIdx >= _rotationDirections.Length)
|
||||
{
|
||||
_currentRotationDirectionIdx = 0;
|
||||
}
|
||||
|
||||
int nextRotationDirectionIdx = _currentRotationDirectionIdx + 1;
|
||||
if (nextRotationDirectionIdx >= _rotationDirections.Length)
|
||||
{
|
||||
nextRotationDirectionIdx = 0;
|
||||
}
|
||||
|
||||
_rotationDirectionText.text = _rotationDirectionNames[nextRotationDirectionIdx];
|
||||
for (int idx = 0; idx < _rotationDirections.Length; ++idx)
|
||||
{
|
||||
_rotationDirectionIcons[idx].SetActive(idx == nextRotationDirectionIdx);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the target elevation of the controlled object in the requested direction, within the limits [0.2, 2].
|
||||
/// Set the text to display the new target elevation.
|
||||
/// </summary>
|
||||
public void IncrementElevation(bool up)
|
||||
{
|
||||
float increment = _elevationChangeIncrement;
|
||||
if (!up)
|
||||
{
|
||||
increment *= -1f;
|
||||
}
|
||||
_targetPosition = new Vector3(_targetPosition.x, Mathf.Clamp(_targetPosition.y + increment, 0.2f, 2f), _targetPosition.z);
|
||||
_elevationText.text = "Elevation: " + _targetPosition.y.ToString("0.0");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the shape of the controlled object to the next or previous in the list of allowed shapes, depending on the requested direction, looping beyond the bounds of the list.
|
||||
/// Set the text to display the name of the current shape.
|
||||
/// </summary>
|
||||
public void CycleShape(bool cycleForward)
|
||||
{
|
||||
Debug.Assert(_shapeNames.Length == _shapes.Length);
|
||||
|
||||
_currentShapeIdx += cycleForward ? 1 : -1;
|
||||
if (_currentShapeIdx >= _shapes.Length)
|
||||
{
|
||||
_currentShapeIdx = 0;
|
||||
}
|
||||
else if (_currentShapeIdx < 0)
|
||||
{
|
||||
_currentShapeIdx = _shapes.Length - 1;
|
||||
}
|
||||
|
||||
_shapeNameText.text = _shapeNames[_currentShapeIdx];
|
||||
_controlledObject.GetComponent<MeshFilter>().mesh = _shapes[_currentShapeIdx];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 65307bdd58ef73943b02570244339263
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user