【Unity(C#)】Oculus Integration使用時、IPointerEnterHandlerが実機で反応しない問題の解決方法

## はじめに

こういうのをOculus Integrationで作りました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">選択項目に吸いつくポインターの検証🙆‍♂️ <a href="https://t.co/uGVOJgQzhf">pic.twitter.com/uGVOJgQzhf</a></p>&mdash; KENTO⚽️XRエンジニア😎Shader100記事マラソン挑戦中49/100 (@okprogramming) <a href="https://twitter.com/okprogramming/status/1475486398195331074?ref_src=twsrc%5Etfw">December 27, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

その際に`IPointerEnterHandler`が**実機でのみ**反応しなくて困りました。

解決策をメモします。


## バージョン情報
| 諸々名前 | バージョン |
|:-:|:-:|
|  Unity | 20219.4.29f1  |
|  Oculus Integration | 35.0  |

## 原因

`OVRInputModule`の`ProcessMouseEvent`が原因です。

```C#:OVRInputModule
private void ProcessMouseEvent(MouseState mouseData)
{
    var pressed = mouseData.AnyPressesThisFrame();
    var released = mouseData.AnyReleasesThisFrame();

    var leftButtonData = mouseData.GetButtonState(PointerEventData.InputButton.Left).eventData;

    if (!UseMouse(pressed, released, leftButtonData.buttonData))
        return;

    // Process the first mouse button fully
    ProcessMousePress(leftButtonData);
    ProcessMove(leftButtonData.buttonData); //ここ
    ProcessDrag(leftButtonData.buttonData);
    
    //...以下省略
}
```

`ProcessMove`の`HandlePointerExitAndEnter`の中で`IPointerEnterHandler`が呼び出されています。
`ProcessMove`は`PointerInputModule`に実装されています。

```C#:PointerInputModule
protected virtual void ProcessMove(PointerEventData pointerEvent)
{
    var targetGO = (Cursor.lockState == CursorLockMode.Locked ? null : pointerEvent.pointerCurrentRaycast.gameObject);
    HandlePointerExitAndEnter(pointerEvent, targetGO);
}
```

このコード内の`Cursor.lockState`が実機では常にLockedを返します。
これが原因です。

## 対応方法

いろいろとやり方はありそうですが、私は`ProcessMove`と同様の処理を`OVRInputModule`に定義し、
`ProcessMouseEvent`で呼び出しました。

```C#:OVRInputModuleに新たに定義
private void OriginalProcessMove(MouseButtonEventData data)
{
    var pointerEvent = data.buttonData;
    var targetGO =  pointerEvent.pointerCurrentRaycast.gameObject;
    HandlePointerExitAndEnter(pointerEvent, targetGO);
}
```

```C#:OVRInputModuleのProcessMouseEventで呼び出し
private void ProcessMouseEvent(MouseState mouseData)
{
    var pressed = mouseData.AnyPressesThisFrame();
    var released = mouseData.AnyReleasesThisFrame();

    var leftButtonData = mouseData.GetButtonState(PointerEventData.InputButton.Left).eventData;

    if (!UseMouse(pressed, released, leftButtonData.buttonData))
        return;

    // Process the first mouse button fully
    ProcessMousePress(leftButtonData);
    OriginalProcessMove(leftButtonData); //ここを修正
    ProcessDrag(leftButtonData.buttonData);
    
    //...以下省略
}
```



## おわりに
Oculusのフォーラムに同じ内容の質問がいくつかあったのでこの記事のリンクを紹介しておきます。

[Oculus Quest, UI and Unity : No hover event, even on the example scene](https://forums.oculusvr.com/t5/Quest-Development/Oculus-Quest-UI-and-Unity-No-hover-event-even-on-the-example/m-p/760907#M838)
[Detecting laser pointer hovering on button (Oculus Quest - Unity3D)](https://forums.oculusvr.com/t5/Unity-VR-Development/Detecting-laser-pointer-hovering-on-button-Oculus-Quest-Unity3D/m-p/754797#M17620)
[Unity 2020 or 2021 - Gaze Pointer - Button Hover not working](https://forums.oculusvr.com/t5/Oculus-Quest-2-and-Quest/Unity-2020-or-2021-Gaze-Pointer-Button-Hover-not-working/m-p/862747#M35870)