437 lines
14 KiB
C#
437 lines
14 KiB
C#
|
//======= Copyright (c) Valve Corporation, All rights reserved. ===============
|
|||
|
//
|
|||
|
// Purpose: Player interface used to query HMD transforms and VR hands
|
|||
|
//
|
|||
|
//=============================================================================
|
|||
|
|
|||
|
using UnityEngine;
|
|||
|
using System.Collections;
|
|||
|
using System.Collections.Generic;
|
|||
|
|
|||
|
namespace Valve.VR.InteractionSystem
|
|||
|
{
|
|||
|
//-------------------------------------------------------------------------
|
|||
|
// Singleton representing the local VR player/user, with methods for getting
|
|||
|
// the player's hands, head, tracking origin, and guesses for various properties.
|
|||
|
//-------------------------------------------------------------------------
|
|||
|
public class Player : MonoBehaviour
|
|||
|
{
|
|||
|
[Tooltip("Virtual transform corresponding to the meatspace tracking origin. Devices are tracked relative to this.")]
|
|||
|
public Transform trackingOriginTransform;
|
|||
|
|
|||
|
[Tooltip("List of possible transforms for the head/HMD, including the no-SteamVR fallback camera.")]
|
|||
|
public Transform[] hmdTransforms;
|
|||
|
|
|||
|
[Tooltip("List of possible Hands, including no-SteamVR fallback Hands.")]
|
|||
|
public Hand[] hands;
|
|||
|
|
|||
|
[Tooltip("Reference to the physics collider that follows the player's HMD position.")]
|
|||
|
public Collider headCollider;
|
|||
|
|
|||
|
[Tooltip("These objects are enabled when SteamVR is available")]
|
|||
|
public GameObject rigSteamVR;
|
|||
|
|
|||
|
[Tooltip("These objects are enabled when SteamVR is not available, or when the user toggles out of VR")]
|
|||
|
public GameObject rig2DFallback;
|
|||
|
|
|||
|
[Tooltip("The audio listener for this player")]
|
|||
|
public Transform audioListener;
|
|||
|
|
|||
|
[Tooltip("This action lets you know when the player has placed the headset on their head")]
|
|||
|
public SteamVR_Action_Boolean headsetOnHead = SteamVR_Input.GetBooleanAction("HeadsetOnHead");
|
|||
|
|
|||
|
public bool allowToggleTo2D = true;
|
|||
|
|
|||
|
|
|||
|
public delegate void HeadsetDelegate();
|
|||
|
public static HeadsetDelegate OnHeadsetOn;
|
|||
|
|
|||
|
public delegate void HeadOffDelegate();
|
|||
|
public static HeadOffDelegate OnHeadsetOff;
|
|||
|
|
|||
|
|
|||
|
//-------------------------------------------------
|
|||
|
// Singleton instance of the Player. Only one can exist at a time.
|
|||
|
//-------------------------------------------------
|
|||
|
private static Player _instance;
|
|||
|
public static Player instance
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (_instance == null)
|
|||
|
{
|
|||
|
_instance = FindObjectOfType<Player>();
|
|||
|
}
|
|||
|
return _instance;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//-------------------------------------------------
|
|||
|
// Get the number of active Hands.
|
|||
|
//-------------------------------------------------
|
|||
|
public int handCount
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
int count = 0;
|
|||
|
for (int i = 0; i < hands.Length; i++)
|
|||
|
{
|
|||
|
if (hands[i].gameObject.activeInHierarchy)
|
|||
|
{
|
|||
|
count++;
|
|||
|
}
|
|||
|
}
|
|||
|
return count;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//-------------------------------------------------
|
|||
|
// Get the i-th active Hand.
|
|||
|
//
|
|||
|
// i - Zero-based index of the active Hand to get
|
|||
|
//-------------------------------------------------
|
|||
|
public Hand GetHand(int i)
|
|||
|
{
|
|||
|
for (int j = 0; j < hands.Length; j++)
|
|||
|
{
|
|||
|
if (!hands[j].gameObject.activeInHierarchy)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (i > 0)
|
|||
|
{
|
|||
|
i--;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
return hands[j];
|
|||
|
}
|
|||
|
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//-------------------------------------------------
|
|||
|
public Hand leftHand
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
for (int j = 0; j < hands.Length; j++)
|
|||
|
{
|
|||
|
if (!hands[j].gameObject.activeInHierarchy)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (hands[j].handType != SteamVR_Input_Sources.LeftHand)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
return hands[j];
|
|||
|
}
|
|||
|
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//-------------------------------------------------
|
|||
|
public Hand rightHand
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
for (int j = 0; j < hands.Length; j++)
|
|||
|
{
|
|||
|
if (!hands[j].gameObject.activeInHierarchy)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (hands[j].handType != SteamVR_Input_Sources.RightHand)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
return hands[j];
|
|||
|
}
|
|||
|
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//-------------------------------------------------
|
|||
|
// Get Player scale. Assumes it is scaled equally on all axes.
|
|||
|
//-------------------------------------------------
|
|||
|
|
|||
|
public float scale
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return transform.lossyScale.x;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//-------------------------------------------------
|
|||
|
// Get the HMD transform. This might return the fallback camera transform if SteamVR is unavailable or disabled.
|
|||
|
//-------------------------------------------------
|
|||
|
public Transform hmdTransform
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (hmdTransforms != null)
|
|||
|
{
|
|||
|
for (int i = 0; i < hmdTransforms.Length; i++)
|
|||
|
{
|
|||
|
if (hmdTransforms[i].gameObject.activeInHierarchy)
|
|||
|
return hmdTransforms[i];
|
|||
|
}
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//-------------------------------------------------
|
|||
|
// Height of the eyes above the ground - useful for estimating player height.
|
|||
|
//-------------------------------------------------
|
|||
|
public float eyeHeight
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
Transform hmd = hmdTransform;
|
|||
|
if (hmd)
|
|||
|
{
|
|||
|
Vector3 eyeOffset = Vector3.Project(hmd.position - trackingOriginTransform.position, trackingOriginTransform.up);
|
|||
|
return eyeOffset.magnitude / trackingOriginTransform.lossyScale.x;
|
|||
|
}
|
|||
|
return 0.0f;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//-------------------------------------------------
|
|||
|
// Guess for the world-space position of the player's feet, directly beneath the HMD.
|
|||
|
//-------------------------------------------------
|
|||
|
public Vector3 feetPositionGuess
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
Transform hmd = hmdTransform;
|
|||
|
if (hmd)
|
|||
|
{
|
|||
|
return trackingOriginTransform.position + Vector3.ProjectOnPlane(hmd.position - trackingOriginTransform.position, trackingOriginTransform.up);
|
|||
|
}
|
|||
|
return trackingOriginTransform.position;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//-------------------------------------------------
|
|||
|
// Guess for the world-space direction of the player's hips/torso. This is effectively just the gaze direction projected onto the floor plane.
|
|||
|
//-------------------------------------------------
|
|||
|
public Vector3 bodyDirectionGuess
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
Transform hmd = hmdTransform;
|
|||
|
if (hmd)
|
|||
|
{
|
|||
|
Vector3 direction = Vector3.ProjectOnPlane(hmd.forward, trackingOriginTransform.up);
|
|||
|
if (Vector3.Dot(hmd.up, trackingOriginTransform.up) < 0.0f)
|
|||
|
{
|
|||
|
// The HMD is upside-down. Either
|
|||
|
// -The player is bending over backwards
|
|||
|
// -The player is bent over looking through their legs
|
|||
|
direction = -direction;
|
|||
|
}
|
|||
|
return direction;
|
|||
|
}
|
|||
|
return trackingOriginTransform.forward;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//-------------------------------------------------
|
|||
|
private void Awake()
|
|||
|
{
|
|||
|
if (trackingOriginTransform == null)
|
|||
|
{
|
|||
|
trackingOriginTransform = this.transform;
|
|||
|
}
|
|||
|
|
|||
|
#if OPENVR_XR_API && UNITY_LEGACY_INPUT_HELPERS
|
|||
|
if (hmdTransforms != null)
|
|||
|
{
|
|||
|
foreach (var hmd in hmdTransforms)
|
|||
|
{
|
|||
|
if (hmd.GetComponent<UnityEngine.SpatialTracking.TrackedPoseDriver>() == null)
|
|||
|
hmd.gameObject.AddComponent<UnityEngine.SpatialTracking.TrackedPoseDriver>();
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//-------------------------------------------------
|
|||
|
private IEnumerator Start()
|
|||
|
{
|
|||
|
_instance = this;
|
|||
|
|
|||
|
while (SteamVR.initializedState == SteamVR.InitializedStates.None || SteamVR.initializedState == SteamVR.InitializedStates.Initializing)
|
|||
|
yield return null;
|
|||
|
|
|||
|
if (SteamVR.instance != null)
|
|||
|
{
|
|||
|
ActivateRig(rigSteamVR);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
#if !HIDE_DEBUG_UI
|
|||
|
ActivateRig(rig2DFallback);
|
|||
|
#endif
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
protected virtual void Update()
|
|||
|
{
|
|||
|
if (SteamVR.initializedState != SteamVR.InitializedStates.InitializeSuccess)
|
|||
|
return;
|
|||
|
|
|||
|
if (headsetOnHead != null)
|
|||
|
{
|
|||
|
if (headsetOnHead.GetStateDown(SteamVR_Input_Sources.Head))
|
|||
|
{
|
|||
|
Debug.Log("<b>SteamVR Interaction System</b> Headset placed on head");
|
|||
|
OnHeadsetOn?.Invoke();
|
|||
|
}
|
|||
|
else if (headsetOnHead.GetStateUp(SteamVR_Input_Sources.Head))
|
|||
|
{
|
|||
|
Debug.Log("<b>SteamVR Interaction System</b> Headset removed");
|
|||
|
OnHeadsetOff?.Invoke();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//-------------------------------------------------
|
|||
|
void OnDrawGizmos()
|
|||
|
{
|
|||
|
if (this != instance)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//NOTE: These gizmo icons don't work in the plugin since the icons need to exist in a specific "Gizmos"
|
|||
|
// folder in your Asset tree. These icons are included under Core/Icons. Moving them into a
|
|||
|
// "Gizmos" folder should make them work again.
|
|||
|
|
|||
|
Gizmos.color = Color.white;
|
|||
|
Gizmos.DrawIcon(feetPositionGuess, "vr_interaction_system_feet.png");
|
|||
|
|
|||
|
Gizmos.color = Color.cyan;
|
|||
|
Gizmos.DrawLine(feetPositionGuess, feetPositionGuess + trackingOriginTransform.up * eyeHeight);
|
|||
|
|
|||
|
// Body direction arrow
|
|||
|
Gizmos.color = Color.blue;
|
|||
|
Vector3 bodyDirection = bodyDirectionGuess;
|
|||
|
Vector3 bodyDirectionTangent = Vector3.Cross(trackingOriginTransform.up, bodyDirection);
|
|||
|
Vector3 startForward = feetPositionGuess + trackingOriginTransform.up * eyeHeight * 0.75f;
|
|||
|
Vector3 endForward = startForward + bodyDirection * 0.33f;
|
|||
|
Gizmos.DrawLine(startForward, endForward);
|
|||
|
Gizmos.DrawLine(endForward, endForward - 0.033f * (bodyDirection + bodyDirectionTangent));
|
|||
|
Gizmos.DrawLine(endForward, endForward - 0.033f * (bodyDirection - bodyDirectionTangent));
|
|||
|
|
|||
|
Gizmos.color = Color.red;
|
|||
|
int count = handCount;
|
|||
|
for (int i = 0; i < count; i++)
|
|||
|
{
|
|||
|
Hand hand = GetHand(i);
|
|||
|
|
|||
|
if (hand.handType == SteamVR_Input_Sources.LeftHand)
|
|||
|
{
|
|||
|
Gizmos.DrawIcon(hand.transform.position, "vr_interaction_system_left_hand.png");
|
|||
|
}
|
|||
|
else if (hand.handType == SteamVR_Input_Sources.RightHand)
|
|||
|
{
|
|||
|
Gizmos.DrawIcon(hand.transform.position, "vr_interaction_system_right_hand.png");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/*
|
|||
|
Hand.HandType guessHandType = hand.currentHandType;
|
|||
|
|
|||
|
if ( guessHandType == Hand.HandType.Left )
|
|||
|
{
|
|||
|
Gizmos.DrawIcon( hand.transform.position, "vr_interaction_system_left_hand_question.png" );
|
|||
|
}
|
|||
|
else if ( guessHandType == Hand.HandType.Right )
|
|||
|
{
|
|||
|
Gizmos.DrawIcon( hand.transform.position, "vr_interaction_system_right_hand_question.png" );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Gizmos.DrawIcon( hand.transform.position, "vr_interaction_system_unknown_hand.png" );
|
|||
|
}
|
|||
|
*/
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//-------------------------------------------------
|
|||
|
public void Draw2DDebug()
|
|||
|
{
|
|||
|
if (!allowToggleTo2D)
|
|||
|
return;
|
|||
|
|
|||
|
if (!SteamVR.active)
|
|||
|
return;
|
|||
|
|
|||
|
int width = 100;
|
|||
|
int height = 25;
|
|||
|
int left = Screen.width / 2 - width / 2;
|
|||
|
int top = Screen.height - height - 10;
|
|||
|
|
|||
|
string text = (rigSteamVR.activeSelf) ? "2D Debug" : "VR";
|
|||
|
|
|||
|
if (GUI.Button(new Rect(left, top, width, height), text))
|
|||
|
{
|
|||
|
if (rigSteamVR.activeSelf)
|
|||
|
{
|
|||
|
ActivateRig(rig2DFallback);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ActivateRig(rigSteamVR);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//-------------------------------------------------
|
|||
|
private void ActivateRig(GameObject rig)
|
|||
|
{
|
|||
|
rigSteamVR.SetActive(rig == rigSteamVR);
|
|||
|
rig2DFallback.SetActive(rig == rig2DFallback);
|
|||
|
|
|||
|
if (audioListener)
|
|||
|
{
|
|||
|
audioListener.transform.parent = hmdTransform;
|
|||
|
audioListener.transform.localPosition = Vector3.zero;
|
|||
|
audioListener.transform.localRotation = Quaternion.identity;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//-------------------------------------------------
|
|||
|
public void PlayerShotSelf()
|
|||
|
{
|
|||
|
//Do something appropriate here
|
|||
|
}
|
|||
|
}
|
|||
|
}
|