Unity & Vive: Tutoriel

J'ai décidé de partir en vacances pour étudier le développement de Vive dans Unity3D. Googlé quelques exemples et a commencé à essayer, mais pour une raison quelconque, cela n'a pas fonctionné. Ayant commencé à comprendre plus en détail et découvert que Valve a récemment déployé une mise à jour du plugin pour Unity3D - une nouvelle version fortement refaite. Deux innovations fondamentales y sont apparues qui ont rendu les anciens tutoriels non pertinents. J'ai décidé d'en écrire un nouveau



Nous aurons besoin d'Unity> = 5.4.0 et du nouveau plugin SteamVR Plugin ( GitHub )


Dans le plugin lui-même, il y a trois fichiers PDF utiles pour la familiarisation


\ Ressources \ SteamVR \ SteamVR Unity Plugin.pdf
\ Ressources \ SteamVR \ Plugin SteamVR Unity - Input System.pdf
\ Actifs \ SteamVR \ InteractionSystem \ InteractionSystem.pdf


Et deux exemples:


\ Assets \ SteamVR \ Simple Sample.unity
\ Assets \ SteamVR \ InteractionSystem \ Samples \ Interactions_Example.unity


Le plugin prend en charge le mode simulation - il s'allume si le casque n'est pas allumé



Eh bien, maintenant étape par étape;


  1. Créez un nouveau projet de modèle 3D avec le plugin SteamVR Plugin


  2. Nous sommes d'accord avec les paramètres



  3. Maintenant, le point clé est que vous devez configurer la gestion. Sélectionnez l'élément de menu Window \ SteamVR Imput



  4. Unity vous posera des questions sur le fichier actions.json manquant et vous proposera de copier l'exemple de fichier (il se trouve dans \ Assets \ SteamVR \ Input \ ExampleJSON) - je vous conseille d'accepter.



    Les fichiers json montrent que le plugin est conçu non seulement pour Vive, mais aussi pour Oculus et Windows MR, ainsi que de nouveaux contrôleurs de phalanges. Des changements majeurs y sont associés.


  5. Dans la fenêtre qui s'ouvre, cliquez simplement sur "Enregistrer et générer"



  6. Vous devez maintenant ajouter le lecteur (Player from \ Assets \ SteamVR \ InteractionSystem \ Core \ Prefabs) à la scène et retirer la caméra principale



    pour que le mode de simulation fonctionne - il doit être activé dans les propriétés du lecteur



    Il est également utile de copier le dossier \ Assets \ SteamVR \ InteractionSystem \ Core \ Icons vers \ Assets et de le renommer en Gizmos


  7. Vous pouvez cliquer sur Lecture - mais seule une fausse main dans la simulation ne fonctionnera pas, vous devez copier le script à partir d' ici et l'accrocher sur Player-> NoSteamVRFallbackObjects-> FallbackHand



Faux code de script à la main
using System.Collections.Generic; using UnityEngine; using Valve.VR; public class VrSimulatorHandFixer156 : SteamVR_Behaviour_Pose { Valve.VR.InteractionSystem.Hand _hand; protected override void Start() { base.Start(); _hand = this.gameObject.GetComponent<Valve.VR.InteractionSystem.Hand>(); _hand.handType = SteamVR_Input_Sources.RightHand; GameObject broHand = GameObject.Instantiate(_hand.gameObject); Destroy(broHand.GetComponent<VrSimulatorHandFixer156>()); broHand.SetActive(false); _hand.otherHand = broHand.GetComponent<Valve.VR.InteractionSystem.Hand>(); _hand.otherHand.handType = SteamVR_Input_Sources.LeftHand; var spoofMouse = new SpoofMouseAction(); _hand.grabGripAction = spoofMouse; spoofMouse.InitializeDictionariesExposed(_hand.handType); this.poseAction = new Poser_SteamVR_Action_Pose(); } protected override void OnEnable() { } protected override void Update() { _hand.grabGripAction.UpdateValue(SteamVR_Input_Sources.RightHand); } protected override void OnDisable() { } protected override void CheckDeviceIndex() { } //---------------------------------------------------------------------------------------------- class SpoofMouseAction : SteamVR_Action_Boolean { public SpoofMouseAction() { } public void InitializeDictionariesExposed(SteamVR_Input_Sources source) { try { InitializeDictionaries(source); } catch(System.Exception e) { } } protected override void InitializeDictionaries(SteamVR_Input_Sources source) { base.InitializeDictionaries(source); onStateDown.Add(source, null); onStateUp.Add(source, null); actionData.Add(source, new InputDigitalActionData_t()); lastActionData.Add(source, new InputDigitalActionData_t()); } public override void UpdateValue(SteamVR_Input_Sources inputSource) { lastActionData[inputSource] = actionData[inputSource]; tempActionData.bState = Input.GetMouseButton(0) || Input.GetMouseButtonDown(0); tempActionData.bChanged = Input.GetMouseButtonDown(0) || Input.GetMouseButtonUp(0); tempActionData.bActive = true; //tempActionData.fUpdateTime //tempActionData.act actionData[inputSource] = tempActionData; changed[inputSource] = tempActionData.bChanged; active[inputSource] = tempActionData.bActive; activeOrigin[inputSource] = tempActionData.activeOrigin; updateTime[inputSource] = Time.time;// tempActionData.fUpdateTime; if (changed[inputSource]) lastChanged[inputSource] = Time.time; if (onStateDown[inputSource] != null && GetStateDown(inputSource)) onStateDown[inputSource].Invoke(this); if (onStateUp[inputSource] != null && GetStateUp(inputSource)) onStateUp[inputSource].Invoke(this); if (onChange[inputSource] != null && GetChanged(inputSource)) onChange[inputSource].Invoke(this); if (onUpdate[inputSource] != null) onUpdate[inputSource].Invoke(this); if (onActiveChange[inputSource] != null && lastActionData[inputSource].bActive != active[inputSource]) onActiveChange[inputSource].Invoke(this, active[inputSource]); } } class Poser_SteamVR_Action_Pose : SteamVR_Action_Pose { public override bool GetActive(SteamVR_Input_Sources inputSource) { return false; } } } 

En mode VR avec les contrôleurs allumés - ils seront visibles



  1. Nous avons ajouté la VR, mais nous ne pouvons même pas nous déplacer. Le plugin a une implémentation de téléportation
    pour l'activer, vous devez ajouter à la scène de téléportation à partir de \ Assets \ SteamVR \ InteractionSystem \ Teleport \ Prefabs
    et organiser TeleportPoint à partir de là



    Vous pouvez également vous téléporter en imitation à l'aide de la touche T.


  2. Vous pouvez créer une surface de téléportation - Créez un avion et accrochez le script TeleportArea.cs à partir de \ Assets \ SteamVR \ InteractionSystem \ Teleport \ Scripts dessus



  3. Essayons d'interagir avec des objets - créez un cube et suspendez le script Interactable.cs à \ Assets \ SteamVR \ InteractionSystem \ Core \ Scripts dessus
    maintenant il est mis en évidence, mais rien ne lui arrive



  4. Nous devons enregistrer l'interaction - nous allons créer un nouveau script pour Cube



Code d'interaction
 using System.Collections; using System.Collections.Generic; using UnityEngine; using Valve.VR.InteractionSystem; public class NewBehaviourScript : MonoBehaviour { private Hand.AttachmentFlags attachmentFlags = Hand.defaultAttachmentFlags & (~Hand.AttachmentFlags.SnapOnAttach) & (~Hand.AttachmentFlags.DetachOthers) & (~Hand.AttachmentFlags.VelocityMovement); private Interactable interactable; // Use this for initialization void Start () { interactable = this.GetComponent<Interactable>(); } private void HandHoverUpdate(Hand hand) { GrabTypes startingGrabType = hand.GetGrabStarting(); bool isGrabEnding = hand.IsGrabEnding(this.gameObject); if (startingGrabType != GrabTypes.None) { // Call this to continue receiving HandHoverUpdate messages, // and prevent the hand from hovering over anything else hand.HoverLock(interactable); // Attach this object to the hand hand.AttachObject(gameObject, startingGrabType, attachmentFlags); } else if (isGrabEnding) { // Detach this object from the hand hand.DetachObject(gameObject); // Call this to undo HoverLock hand.HoverUnlock(interactable); } } } 


Plus de détails sur l'interaction peuvent être trouvés dans les exemples du plugin, en particulier dans \ Assets \ SteamVR \ InteractionSystem \ Samples \ Scripts \ InteractableExample.cs


Et puis nous allons essayer de faire ce qui n'est pas dans les exemples - ajouter de nouvelles actions


  1. Ouvrez le script Player.cs et ajoutez les champs


      [SteamVR_DefaultAction("PlayerMove", "default")] public SteamVR_Action_Vector2 a_move; [SteamVR_DefaultAction("PlayerRotate", "default")] public SteamVR_Action_Vector2 a_rotate; [SteamVR_DefaultAction("MenuClick", "default")] public SteamVR_Action_Boolean a_menu; 

    Les types de retour valides se trouvent dans \ Assets \ SteamVR \ Input



Et la méthode de mise à jour:


  private void Update() { bool st = a_menu.GetStateDown(SteamVR_Input_Sources.Any); if (st) { this.transform.position = new Vector3(0, 0, 0); } else { Camera camera = this.GetComponentInChildren<Camera>(); Quaternion cr = Quaternion.Euler(0, 0, 0); if (camera != null) { Vector2 r = a_rotate.GetAxis(SteamVR_Input_Sources.RightHand); Quaternion qp = this.transform.rotation; qp.eulerAngles += new Vector3(0, rx, 0); this.transform.rotation = qp; cr = camera.transform.rotation; } Vector2 m = a_move.GetAxis(SteamVR_Input_Sources.LeftHand); m = Quaternion.Euler(0, 0, -cr.eulerAngles.y) * m; this.transform.position += new Vector3(mx / 10, 0, my / 10); } } 

Code Player.cs complet
 //======= 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; public bool allowToggleTo2D = true; [SteamVR_DefaultAction("PlayerMove", "default")] public SteamVR_Action_Vector2 a_move; [SteamVR_DefaultAction("PlayerRotate", "default")] public SteamVR_Action_Vector2 a_rotate; [SteamVR_DefaultAction("MenuClick", "default")] public SteamVR_Action_Boolean a_menu; //------------------------------------------------- // 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; } } //------------------------------------------------- void Awake() { SteamVR.Initialize(true); //force openvr if ( trackingOriginTransform == null ) { trackingOriginTransform = this.transform; } } //------------------------------------------------- private IEnumerator Start() { _instance = this; while (SteamVR_Behaviour.instance.forcingInitialization) yield return null; if ( SteamVR.instance != null ) { ActivateRig( rigSteamVR ); } else { #if !HIDE_DEBUG_UI ActivateRig( rig2DFallback ); #endif } } private void Update() { bool st = a_menu.GetStateDown(SteamVR_Input_Sources.Any); if (st) { this.transform.position = new Vector3(0, 0, 0); } else { Camera camera = this.GetComponentInChildren<Camera>(); Quaternion cr = Quaternion.Euler(0, 0, 0); if (camera != null) { Vector2 r = a_rotate.GetAxis(SteamVR_Input_Sources.RightHand); Quaternion qp = this.transform.rotation; qp.eulerAngles += new Vector3(0, rx, 0); this.transform.rotation = qp; cr = camera.transform.rotation; } Vector2 m = a_move.GetAxis(SteamVR_Input_Sources.LeftHand); m = Quaternion.Euler(0, 0, -cr.eulerAngles.y) * m; this.transform.position += new Vector3(mx / 10, 0, my / 10); } } //------------------------------------------------- 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 } } } 

  1. Exécutez Window \ SteamVR Imput, créez nos actions dans le jeu par défaut et enregistrez-les, sélectionnez maintenant "Open binding UI" (SteamVR doit être en cours d'exécution et au moins un contrôleur est allumé)



  2. L'onglet Controller Binding s'ouvre dans le navigateur - vous devez configurer la connexion de nos actions avec les contrôleurs: nous suspendons PlayerMove sur le TRACKPAD gauche (n'oubliez pas de désactiver le mode Miroir), PlayerRotate sur le TRACKPAD droit et accrochez MenuClick sur les touches de menu



  3. Fermez la liaison du contrôleur et enregistrez les modifications.


  4. Dans les propriétés du Player nous connecterons les nouvelles actions



  5. Lancer la lecture




Projet


Conclusion


Il convient de noter quelques points. Certaines actions dans SteamVR Imput peuvent faire réfléchir Unity pendant une longue période, en principe, vous pouvez apporter ces modifications vous-même dans le code, et au lieu d'utiliser Controller Binding, vous pouvez modifier directement les fichiers json, mais il y a un gros risque d'erreurs qui peuvent être difficiles à détecter.


Pour une étude plus approfondie du plugin - il est utile d'étudier les exemples en détail, et bien sûr - lire la documentation.

Source: https://habr.com/ru/post/fr426357/


All Articles