Řadiče HP Reverb G2 v Unity

Ovladače pohybu HP jsou zcela novým typem Windows Mixed Reality kontrolerů: stejná technologie sledování s mírně odlišnou sadou dostupných vstupů:

  • Touchpad byl nahrazen dvěma tlačítky: A a B pro pravý ovladač a X a Y pro levý ovladač.
  • Funkce Uchopení je teď trigger, který publikuje datový proud hodnot mezi 0,0 a 1,0 místo tlačítka se stavy Stisknutí a Nestisknuto.

Vzhledem k tomu, že nové vstupy nejsou přístupné prostřednictvím stávajících rozhraní API pro Windows a Unity, potřebujete vyhrazený balíček UPM Microsoft.MixedReality.Input .

Důležité

Třídy v tomto balíčku nenahrazovat stávající rozhraní API pro Windows a Unity, ale doplňují je. Funkce, které jsou běžně dostupné pro klasické ovladače Windows Mixed Reality i ovladače POHYBU HP, jsou přístupné přes stejnou cestu kódu pomocí existujících rozhraní API. Pouze nové vstupy vyžadují použití dalšího balíčku Microsoft.MixedReality.Input.

Přehled ovladače HP Motion Controller

Microsoft.MixedReality.Input.MotionController představuje ovladač pohybu. Každá instance MotionControllerXR. WSA. Partnerský uzel Input.InteractionSource , který lze korelovat pomocí předání, ID dodavatele, ID produktu a verze.

Instance MotionControlleru můžete získat tak, že vytvoříte MotionControllerWatcher a předplacete jeho události, podobně jako pomocí událostí InteractionManager ke zjišťování nových instancí InteractionSource . Metody a vlastnosti MotionControlleru popisují vstupy podporované ovladačem, včetně jeho tlačítek, triggerů, 2D osy a páčky. Třída MotionController také zveřejňuje metody pro přístup ke vstupním stavům prostřednictvím Třídy MotionControllerReading . MotionControllerReading Třída představuje snímek stavu kontroleru v daném čase.

Instalace Microsoft.MixedReality.Input pomocí nástroje Mixed Reality Feature Tool

Nainstalujte modul plug-in Microsoft.MixedReality.Input s novou aplikací Mixed Reality Feature Tool. Postupujte podle pokynů k instalaci a použití a v kategorii Mixed Reality Toolkit vyberte balíček Mixed Reality Input:

Mixed Reality okno balíčků nástrojů funkcí se zvýrazněným vstupem hybridní reality

Použití Microsoft.MixedReality.Input

Vstupní hodnoty

MotionController může vystavit dva druhy vstupů:

  • Tlačítka a stavy triggeru jsou vyjádřeny jedinečnou plovoucí hodnotou mezi 0,0 a 1,0, která označuje, jak moc jsou stisknuta.
    • Tlačítko může vrátit hodnotu 0,0 (pokud není stisknuto) nebo 1.0 (při stisknutí), zatímco spoušť může vrátit spojité hodnoty mezi 0,0 (plně uvolněno) až 1.0 (plně stisknuto).
  • Stav thumbsticku je vyjádřen vektorem 2, jehož komponenty X a Y jsou mezi -1,0 a 1,0.

Pomocí metody MotionController.GetPressableInputs() můžete vrátit seznam vstupů vracejících stisknutou hodnotu (tlačítka a triggery) nebo metodu MotionController.GetXYInputs() k vrácení seznamu vstupů vracejících hodnotu ve 2 osách.

Instance MotionControllerReading představuje stav kontroleru v daném okamžiku:

  • GetPressedValue() načte stav tlačítka nebo triggeru.
  • GetXYValue() načte stav páčky.

Vytvoření mezipaměti pro údržbu kolekce instancí MotionControlleru a jejich stavů

Začněte vytvořením instance MotionControllerWatcher a registrací obslužných rutin pro jeho MotionControllerAdded a MotionControllerRemoved události zachovat mezipaměť dostupných instancí MotionController. Tato mezipaměť by měla být MonoBehavior připojená k objektu GameObject, jak je znázorněno v následujícím kódu:

public class MotionControllerStateCache : MonoBehaviour 
{ 
    /// <summary> 
    /// Internal helper class which associates a Motion Controller 
    /// and its known state 
    /// </summary> 
    private class MotionControllerState 
    { 
        /// <summary> 
        /// Construction 
        /// </summary> 
        /// <param name="mc">motion controller</param>` 
        public MotionControllerState(MotionController mc) 
        { 
            this.MotionController = mc; 
        } 

        /// <summary> 
        /// Motion Controller that the state represents 
        /// </summary> 
        public MotionController MotionController { get; private set; } 
        … 
    } 

    private MotionControllerWatcher _watcher; 
    private Dictionary<Handedness, MotionControllerState> 
        _controllers = new Dictionary<Handedness, MotionControllerState>(); 

    /// <summary> 
    /// Starts monitoring controller's connections and disconnections 
    /// </summary> 
    public void Start() 
    { 
        _watcher = new MotionControllerWatcher(); 
        _watcher.MotionControllerAdded += _watcher_MotionControllerAdded; 
        _watcher.MotionControllerRemoved += _watcher_MotionControllerRemoved; 
        var nowait = _watcher.StartAsync(); 
    } 

    /// <summary> 
    /// Stops monitoring controller's connections and disconnections 
    /// </summary> 
    public void Stop() 
    { 
        if (_watcher != null) 
        { 
            _watcher.MotionControllerAdded -= _watcher_MotionControllerAdded; 
            _watcher.MotionControllerRemoved -= _watcher_MotionControllerRemoved; 
            _watcher.Stop(); 
        } 
    }

    /// <summary> 
    /// called when a motion controller has been removed from the system: 
    /// Remove a motion controller from the cache 
    /// </summary> 
    /// <param name="sender">motion controller watcher</param> 
    /// <param name="e">motion controller </param> 
    private void _watcher_MotionControllerRemoved(object sender, MotionController e) 
    { 
        lock (_controllers) 
        { 
            _controllers.Remove(e.Handedness); 
        } 
    }

    /// <summary> 
    /// called when a motion controller has been added to the system: 
    /// Remove a motion controller from the cache 
    /// </summary> 
    /// <param name="sender">motion controller watcher</param> 
    /// <param name="e">motion controller </param> 
    private void _watcher_MotionControllerAdded(object sender, MotionController e) 
    { 
        lock (_controllers) 
        { 
            _controllers[e.Handedness] = new MotionControllerState(e); 
        } 
    } 
} 

Čtení nových vstupů pomocí dotazování

Aktuální stav každého známého kontroleru můžete číst prostřednictvím MotionController.TryGetReadingAtTime během metody Update třídy MonoBehavior. Jako parametr časového razítka chcete předat DateTime.Now , abyste zajistili, že se přečte nejnovější stav kontroleru.

public class MotionControllerStateCache : MonoBehaviour 
{ 
    … 

    private class MotionControllerState 
    {
        … 

        /// <summary> 
        /// Update the current state of the motion controller 
        /// </summary> 
        /// <param name="when">time of the reading</param> 
        public void Update(DateTime when) 
        { 
            this.CurrentReading = this.MotionController.TryGetReadingAtTime(when); 
        } 

        /// <summary> 
        /// Last reading from the controller 
        /// </summary> 
        public MotionControllerReading CurrentReading { get; private set; } 
    } 

    /// <summary> 
    /// Updates the input states of the known motion controllers 
    /// </summary> 
    public void Update() 
    { 
        var now = DateTime.Now; 

        lock (_controllers) 
        { 
            foreach (var controller in _controllers) 
            { 
                controller.Value.Update(now); 
            } 
        } 
    } 
} 

Aktuální vstupní hodnotu kontrolerů můžete uchopit pomocí možnosti Ovládání ovladače:

public class MotionControllerStateCache : MonoBehaviour 
{ 
    … 
    /// <summary> 
    /// Returns the current value of a controller input such as button or trigger 
    /// </summary> 
    /// <param name="handedness">Handedness of the controller</param> 
    /// <param name="input">Button or Trigger to query</param> 
    /// <returns>float value between 0.0 (not pressed) and 1.0 
    /// (fully pressed)</returns> 
    public float GetValue(Handedness handedness, ControllerInput input) 
    { 
        MotionControllerReading currentReading = null; 

        lock (_controllers) 
        { 
            if (_controllers.TryGetValue(handedness, out MotionControllerState mc)) 
            { 
                currentReading = mc.CurrentReading; 
            } 
        } 

        return (currentReading == null) ? 0.0f : currentReading.GetPressedValue(input); 
    } 

    /// <summary> 
    /// Returns the current value of a controller input such as button or trigger 
    /// </summary> 
    /// <param name="handedness">Handedness of the controller</param> 
    /// <param name="input">Button or Trigger to query</param> 
    /// <returns>float value between 0.0 (not pressed) and 1.0 
    /// (fully pressed)</returns> 
    public float GetValue(UnityEngine.XR.WSA.Input.InteractionSourceHandedness handedness, ControllerInput input) 
    { 
        return GetValue(Convert(handedness), input); 
    } 

    /// <summary> 
    /// Returns a boolean indicating whether a controller input such as button or trigger is pressed 
    /// </summary> 
    /// <param name="handedness">Handedness of the controller</param> 
    /// <param name="input">Button or Trigger to query</param> 
    /// <returns>true if pressed, false if not pressed</returns> 
    public bool IsPressed(Handedness handedness, ControllerInput input) 
    { 
        return GetValue(handedness, input) >= PressedThreshold; 
    } 
} 

Pokud například chcete přečíst analogovou hodnotu uchopování z InteractionSource:

/// Read the analog grasp value of all connected interaction sources 
void Update() 
{ 
    … 
    var stateCache = gameObject.GetComponent<MotionControllerStateCache>(); 
    foreach (var sourceState in InteractionManager.GetCurrentReading()) 
    { 
        float graspValue = stateCache.GetValue(sourceState.source.handedness, 
            Microsoft.MixedReality.Input.ControllerInput.Grasp);
        … 
    }
} 

Generování událostí z nových vstupů

Místo dotazování stavu kontroleru jednou pro každý snímek máte možnost zpracovávat všechny změny stavu jako události, což vám umožní zpracovat i ty nejrychlejší akce, které trvají méně než rámec. Aby tento přístup fungoval, musí mezipaměť kontrolerů pohybu zpracovat všechny stavy publikované kontrolerem od posledního snímku, což můžete udělat uložením časového razítka posledního MotionControllerReading načteného z MotionControlleru a voláním MotionController.TryGetReadingAfterTime():

private class MotionControllerState 
{ 
    … 
    /// <summary> 
    /// Returns an array representng buttons which are pressed 
    /// </summary> 
    /// <param name="reading">motion controller reading</param> 
    /// <returns>array of booleans</returns> 
    private bool[] GetPressed(MotionControllerReading reading) 
    { 
        if (reading == null) 
        { 
            return null; 
        } 
        else 
        { 
            bool[] ret = new bool[this.pressableInputs.Length]; 
            for (int i = 0; i < pressableInputs.Length; ++i) 
            { 
                ret[i] = reading.GetPressedValue(pressableInputs[i]) >= PressedThreshold; 
            } 

            return ret; 
        } 
    } 

    /// <summary> 
    /// Get the next available state of the motion controller 
    /// </summary> 
    /// <param name="lastReading">previous reading</param> 
    /// <param name="newReading">new reading</param> 
    /// <returns>true is a new reading was available</returns> 
    private bool GetNextReading(MotionControllerReading lastReading, out MotionControllerReading newReading) 
    { 
        if (lastReading == null) 
        { 
            // Get the first state published by the controller 
            newReading = this.MotionController.TryGetReadingAfterSystemRelativeTime(TimeSpan.FromSeconds(0.0)); 
        } 
        else 
        { 
            // Get the next state published by the controller 
            newReading = this.MotionController.TryGetReadingAfterTime(lastReading.InputTime); 
        } 

        return newReading != null; 
    } 

    /// <summary> 
    /// Processes all the new states published by the controller since the last call 
    /// </summary> 
    public IEnumerable<MotionControllerEventArgs> GetNextEvents() 
    {
        MotionControllerReading lastReading = this.CurrentReading; 
        bool[] lastPressed = GetPressed(lastReading); 
        MotionControllerReading newReading; 
        bool[] newPressed; 

        while (GetNextReading(lastReading, out newReading)) 
        { 
            newPressed = GetPressed(newReading); 

            // If we have two readings, compare and generate events 
            if (lastPressed != null) 
            { 
                for (int i = 0; i < pressableInputs.Length; ++i) 
                { 
                    if (newPressed[i] != lastPressed[i]) 
                    { 
                        yield return new MotionControllerEventArgs(this.MotionController.Handedness, newPressed[i], this.pressableInputs[i], newReading.InputTime); 
                    } 
                } 
            } 

            lastPressed = newPressed; 
            lastReading = newReading; 
        } 

        // No more reading 
        this.CurrentReading = lastReading; 
    } 
} 

Teď, když jste aktualizovali interní třídy mezipaměti, může třída MonoBehavior zveřejnit dvě události – Pressed a Released – a vyvolat je ze své metody Update():

/// <summary> 
/// Event argument class for InputPressed and InputReleased events 
/// </summary> 
public class MotionControllerEventArgs : EventArgs 
{ 
    public MotionControllerEventArgs(Handedness handedness, bool isPressed, rollerInput input, DateTime inputTime) 
    { 
        this.Handedness = handedness; 
        this.Input = input; 
        this.InputTime = inputTime; 
        this.IsPressed = isPressed; 
    } 

    /// <summary> 
    /// Handedness of the controller raising the event 
    /// </summary> 
    public Handedness Handedness { get; private set; } 

    /// <summary> 
    /// Button pressed or released 
    /// </summary> 
    public ControllerInput Input { get; private set; } 

    /// <summary> 
    /// Time of the event 
    /// </summary> 
    public DateTime InputTime { get; private set; } 

    /// <summary> 
    /// true if button is pressed, false otherwise 
    /// </summary> 
    public bool IsPressed { get; private set; } 
} 

/// <summary> 
/// Event raised when a button is pressed 
/// </summary> 
public event EventHandler<MotionControllerEventArgs> InputPressed; 

/// <summary> 
/// Event raised when a button is released 
/// </summary> 
public event EventHandler<MotionControllerEventArgs> InputReleased; 

/// <summary> 
/// Updates the input states of the known motion controllers 
/// </summary> 
public void Update() 
{ 
    // If some event handler has been registered, we need to process all states  
    // since the last update, to avoid missing a quick press / release 
    if ((InputPressed != null) || (InputReleased != null)) 
    { 
        List<MotionControllerEventArgs> events = new <MotionControllerEventArgs>(); 

        lock (_controllers) 
        { 
            foreach (var controller in _controllers) 
            { 
                events.AddRange(controller.Value.GetNextEvents()); 
            } 
        } 
 
        // Sort the events by time 
        events.Sort((e1, e2) => DateTime.Compare(e1.InputTime, e2.InputTime)); 

        foreach (MotionControllerEventArgs evt in events) 
        { 
            if (evt.IsPressed && (InputPressed != null)) 
            { 
                InputPressed(this, evt); 
            } 
            else if (!evt.IsPressed && (InputReleased != null)) 
            { 
                InputReleased(this, evt); 
            } 
        } 
    } 
    else 
    { 
        // As we do not predict button presses and the timestamp of the next e is in the future 
        // DateTime.Now is correct in this context as it will return the latest e of controllers 
        // which is the best we have at the moment for the frame. 
        var now = DateTime.Now; 
        lock (_controllers) 
        { 
            foreach (var controller in _controllers) 
            { 
                controller.Value.Update(now); 
            } 
        } 
    } 
} 

Struktura ve výše uvedených příkladech kódu usnadňuje registraci událostí:

public InteractionSourceHandedness handedness; 
public Microsoft.MixedReality.Input.ControllerInput redButton;

// Start of the Mono Behavior: register handlers for events from cache 
void Start() 
{ 
    var stateCache = gameObject.GetComponent<MotionControllerStateCache>(); 
    stateCache.InputPressed += stateCache_InputPressed; 
    stateCache.InputReleased += stateCache_InputReleased; 
    … 
} 

// Called when a button is released 
private void stateCache_InputReleased(object sender, MotionControllerStateCache.MotionControllerEventArgs e) 
{ 
    if ((e.SourceHandedness == handedness) && (e.Input == redButton)) 
    { 
        … 
    } 
} 

// Called when a button is pressed 
private void stateCache_InputPressed(object sender, MotionControllerStateCache.MotionControllerEventArgs e) 
{ 
    if ((e.SourceHandedness == handedness) && (e.Input == redButton)) 
    { 
        … 
    } 
} 

Viz také