Unity'de HP Reverb G2 Denetleyicileri

HP Hareket denetleyicileri, yeni bir Windows Mixed Reality denetleyicisi türüdür: biraz farklı bir kullanılabilir giriş kümesine sahip aynı izleme teknolojisi:

  • Dokunmatik yüzey iki düğmeyle değiştirildi: Sağ denetleyici için A ve B, sol denetleyici için X ve Y.
  • Kavrama artık Basılmış ve Basılmadı durumlarına sahip bir düğme yerine 0.0 ile 1.0 arasında bir değer akışı yayımlayan bir tetikleyicidir.

Yeni girişlere mevcut Windows ve Unity API'leri üzerinden erişilebildiğinden ayrılmış Microsoft.MixedReality.Input UPM Paketi gerekir.

Önemli

Bu paketteki sınıflar mevcut Windows ve Unity API'lerinin yerini almaz, ancak bunları tamamlar. Hem klasik Windows Mixed Reality denetleyicileri hem de HP Hareket Denetleyicileri için yaygın olarak kullanılabilen özelliklere mevcut API'ler kullanılarak aynı kod yolu üzerinden erişilebilir. Yalnızca yeni girişler ek Microsoft.MixedReality.Input paketinin kullanılmasını gerektirir.

HP Hareket Denetleyicisi'ne genel bakış

Microsoft.MixedReality.Input.MotionController bir hareket denetleyicisini temsil eder. Her MotionController örneğinde bir XR vardır. WSA. Input.InteractionSource eşliği; elle kullanım, satıcı kimliği, ürün kimliği ve sürüm kullanılarak ilişkilendirilebilir.

Yeni InteractionSource örneklerini keşfetmek için InteractionManager olaylarını kullanmaya benzer şekilde, bir MotionControllerWatcher oluşturup olaylarına abone olarak MotionController örneklerini alabilirsiniz. MotionController'ın yöntemleri ve özellikleri, düğmeleri, tetikleyicileri, 2B ekseni ve parmak çubuğu dahil olmak üzere denetleyici tarafından desteklenen girişleri açıklar. MotionController sınıfı ayrıca MotionControllerReading sınıfı aracılığıyla giriş durumlarına erişme yöntemlerini de kullanıma sunar. MotionControllerReading sınıfı, denetleyicinin belirli bir zamanda durumunun anlık görüntüsünü temsil eder.

Karma Gerçeklik Özellik Aracı ile Microsoft.MixedReality.Input'u Yükleme

Yeni Karma Gerçeklik Özellik Aracı uygulamasıyla Microsoft.MixedReality.Input eklentisini yükleyin. Yükleme ve kullanım yönergelerini izleyin ve Karma Gerçeklik Araç Seti kategorisindeki Karma Gerçeklik Giriş paketini seçin:

Karma gerçeklik girişinin vurgulandığı Karma Gerçeklik Özellik Aracı paketleri penceresi

Microsoft.MixedReality.Input Kullanma

Giriş değerleri

Bir MotionController iki tür girişi kullanıma açabilir:

  • Düğmeler ve tetikleyici durumları, 0,0 ile 1,0 arasında, ne kadar basıldıklarını gösteren benzersiz bir kayan değerle ifade edilir.
    • Bir düğme yalnızca 0,0 (basılmadığında) veya 1,0 (basıldığında) döndürebilirken, tetikleyici 0,0 (tam olarak serbest bırakılır) ile 1,0 (tam olarak basılmış) arasında sürekli değerler döndürebilir.
  • Parmak çubuğu durumu, X ve Y bileşenleri -1.0 ile 1.0 arasında olan bir Vector2 tarafından ifade edilir.

2 eksenli bir değer döndüren girişlerin listesini döndürmek için MotionController.GetPressableInputs() kullanarak basılı değer (düğmeler ve tetikleyiciler) döndüren girişlerin listesini veya MotionController.GetXYInputs() yöntemini döndürebilirsiniz.

MotionControllerReading örneği, denetleyicinin belirli bir zamanda durumunu temsil eder:

  • GetPressedValue() bir düğmenin veya tetikleyicinin durumunu alır.
  • GetXYValue() bir parmak çubuğunun durumunu alır.

MotionController örneklerinin ve durumlarının koleksiyonunu korumak için önbellek oluşturma

Bir MotionControllerWatcher örneği oluşturarak ve kullanılabilir MotionController örneklerinin önbelleğini tutmak için MotionControllerAdded ve MotionControllerRemoved olayları için işleyicileri kaydederek başlayın. Bu önbellek, aşağıdaki kodda gösterildiği gibi bir GameObject'e eklenmiş bir MonoBehavior olmalıdır:

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

Yoklama yaparak yeni girişleri okuma

Bilinen her denetleyicinin geçerli durumunu, MonoBehavior sınıfının Update yöntemi sırasında MotionController.TryGetReadingAtTime aracılığıyla okuyabilirsiniz. Denetleyicinin en son durumunun okunmasını sağlamak için DateTime.Now değerini zaman damgası parametresi olarak geçirmek istiyorsunuz.

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

Denetleyicinin Kullanım Durumunu kullanarak denetleyicilerin geçerli giriş değerini alabilirsiniz:

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

Örneğin, bir InteractionSource'un analog kavrama değerini okumak için:

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

Yeni girişlerden olaylar oluşturma

Bir denetleyicinin durumunu çerçeve başına bir kez yoklama yerine, tüm durum değişikliklerini olay olarak işleme seçeneğiniz vardır ve bu da bir çerçeveden daha kısa süren en hızlı eylemleri bile işlemenize olanak tanır. Bu yaklaşımın çalışması için, hareket denetleyicileri önbelleğinin son kareden bu yana bir denetleyici tarafından yayımlanan tüm durumları işlemesi gerekir. Bunu, bir MotionController'dan alınan son MotionControllerReading'in zaman damgasını depolayarak ve MotionController.TryGetReadingAfterTime() çağrısı yaparak yapabilirsiniz:

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

Önbellek iç sınıflarını güncelleştirdiğinize göre, MonoBehavior sınıfı iki olayı kullanıma seçip (Basıldı ve Yayımlandı) ve bunları Update() yönteminden yükseltebilir:

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

Yukarıdaki kod örneklerindeki yapı, olayların kaydedilmesini çok daha okunabilir hale getirir:

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

Ayrıca bkz.