スクリプトを通して Interactor Events / Interactable Events にイベントを登録する【XR Interaction Toolkit】

Interactor / Interactable Events について少し深堀しつつ,スクリプトを通してイベント登録をしてみます.

検証環境

  • Unity 2019.4.17f1
  • URP 7.5.2
  • XR Interaction Toolkit 1.0.0-pre.2

Interactor Events / Interactable Events

Interaction System には Hover,Select,Activate という3つの状態があります.

Hover は Interactor / Interactable お互いが衝突判定に入っている状態です.
説明が難しいですが,マウスで説明すると,画像やリンクにマウスカーソルを合わせたときに,
クリックしなくてもアニメーションが始まったりするような変化が起こる表現のことを指します.
マウスーオーバーですかね.

Select は Interactor / Interactable お互いがイベント実行中に入っている状態です.
例えば,ボタン押下中だったり,物を掴んでいる状態だったりなど.
マウスで言えば,マウスダウンですかね.

Activate は Interactable 特有の Event ですが,Select に入った瞬間に行われるイベントのようです.
主に注目すべきなのは Hover と Select です.

f:id:xrdnk:20210206162321p:plain

(XR Interaction Toolkit マニュアルより抜粋)

f:id:xrdnk:20210206163933p:plain

Interactor Events に何かイベントを登録したい場合はこちらで登録することができます.

注意 (1.0.0-pre.1 以前 と 1.0.0-pre.2)

XR Interaction Toolkit 1.0.0-pre.1 以前と XR Interaction Toolkit 1.0.0-pre.2 以後で
Interactor / Interactable Events のメソッド定義が変わっていますのでご注意を.
1.0.0-pre.1 以前の定義は obsolete 予定になります.今後は 1.0.0-pre.2 の方で説明します.

以下,比較画像になります.

Interactor Events 比較

f:id:xrdnk:20210206161401p:plain

Interactable Events 比較

f:id:xrdnk:20210206161410p:plain

BaseInteractionEventArgs

XXXXXEventArgs は BaseInteractionEventArgs を継承しているクラスです.
例えば,HoverEnterEventArgs スクリプトはこのようになっています.

namespace UnityEngine.XR.Interaction.Toolkit
{
  public class HoverEnterEventArgs : BaseInteractionEventArgs
  {
  }
}

BaseInteractonEventArgsスクリプトの中身を読むとわかりますが,
これまでは InteractorEvents の引数は Interactable,InteractableEvents の引数は Interactor しかできませんでしたが,
これからは Interactor / Interactable 両方利用できるようになります.

    /// <summary>
    /// Event data associated with an interaction event between an Interactor and Interactable.
    /// </summary>
    public abstract class BaseInteractionEventArgs
    {
        /// <summary>
        /// The Interactor associated with the interaction event.
        /// </summary>
        public XRBaseInteractor interactor { get; set; }

        /// <summary>
        /// The Interactable associated with the interaction event.
        /// </summary>
        public XRBaseInteractable interactable { get; set; }
    }

これは各自で Interactor / Interactable を継承したカスタムクラスを作成する際に活きそうです.
これまで紹介してきた Interactor / Interactable 系のクラスは Base Interactor / Base Interactable を基底クラスとしています.
なので,自分で独自の Interactor / Interactable クラスを作成することは可能です.

f:id:xrdnk:20210206162452p:plain

(XR Interaction Toolkit マニュアルより抜粋)

デバッグスクリプト

動作確認のため以下のスクリプトを作成しました.

Interactor 用

using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;

namespace xrdnk.DebugScripts
{
    public class DebugInteractorEvents : MonoBehaviour
    {
        [SerializeField] private XRBaseControllerInteractor _interactor;

        private void OnEnable()
        {
            _interactor.hoverEntered.AddListener(DebugPrint);
            _interactor.hoverExited.AddListener(DebugPrint);
            _interactor.selectEntered.AddListener(DebugPrint);
            _interactor.selectExited.AddListener(DebugPrint);
        }

        private void OnDisable()
        {
            _interactor.hoverEntered.RemoveListener(DebugPrint);
            _interactor.hoverExited.RemoveListener(DebugPrint);
            _interactor.selectEntered.RemoveListener(DebugPrint);
            _interactor.selectExited.RemoveListener(DebugPrint);
        }

        private void DebugPrint(BaseInteractionEventArgs args) =>
            print($"Args : {args}. Interactor : {args.interactor}. Interactable : {args.interactable}.");
    }
}

Interactable 用

using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;

namespace xrdnk.DebugScripts
{
    public class DebugInteractableEvents : MonoBehaviour
    {
        [SerializeField] private XRBaseInteractable _interactable;

        private void OnEnable()
        {
            _interactable.firstHoverEntered.AddListener(DebugPrint);
            _interactable.lastHoverExited.AddListener(DebugPrint);

            _interactable.hoverEntered.AddListener(DebugPrint);
            _interactable.hoverExited.AddListener(DebugPrint);

            _interactable.selectEntered.AddListener(DebugPrint);
            _interactable.selectExited.AddListener(DebugPrint);

            _interactable.activated.AddListener(DebugPrint);
            _interactable.deactivated.AddListener(DebugPrint);
        }

        private void OnDisable()
        {
            _interactable.firstHoverEntered.RemoveListener(DebugPrint);
            _interactable.lastHoverExited.RemoveListener(DebugPrint);

            _interactable.hoverEntered.RemoveListener(DebugPrint);
            _interactable.hoverExited.RemoveListener(DebugPrint);

            _interactable.selectEntered.RemoveListener(DebugPrint);
            _interactable.selectExited.RemoveListener(DebugPrint);

            _interactable.activated.RemoveListener(DebugPrint);
            _interactable.deactivated.RemoveListener(DebugPrint);
        }

        private void DebugPrint(BaseInteractionEventArgs args) =>
            print($"Args : {args}. Interactor : {args.interactor}. Interactable : {args.interactable}.");
    }
}

イベント登録時は interactor.hoverEntered.AddListener() のようにし,
解除時は interactor.hoverEntered.RemoveListener() と書けばよいです.

ちなみに,1.0.0-pre.1 以前では interactor.hoverEntered は
interactor.onHoverEntered になっているので注意.(他のイベントも同様)

動作確認

まずは Interactor Events の確認.XRBaseControllerInteractor には Right Hand Controller を入れてます.

gyazo.com

ホバー可能な物体に触れると HoverEnter が発火し,触れるのをやめると HoverExited が発火.
物体を掴んだ瞬間,SelectEnter が発火するとともに,ここで HoverExited も発火します.
そして掴むのやめて投げると, SelectExited が発火します.

次に Interactable Events の確認.XRBaseInteractable には Cubre (XRGrabInteractable) を入れてます.

gyazo.com

Interactor の時と同じですね.
少しわかりにくいですが,Hover Enter / Exit EventArgs が2つ表示されるのは FirstHoverEntered / LastHoverExited があるためです.

主に使うのは HoverEntered / Exited,SelectEntered / Exited になるかと思います.