الوحدة والحيوية: تعليمي

قررت في إجازة لدراسة تطوير Vive in Unity3D. غوغل بضعة أمثلة وبدأت في المحاولة ، ولكن لسبب ما لم ينجح. بعد أن بدأت تفهم بمزيد من التفاصيل ووجدت أن Valve طرحت مؤخرًا تحديثًا للمكون الإضافي لـ Unity3D - إصدار جديد تم إعادة بنائه بشدة. ظهر فيها ابتكاران أساسيان جعلا الدروس القديمة غير ذات صلة. قررت أن أكتب واحدة جديدة



سنحتاج إلى Unity> = 5.4.0 والمكوّن الإضافي SteamVR الإضافي الجديد ( GitHub )


في البرنامج المساعد نفسه ، هناك ثلاثة ملفات PDF مفيدة للتعرف


\ الأصول \ SteamVR \ SteamVR Unity Plugin.pdf
\ الأصول \ SteamVR \ SteamVR Unity Plugin - Input System.pdf
\ الأصول \ SteamVR \ InteractionSystem \ InteractionSystem.pdf


ومثالين:


\ الأصول \ SteamVR \ Simple Sample.unity
\ الأصول \ SteamVR \ InteractionSystem \ Samples \ Interactions_Example.unity


يدعم المكون الإضافي وضع المحاكاة - يتم تشغيله إذا لم يتم تشغيل الخوذة



حسنا ، الآن خطوة بخطوة.


  1. قم بإنشاء مشروع قالب ثلاثي الأبعاد جديد باستخدام المكون الإضافي SteamVR


  2. نحن نتفق مع الإعدادات



  3. الآن النقطة الأساسية هي أنك تحتاج إلى تكوين الإدارة. حدد عنصر القائمة Window \ SteamVR Imput



  4. ستسأل الوحدة عن الإجراءات المفقودة. json وعرض نسخ ملف المثال (الموجود في \ Assets \ SteamVR \ Input \ ExampleJSON) - أنصحك بالموافقة.



    تظهر ملفات json أن المكون الإضافي مصمم ليس فقط لـ Vive ، ولكن أيضًا لـ Oculus و Windows MR ، بالإضافة إلى وحدات التحكم المفصلة الجديدة. ترتبط التغييرات الرئيسية بهذا.


  5. في النافذة التي تفتح ، ما عليك سوى النقر فوق "حفظ وإنشاء"



  6. تحتاج الآن إلى إضافة المشغل (المشغل من \ Assets \ SteamVR \ InteractionSystem \ Core \ Prefabs) إلى المشهد وإزالة الكاميرا الرئيسية



    لكي يعمل وضع المحاكاة - يجب تمكينه في خصائص المشغل



    من المفيد أيضًا نسخ مجلد \ Assets \ SteamVR \ InteractionSystem \ Core \ Icons إلى \ Assets وإعادة تسميته إلى Gizmos


  7. يمكنك النقر فوق تشغيل - ولكن لن تعمل سوى اليد المزيفة في المحاكاة ، تحتاج إلى نسخ البرنامج النصي من هنا وتعليقه على Player-> NoSteamVRFallbackObjects-> FallbackHand



رمز النص اليدوي المزيف
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; } } } 

في وضع VR مع تشغيل وحدات التحكم - ستكون مرئية



  1. أضفنا VR ، ولكن لا يمكننا حتى التنقل. البرنامج المساعد لديه تطبيق النقل عن بعد
    لتمكينه ، تحتاج إلى الإضافة إلى مشهد النقل الفوري من \ Assets \ SteamVR \ InteractionSystem \ Teleport \ Prefabs
    وأيضا ترتيب TeleportPoint من هناك



    يمكنك أيضًا الانتقال الفوري في التقليد باستخدام مفتاح T.


  2. يمكنك إنشاء سطح انتقال فوري - قم بإنشاء طائرة وتعليق البرنامج النصي TeleportArea.cs من \ Assets \ SteamVR \ InteractionSystem \ Teleport \ Scripts عليه



  3. دعونا نحاول التفاعل مع الكائنات - قم بإنشاء مكعب وتعليق البرنامج النصي Interactable.cs من \ Assets \ SteamVR \ InteractionSystem \ Core \ Scripts عليه
    الآن يتم تسليط الضوء عليه ، ولكن لا شيء يحدث له



  4. نحن بحاجة إلى تسجيل التفاعل - سنقوم بإنشاء برنامج نصي جديد لـ Cube



كود للتفاعل
 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); } } } 


يمكن العثور على مزيد من التفاصيل حول التفاعل في أمثلة المكون الإضافي ، ولا سيما في \ Assets \ SteamVR \ InteractionSystem \ Samples \ Scripts \ InteractableExample.cs


ثم سنحاول القيام بما هو غير موجود في الأمثلة - أضف إجراءات جديدة


  1. افتح البرنامج النصي Player.cs وأضف الحقول


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

    يمكن العثور على أنواع الإرجاع الصالحة في \ Assets \ SteamVR \ Input



وطريقة التحديث:


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

كود Player.cs الكامل
 //======= 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. قم بتشغيل Window \ SteamVR Imput ، وقم بإنشاء إجراءاتنا في المجموعة الافتراضية وحفظها ، حدد الآن "فتح واجهة مستخدم الربط" (يجب تشغيل SteamVR وتشغيل وحدة تحكم واحدة على الأقل)



  2. تفتح علامة التبويب ربط وحدة التحكم في المستعرض - تحتاج فيها إلى تكوين اتصال إجراءاتنا مع وحدات التحكم: سنعلق PlayerMove على TRACKPAD الأيسر (لا تنس إيقاف تشغيل وضع المرآة) ، PlayerRotate على TRACKPAD الأيمن ، وتعليق القائمة انقر على مفاتيح القائمة



  3. أغلق ربط وحدة التحكم واحفظ التغييرات.


  4. في خصائص المشغل ، نربط الإجراءات الجديدة



  5. شغّل Play




المشروع


الخلاصة


يجب ملاحظة بضع نقاط. يمكن لبعض الإجراءات في SteamVR Imput أن تجعل Unity تفكر لفترة طويلة ، من حيث المبدأ ، يمكنك إجراء هذه التغييرات بنفسك في التعليمات البرمجية ، وبدلاً من استخدام وحدة تحكم Binding ، يمكنك تحرير ملفات json مباشرة ، ولكن هناك خطر كبير من الأخطاء التي يصعب اكتشافها.


لدراسة أعمق للبرنامج المساعد - من المفيد دراسة الأمثلة بالتفصيل ، وبالطبع - اقرأ الوثائق.

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


All Articles