Laborator VR 03. SteamVR. Advanced input

În acest laborator vom studia câteva aspecte mai avansate legate de input - cum îl putem citi în cod, cum ne putem defini acțiuni custom - veți reuni aceste aspecte în implementarea unui gravity gun!

Acest laborator presupune deja că aveți setup-ul de SteamVR funcțional pentru un anumit headset. Laboratorul precedent prezintă în detaliu acest setup, așdar urmați pașii descriși acolo dacă este cazul, aceste aspecte nu vor fi prezentate aici.

Notă referitoare la sistemele de input din Unity și SteamVR

Sistemul de input folosit în SteamVR se bazează pe acțiuni.

Înainte să discutăm despre el, vom prezenta pe scurt cele 2 paradigme de folosire a input-ului în Unity.

Următoarele subsecțiuni au scop informativ, nu sunt esențiale în rezolvarea laboratorului, dar vă introduc în modalitatea în care se prelucreză input-ul în SteamVR.

Sistemul de input legacy (Unity)

În sistem legacy de input din Unity testarea acțiunilor este destul de simplă în cod, dar are câteva limitări. În cel mai simplu mod, input-ul se poate testa prin polling-ul unor butoane de input specifice.

public class LegacyInputExample : MonoBehaviour
{
    // Update is needed to poll every frame.
    private void Update()
    {
        // I want 3 buttons to call the same logic: LMB (mouse), Space (key), LCtrl (key).
        // In the legacy system, I need to hardcode the keycodes.
        if (Input.GetMouseButtonDown(0) || 
            Input.GetKeyDown(KeyCode.Space) || 
            Input.GetKeyDown(KeyCode.LeftControl))
        {
            Debug.Log("[LegacyInputExample] Action performed!");
        }
    }
}

Sistemul de input baza pe acțiuni (Unity)

Noul sistem de input abstractizează partea de testare manuală a acestor input-uri prin mapping-uri și acțiuni.

Un mapping definește un set de acțiuni legate de anumite input-uri fizice (cum ar fi tastatura, mouse-ul, controller-ele, etc.). Practic, un mapping este o schemă de control care asociază diferite acțiuni cu diverse metode de input. Puteți să vă gândiți la un mapping ca la modul în care un player interacționează cu jocul în funcție de context. Câteva exemple de mapping-uri:

  • MovementOnFoot pentru mișcare pedestră (WASD pe tastatură sau joystick pe un controller)
  • MovementInCar pentru condusul unei mașini (aceleași taste WASD pe tastatură sau joystick pe un controller, dar într-un alt context!)
  • MovementInBoat pentru navigarea cu o barcă

O acțiune reprezintă o interacțiune definită de utilizator care poate fi activată prin diverse tipuri de input-uri. Acțiunile se pot lega la una sau mai multe intrări fizice printr-un binding.

Pentru a folosi acest sistem de input:

  • Trebuie să aveți pachetul Input System integrat în proiect
  • La o cale dorită în Project window din Unity, click-dreapta → Create → Input Actions
  • În acest asset puteți crea mapping-uri, acțiuni și binding-uri, un exemplu este prezentat în imaginea de mai jos.
  • De asemenea, este util să bifați Generate C# Class din inspectorul acestui asset pentru a-l utiliza mai ușor în script-urile voastre

În final, următorul cod prezintă modul de utilizare al acestui sistem. Comentariile din cod ar trebuie să fie explicative.

public class ActionsInputExample : MonoBehaviour
{
    private InputActionAssetExample playerInputActions;
    private InputAction fireAction;
 
    // Initialize input asset.
    private void Awake() => playerInputActions = new InputActionAssetExample();
 
    // Get the action `Fire` from the mapping `PlayerMapping`. Enable it & bind event.
    private void OnEnable()
    {
        fireAction = playerInputActions.PlayerMapping.Fire;
        fireAction.Enable();
        fireAction.performed += FirePerformed;
    }
 
    private void OnDisable()
    {
        fireAction.Disable();
        fireAction.performed -= FirePerformed;
    }
 
    // Called whenever any of the bindings defined for the `Fire` action are called.
    // No need for explicit checks or the `Update` method!
    private void FirePerformed(InputAction.CallbackContext context) => Debug.Log("Fired action!");
}

Sistemul de input SteamVR versus cel din Unity

Sistemul de input din SteamVR nu se bazează în mod expres pe sistemul de input din Unity; SteamVR are un sistem propriu bazat pe acțiuni. Cu toate acestea, conceptele utilizate (acțiuni, mapări, evenimente) sunt foarte similare. Înțelegerea sistemului de input bazat pe acțiuni din Unity vă va ajuta să înțelegeți mai bine modul în care funcționează cel din SteamVR.

Import schelet laborator

  • Importați ultima versiune a pachetului IRVA_L3_VR_SteamVR_Skeleton care se găsește în folder-ul UnityPackages din folder-ul root al proiectului
  • Folder-ul Assets → L3_VR_SteamVR_Advanced conține asset-urile suport pentru acest laborator. Deschideți scena L3_VR_SteamVR_GravityGun

Această scenă conține prefab-ul de player, câteva mese pe care se află câteva obiecte, precum și un gravity gun.

Sistemul de input din SteamVR

Precum a fost menționat anterior, sistemul de input din SteamVR se bazează pe acțiuni.

Deschideți fereastra de editor din Window → SteamVR Input. Aici o să vedeți definite:

  • Action sets, seturi de acțiuni, analog cu un mapping din sistemul de input din Unity. Fiecare action set are un set de acțiuni; puteți, de asemenea, crea seturi noi de acțiuni
  • Actions, definite pentru fiecare set de acțiuni

In cod puteți face referire atât la Action sets cât și la Actions. Pentru a învăța modul de utilizare, urmăriți următorii pași:

  • În scena suport din laborator găsiți obiectul [SteamVRInputActionsTestingObject] care are atașat script-ul SteamVRInputActionsTesting, pe care-l va trebui să-l completați
  • În continuare vom interoga acțiunea GrabPinch din action set-ul default. Acesta este legat de buton fizic de trigger al controller-ului (vedem imediat cum)

Citire input prin polling

O metodă validă pentru a realiza acest task este prin polling. Urmăriți comentariile din snippet pentru detalii.

public class SteamVRInputActionsTesting : MonoBehaviour
{
    private void Update()
    {
        // Explanation:
        //  - From the `_default` mapping (action set), get the `GrabPinch` action's state via `GetState`.
        //  - `SteamVR_Input_Sources` can be used to set a specific device to read from. In this case any device which has this action.
        var grabPinchState = SteamVR_Actions._default.GrabPinch.GetState(SteamVR_Input_Sources.Any);
        Debug.Log($"[SteamVRInputActionsTesting] Polling: grabPinchState = {grabPinchState}");
    }
}

Pe lângă GetState puteți să folosiți și alte metode, care vă pot indica tranziții de la true la false (sau invers), etc.

Citire input folosind evenimente (metodă recomandată)

O altă metodă pentru a citi același input se bazează pe evenimente. Recomandăm această metodă întrucât face bypass polling-ului și considerăm că este un mod mai modular de a citi acest input.

public class SteamVRInputActionsTesting : MonoBehaviour
{
    // Subscribe to event `onChange` from the `GrabPinch` contained in the `_default` action set
    // and call `OnGrabPinchChanged` on invocations.
    private void OnEnable() => SteamVR_Actions._default.GrabPinch.onChange += OnGrabPinchChanged;
 
    // Unsubscribe from event.
    private void OnDisable() => SteamVR_Actions._default.GrabPinch.onChange -= OnGrabPinchChanged;
 
    // Method called when `onChange` from the `GrabPinch` is invoked.
    private void OnGrabPinchChanged(SteamVR_Action_Boolean fromAction, SteamVR_Input_Sources fromSource, bool grabPinchState)
    {
        Debug.Log($"[SteamVRInputActionsTesting] Events: grabPinchState = {grabPinchState}");
    }
}

Pe lângă onChange puteți să vă abonați și la alte evenimente, care vă pot indica tranziții de la true la false (sau invers), etc.

Task-ul vostru

Pentru a exersa aceste aspecte implementați una din cele două metode prezentate mai sus în script-ul SteamVRInputActionsTesting pentru acțiunea de GrabPinch și testați-o (similar GIF-urilor prezentate mai sus).

Binding-uri în SteamVR

Pentru a extinde funcționalitatea standard și a vă permite să ve definiți acțiuni noi, este necesar să folosiți sistemul de binding implementat în SteamVR.

Din mediul Window → SteamVR Input apăsați pe opțiunea Open binding UI. Ar trebui să vi să deschidă o nouă fereastră ce prezintă binding-ul curent pentru controller-ele pe care le folosiți.

Editați binding-ul curent (click pe Edit de pe oculus_touch (Local changes).

  • În noua ferestră puteți lega butoanele fizice de acțiuni (acesta este în esență un binding)
  • În partea de sus puteți naviga prin diverse action set-uri (default, platformer, etc.). Ramâneți pentru moment pe cel default.
  • Observați în partea stăngă binding-urile deja configurate pentru Trigger - găsiți aici acțiunea de Grab Pinch

  • Ca să legăm informația învățată: Acest Grab Pinch este o acțiune binded pe butonul de Trigger, conținută în action set-ul default
  • În spate, SteamVR va genera cod care ne permite să referențiem în script-urile noastre aceste elemente
  • Pe scurt, acesta este motivul pentru care putem referenția în cod SteamVR_Actions._default.GrabPinch

My first (new) SteamVR binding

În continuare veți învăța cum să vă creați propriile acțiuni. Pentru simplitate ne vom folosi de action set-ul default și de butonul fizic de Trigger.

Ca și exemplu, vom crea o nouă acțiune de atingere (touch) a trigger-ului

Controller-ele Meta Quest 2 și 3 oferă o formă de input prin atingerea unora dintre butoane. Pentru a declanșa acest tip de eveniment este suficient să țineți degetul pe buton (fără a-l apăsa). Ne vom folosi de acest feature în continuare.

Pentru început, este necesar să vă definiți întâi o nouă acțiune - în acest exemplu vom crea în fereastra SteamVR Input o nouă acțiune TouchTrigger în action set-ul default.

  • Definiți noua acțiune
  • Apăsați pe Save and generate

În continuare trebuie să realizăm binding-ul.

  • Din fereastra de binding, găsiți intrarea pentru Trigger si apăsați pe (+). O să vi se deschidă un prompt care vă întreabă ce mod de input doriți să asignați acestui buton fizic

  • Opțiunea Button vă expune parametrii cum ar fi Click și Touch
  • Opțiunea Trigger vă expune parametrii cum ar fi Click, Touch dar și Pull (valoare 0-1 cât de mult este apăsat acest trigger)

Așadar, acest sistem be binding vă permite astfel să configurați comportamentul logic al butonului fizic.

  • Pentru exemplul nostru puteți alege oricare variantă - pentru simplitate am ales modul Button
  • Ar trebui acum să aveți o nouă intrare sub meniul de Trigger

Următorul pas necesar este configurarea acțiunii:

  • Apăsați pe None în drept-ul evenimentului de Touch
  • Meniul care se va deschide vă va cere să alegeți acțiunea pe care doriți s-o legați - alegeți noua acțiune creată, touchtrigger

Awesome 🎉! În acest moment toată logica necesară acestei acțiuni (și al binding-ului) este finalizată.

Task-ul vostru

Pentru a testa dacă această nouă acțiune a fost configurată corect, completați script-ul SteamVRInputActionsTesting și afișați mesaje în consolă atunci când interogați această nouă acțiune TouchTrigger.

  • Puteți utilza oricare din cele două metode prezentate (polling via Update sau folosind evenimente)

Rendering stereo

În aplicațiile de VR dezvolatate cu Unity se poate seta parametrul Stereo Rendering Mode, care specifică modalitatea de desenare a scenei în realitate virtuală - acest aspect este controlabil întrucât în general există două ecrane (unul pentru fiecare ochi) care trebuie actualizate și există câteva moduri de bază:

  • Multipass: Scena este desenată de două ori - o dată pentru fiecare ochi
  • Single Pass: Scena este desenată o singură dată (într-un singur render pass, într-o singură textură)
  • Single Pass Instanced: La fel cu Single Pass dar folosește și tehnica de GPU instancing (aceasta grupează mai eficient draw call-uri, reducându-le numărul)

Metodele de tip single pass sunt cele mai eficiente din punct de vedere computațional, dar pot suferi de incompatibilități cu diverse shadere. Un exemplu folosit în acest laborator este componenta de tip LineRenderer, care nu este desenată corect în cazul desenării single pass.

Pentru acest laborator va trebui să schimbați din Edit → Project Settings → XR Plug-In Management → OpenVR Stereo Rendering Mode în Multipass.

Este recomandat să dezvoltați aplicațiile în modurile cele mai eficiente (single pass) și să rețineți această setare pentru situații similare în viitor. În cadrul acestui laborator compromisul folosind Multipass este preferat pentru a ne ușura munca.

Gravity gun

Pentru a aplica cunoștințele pe care le-ați învățat pe parcursul acest laborator, va trebuie să finalizați implementarea unui gravity gun.

Logica de funcționare a acestui gravity gun este deja implimentată, voi va trebui să configurați și să legați input-ului necesar folosind acțiuni și binding-uri.

Obiectul [GravityGun] din scena suport este de tip Throwable, așadar îi puteți face grab.

Vă recomandăm (pentru acest obiect în mod particulat) să faceți grab folosind doar grip-ul (butonul lateral), întrucât trigger-ul va fi folosit pentru logica de funcționare.

Funcționare gravity gun

După ce gravity gun-ul a fost grabbed, va trebui să implementați următorul comportament:

  • Dacă nu este apăsat nici un alt buton, nu trebuie să faceți nimic
  • Dacă butonul de trigger este atins (touched), iar un obiect este în raza sa de acțiune, are loc un efect de snap al acelui obiect - acesta este adus și fixat de arma gravitațională
  • Dacă butonul de trigger este lăsat liber (eveniment-ul eferent touch-ului trece din true în false, obiectul snapped este tossed, anume lăsat să cadă liber
  • Dacă un obiect este snapped, iar butonul de trigger este apăsat (click), obiectul este propulsat (throw)

Detalii implementare

Logica de funcționare este implementată în GravityGunController, iar obiectele interactibile care interacționează cu gravity gun-ul implementează GravityGunObject.

  • În GravityGunController aveți metodele SnapObject, TossObject și ThrowObject deja implementate - acestea vor trebui apelate pe baza input-ului definit de voi
  • Veți avea nevoie în esență de 2 acțiuni (de ex. GravityGunTouch și GravityGunClick)
  • Va trebui să vă definiți 2 binding-uri (de ex. pe evenimentele de tip Click și Touch din meniul de bind-uri SteamVR)
  • În GravityGunController va trebui să implementați logica pentru input-urile definite anterior
    • Dacă optați pentru polling pe Update, vedeți ce metode de input trebuie să folosiți (de ex. GetStateUp, GetStateDown, etc.)
    • Dacă optați pentru implementarea bazată pe evenimente, vedeți ce evenimente de input trebuie să folosiți (de ex. onStateUp, onStateDown, etc.)
  • Legați input-ul de metodele de SnapObject, TossObject și ThrowObject pentru a finaliza implementarea

Task-uri

  1. Implementați în SteamVRInputActionsTesting logica de citire a input-ului pentru acțiunea GrabPinch folosind oricare dintre metodele prezentate (polling, evenimente). Afișați rezultatul în consolă pentru a confirma funcționarea
    • Faceți referire la secțiunile Citire input prin polling și Citire input folosind evenimente (metodă recomandată) pentru snippet-uri din cod și explicații suplimentare
  2. Creați-vă o nouă acțiune TouchTrigger, realizați-i binding-ul pe logica de Touch și implementați în SteamVRInputActionsTesting logica de citire a input-ului. Afișați rezultatul în consolă pentru a confirma funcționarea
    • Faceți referire la secțiunea My first (new) SteamVR binding pentru explicații suplimentare
  3. Finalizați logica pentru gravity gun
    • Definiți-vă noile acțiuni și binding-uri
    • Legați input-ul în script-ul GravityGunController și apelați metodele SnapObject, TossObject și ThrowObject pe baza acestuia
    • Faceți referire la capitolul Gravity gun (și subcapitolele acestuia) pentru explicații suplimentare
  4. [✨Bonus✨] Implementați o mecanică de rotire a obiectului snapped de către gravity gun folosind stick-ul direcțional (joystick) al controller-ului
    • Modalitatea de transformare a input-ului oferit de joystick în rotația efectivă este la latitudinea voastră (mai precis trebuie să vedeți față de ce axe (X, Y, Z, locale, globale) rotați bazat pe valoarea input-ului
    • Modulați viteza de rotație în funcție de valoarea analogică (0-1) oferită de joystick
irva/laboratoarevr/03.txt · Last modified: 2024/10/29 12:34 by andrei.lapusteanu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0