Controladores de HP reverberación G2 en UnityHP Reverb G2 Controllers in Unity

Los controladores de movimiento de HP son un nuevo tipo de controlador de Windows Mixed Reality: la misma tecnología de seguimiento con un conjunto de entradas disponibles ligeramente diferente:HP Motion controllers are a brand new type of Windows Mixed Reality controllers: all the same tracking technology with a slightly different set of available inputs:

  • Touchpad se ha reemplazado por dos botones: A y B para el controlador derecho y X e y para el controlador izquierdo.Touchpad has been replaced by two buttons: A and B for the right controller, and X and Y for the left controller.
  • El agarre es ahora un desencadenador que publica un flujo de valores entre 0,0 y 1,0 en lugar de un botón con Estados presionados y no presionados.Grasp is now a trigger that publishes a stream of values between 0.0 and 1.0 instead of a button with Pressed and Not Pressed states.

Dado que no se puede acceder a las nuevas entradas a través de las API de Unity y Windows existentes, necesita el paquete UPM Microsoft. MixedReality. Input dedicado.Since the new inputs aren't accessible through existing Windows and Unity APIs, you need the dedicated Microsoft.MixedReality.Input UPM Package.

Importante

Las clases de este paquete no reemplazan a las API existentes de Windows y Unity, sino que las complementan.Classes in this package do not replace existing Windows and Unity APIs but complement them. Las características que normalmente están disponibles para los controladores clásicos de Windows Mixed Reality y los controladores de movimiento de HP son accesibles a través de la misma ruta de acceso del código con las API existentes.Features commonly available to both classic Windows Mixed Reality controllers and HP Motion Controllers are accessible through the same code path using existing APIs. Solo las nuevas entradas requieren el uso del paquete Microsoft. MixedReality. Input adicional.Only the new inputs require the use of the additional Microsoft.MixedReality.Input package.

Introducción al controlador de movimiento de HPHP Motion Controller overview

Microsoft. MixedReality. Input. MotionController representa un controlador de movimiento.Microsoft.MixedReality.Input.MotionController represents a motion controller. Cada instancia de MotionController tiene un XR. WSA. Input. InteractionSource del mismo nivel, que se puede correlacionar mediante la mano, el ID. de proveedor, el ID. de producto y la versión.Each MotionController instance has an XR.WSA.Input.InteractionSource peer, which can be correlated using handedness, vendor ID, product ID, and version.

Puede obtener instancias de MotionController mediante la creación de un MotionControllerWatcher y la suscripción a sus eventos, de forma similar al uso de eventos InteractionManager para detectar nuevas instancias de InteractionSource .You can grab MotionController instances by creating a MotionControllerWatcher and subscribing to its events, similar to using InteractionManager events to discover new InteractionSource instances. Los métodos y propiedades de MotionController describen las entradas admitidas por el controlador, incluidos sus botones, desencadenadores, eje 2D y stick analógico.The MotionController’s methods and properties describe the inputs supported by the controller, including its buttons, triggers, 2D axis, and thumbstick. La clase MotionController también expone métodos para tener acceso a Estados de entrada a través de la clase MotionControllerReading .The MotionController class also exposes methods for accessing input states through the MotionControllerReading class. La clase MotionControllerReading representa una instantánea del estado del controlador en un momento dado.The MotionControllerReading class represents a snapshot of the controller’s state at a given time.

Instalación de Microsoft. MixedReality. Input mediante el administrador de paquetes de UnityInstalling Microsoft.MixedReality.Input using the Unity Package Manager

El administrador de paquetes de Unity usa un archivo de manifiesto (manifest.jsen) para determinar qué paquetes se deben instalar y los registros (servidores) desde los que se pueden instalar.The Unity Package Manager uses a manifest file (manifest.json) to determine which packages to install and the registries (servers) they can be installed from. Antes de poder usar el paquete Microsoft. MixedReality. Input, deberá registrar el servidor de componentes de realidad mixta.Before you can use the Microsoft.MixedReality.Input package, you'll need to register the Mixed Reality component server.

Registrar el servidor de componentes de realidad mixtaRegistering the Mixed Reality component server

Para cada proyecto que vaya a usar el paquete de entrada de realidad mixta, el manifest.jsen el archivo (en la carpeta paquetes) necesita agregar el registro con ámbito de realidad mixta.For each project that will be using the Mixed Reality Input package, the manifest.json file (in the Packages folder) needs the Mixed Reality scoped registry added. Para modificar manifest.jscorrectamente en para admitir la realidad mixta:To properly modify manifest.json to support Mixed Reality: 1. Abra /Packages/manifest.jsen un editor de texto, como Visual Studio Code.Open /Packages/manifest.json in a text editor, such as Visual Studio Code. 2. En la parte superior del archivo de manifiesto, agregue el servidor de realidad mixta a la sección del registro con ámbito y guarde el archivo.At the top of the manifest file, add the Mixed Reality server to the scoped registry section and save the file.

{ 
  "scopedRegistries": [ 
    { 
      "name": "Microsoft Mixed Reality", 
      "url": "https://pkgs.dev.azure.com/aipmr/MixedReality-Unity-Packages/_packaging/Unity-packages/npm/registry/", 
      "scopes": [ 
        "com.microsoft.mixedreality" 
      ] 
    } 
  ], 

Agregar el paquete Microsoft. MixedReality. InputAdding the Microsoft.MixedReality.Input package

Modifique la sección de dependencias del /Packages/manifest.jsen el archivo en el editor de texto para agregar el paquete com. Microsoft. mixedreality. Input y guarde el archivo.Modify the dependencies section of the /Packages/manifest.json file in the text editor to add com.microsoft.mixedreality.input package and save the file.

  "dependencies": { 
    "com.microsoft.mixedreality.input": "0.9.2006", 
  }

Usar Microsoft. MixedReality. InputUsing Microsoft.MixedReality.Input

Valores de entradaInput values

Un MotionController puede exponer dos tipos de entradas:A MotionController can expose two kinds of inputs:

  • Los botones y los Estados de los desencadenadores se expresan mediante un valor Float único entre 0,0 y 1,0 que indica cuánto están presionados.Buttons and trigger states are expressed by a unique float value between 0.0 and 1.0 that indicates how much they're pressed.
    • Un botón solo puede devolver 0,0 (cuando no se presiona) o 1,0 (cuando se presiona) mientras un desencadenador puede devolver valores continuos entre 0,0 (totalmente liberados) a 1,0 (totalmente presionado).A button can only return 0.0 (when not pressed) or 1.0 (when pressed) while a trigger can return continuous values between 0.0 (fully released) to 1.0 (fully pressed).
  • El estado del Stick está expresado por un Vector2 cuyos componentes X e y están entre-1,0 y 1,0.Thumbstick state is expressed by a Vector2 whose X and Y components are between -1.0 and 1.0.

Puede usar MotionController. GetPressableInputs () para devolver una lista de entradas que devuelven un valor presionado (botones y desencadenadores) o el método MotionController. GetXYInputs () para devolver una lista de entradas que devuelven un valor de 2 ejes.You can use MotionController.GetPressableInputs() to return a list of inputs returning a pressed value (buttons and triggers) or the MotionController.GetXYInputs() method to return a list of inputs returning a 2-axis value.

Una instancia de MotionControllerReading representa el estado del controlador en un momento dado:A MotionControllerReading instance represents the state of the controller at a given time:

  • GetPressedValue () recupera el estado de un botón o un desencadenador.GetPressedValue() retrieves the state of a button or a trigger.
  • GetXYValue () recupera el estado de un stick.GetXYValue() retrieves the state of a thumbstick.

Creación de una memoria caché para mantener una colección de instancias de MotionController y sus EstadosCreating a cache to maintain a collection of MotionController instances and their states

Empiece creando una instancia de MotionControllerWatcher y registrando Controladores para sus eventos MotionControllerAdded y MotionControllerRemoved para mantener una memoria caché de instancias de MotionController disponibles.Start by instantiating a MotionControllerWatcher and registering handlers for its MotionControllerAdded and MotionControllerRemoved events to keep a cache of available MotionController instances. Esta memoria caché debe ser un monobehavior adjunto a un GameObject como se muestra en el código siguiente:This cache should be a MonoBehavior attached to a GameObject as demonstrated in the following code:

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

Lectura de nuevas entradas mediante sondeoReading new inputs by polling

Puede leer el estado actual de cada controlador conocido a través de MotionController. TryGetReadingAtTime durante el método Update de la clase monobehavior.You can read the current state of each known controller through MotionController.TryGetReadingAtTime during the Update method of the MonoBehavior class. Desea pasar DateTime. Now como parámetro timestamp para asegurarse de que se lee el estado más reciente del controlador.You want to pass DateTime.Now as the timestamp parameter to ensure that the latest state of the controller is read.

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

Puede obtener el valor de entrada actual de los controladores mediante la mano del controlador:You can grab the controllers current input value using the Handedness of the controller:

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

Por ejemplo, para leer el valor de agarre análogo de un InteractionSource:For example, to read the analog grasp value of an 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);
        … 
    }
} 

Generar eventos a partir de las nuevas entradasGenerating events from the new inputs

En lugar de sondear el estado de un controlador una vez por fotograma, tiene la opción de controlar todos los cambios de estado como eventos, lo que le permite controlar incluso las acciones más rápidas que duran menos que un fotograma.Instead of polling for a controller's state once per frame, you have the option of handling all state changes as events, which lets you handle even the quickest actions lasting less than a frame. Para que este enfoque funcione, la memoria caché de los controladores de movimiento debe procesar todos los Estados publicados por un controlador desde el último fotograma, lo que se puede hacer mediante el almacenamiento de la marca de tiempo de los últimos MotionControllerReading recuperados de un MotionController y la llamada a MotionController. TryGetReadingAfterTime ():In order for this approach to work, the cache of motion controllers needs to process all states published by a controller since the last frame, which you can do by storing the timestamp of the last MotionControllerReading retrieved from a MotionController and calling 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; 
    } 
} 

Ahora que ha actualizado las clases internas de caché, la clase monobehavior puede exponer dos eventos: pressed y released, y generarlos desde su método Update ():Now that you've updated the cache internal classes, the MonoBehavior class can expose two events – Pressed and Released – and raise them from its Update() method:

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

La estructura de los ejemplos de código anteriores hace que el registro de eventos sea mucho más legible:The structure in the above code examples makes registering events much more readable:

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)) 
    { 
        … 
    } 
} 

Consulte tambiénSee also