Unity & Vive: Tutorial

Ich habe mich für den Urlaub entschieden, um die Entwicklung für Vive in Unity3D zu studieren. Ich habe ein paar Beispiele gegoogelt und angefangen, es zu versuchen, aber aus irgendeinem Grund hat es nicht funktioniert. Nachdem Valve angefangen hatte, genauer zu verstehen und festgestellt hatte, dass Valve kürzlich ein Plugin- Update für Unity3D herausgebracht hat - eine neue, stark überarbeitete Version. Darin tauchten einige grundlegende Neuerungen auf, die die alten Tutorials nicht relevant machten. Ich beschloss, einen neuen zu schreiben



Wir benötigen Unity> = 5.4.0 und das neue SteamVR Plugin Plugin ( GitHub )


Im Plugin selbst gibt es drei nützliche PDFs zur Einarbeitung


\ Assets \ SteamVR \ SteamVR Unity Plugin.pdf
\ Assets \ SteamVR \ SteamVR Unity Plugin - Eingabesystem.pdf
\ Assets \ SteamVR \ InteractionSystem \ InteractionSystem.pdf


Und zwei Beispiele:


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


Das Plugin unterstützt den Simulationsmodus - es wird eingeschaltet, wenn der Helm nicht eingeschaltet ist



Nun, jetzt Schritt für Schritt;


  1. Erstellen Sie ein neues 3D-Vorlagenprojekt mit dem SteamVR-Plugin-Plugin


  2. Wir stimmen den Einstellungen zu



  3. Der entscheidende Punkt ist nun, dass Sie die Verwaltung konfigurieren müssen. Wählen Sie den Menüpunkt Fenster \ SteamVR-Eingabe



  4. Unity fragt nach der fehlenden Aktion.json und bietet an, die Beispieldatei zu kopieren (sie befindet sich in \ Assets \ SteamVR \ Input \ ExampleJSON). Ich rate Ihnen, dem zuzustimmen.



    Die json-Dateien zeigen, dass das Plugin nicht nur für Vive, sondern auch für Oculus und Windows MR sowie für neue Knuckle-Controller entwickelt wurde. Damit sind wesentliche Änderungen verbunden.


  5. Klicken Sie im folgenden Fenster einfach auf "Speichern und generieren".



  6. Jetzt müssen Sie den Player (Player aus \ Assets \ SteamVR \ InteractionSystem \ Core \ Prefabs) zur Szene hinzufügen und die Hauptkamera entfernen



    Damit der Simulationsmodus funktioniert, muss er in den Eigenschaften des Players aktiviert sein



    Es ist auch nützlich, den Ordner \ Assets \ SteamVR \ InteractionSystem \ Core \ Icons nach \ Assets zu kopieren und in Gizmos umzubenennen


  7. Sie können auf Wiedergabe klicken - aber nur eine falsche Hand in der Simulation funktioniert nicht. Sie müssen das Skript von hier kopieren und an Player-> NoSteamVRFallbackObjects-> FallbackHand hängen



Gefälschter Handskriptcode
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; } } } 

Im VR-Modus mit eingeschalteten Controllern sind sie sichtbar



  1. Wir haben VR hinzugefügt, können uns aber nicht einmal bewegen. Das Plugin hat eine Implementierung der Teleportation
    Um es zu aktivieren, müssen Sie der Teleporting-Szene aus \ Assets \ SteamVR \ InteractionSystem \ Teleport \ Prefabs hinzufügen
    und arrangieren Sie von dort aus auch TeleportPoint



    Sie können sich auch mit der T-Taste imitieren.


  2. Sie können eine Teleportationsfläche erstellen - Erstellen Sie eine Ebene und hängen Sie das Skript TeleportArea.cs unter \ Assets \ SteamVR \ InteractionSystem \ Teleport \ Scripts daran auf



  3. Versuchen wir, mit Objekten zu interagieren - erstellen Sie einen Cube und hängen Sie das Skript Interactable.cs an \ Assets \ SteamVR \ InteractionSystem \ Core \ Scripts daran
    Jetzt wird es hervorgehoben, aber es passiert nichts



  4. Wir müssen die Interaktion registrieren - wir werden ein neues Skript für Cube erstellen



Code für die Interaktion
 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); } } } 


Weitere Details zur Interaktion finden Sie in den Beispielen für das Plugin, insbesondere unter \ Assets \ SteamVR \ InteractionSystem \ Samples \ Scripts \ InteractableExample.cs


Und dann werden wir versuchen, das zu tun, was nicht in den Beispielen steht - neue Aktionen hinzufügen


  1. Öffnen Sie das Skript Player.cs und fügen Sie die Felder hinzu


      [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; 

    Gültige Rückgabetypen finden Sie unter \ Assets \ SteamVR \ Input



Und die Update-Methode:


  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); } } 

Vollständiger Player.cs-Code
 //======= 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. Führen Sie Window \ SteamVR Imput aus, erstellen Sie unsere Aktionen im Standardsatz und speichern Sie sie. Wählen Sie nun "Open Binding UI" (SteamVR muss ausgeführt werden und mindestens ein Controller ist eingeschaltet).



  2. Die Registerkarte Controller-Bindung wird im Browser geöffnet. Dort müssen Sie die Verbindung unserer Aktionen mit den Controllern konfigurieren: Wir hängen PlayerMove auf dem linken TRACKPAD (vergessen Sie nicht, den Spiegelmodus auszuschalten), PlayerRotate auf dem rechten TRACKPAD und hängen MenuClick auf die Menütasten



  3. Schließen Sie die Controller-Bindung und speichern Sie die Änderungen.


  4. In den Eigenschaften des Players verbinden wir die neuen Aktionen



  5. Starten Sie Play




Projekt


Fazit


Einige Punkte sollten beachtet werden. Bei einigen Aktionen in SteamVR Imput kann Unity lange nachdenken. Im Prinzip können Sie diese Änderungen selbst im Code vornehmen. Anstatt die Controller-Bindung zu verwenden, können Sie JSON-Dateien direkt bearbeiten. Es besteht jedoch ein großes Risiko, dass Fehler nur schwer abgefangen werden können.


Für eine eingehendere Untersuchung des Plugins - es ist nützlich, die Beispiele im Detail zu studieren und natürlich - lesen Sie die Dokumentation.

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


All Articles