MetaQuestでハンドトラッキングのみでテキスト入力する

# はじめに
コントローラーを使用したテキスト入力の記事は調べたところいくつかヒットしたが、ハンドトラッキング（手）のみの操作で入力できるようにしたという記事を見かけなかったので作成しました

## デモ
パススルー状態での撮影なので、背景が真っ暗です
<iframe width="630" height="630" src="https://www.youtube.com/embed/VnJ1hnhl0qo" title="MetaQuest_HandTracking_InputText_0227" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

# 大まかな実装の方法
1. ハンドトラッキングの有効化
1. Poke（押し込み）の実装
1. スクリプトでのOSのキーボードを呼び出し
1. Poke（押し込み）の判定を外部で利用可能にする
1. キーボードの入力内容とInputを同期

# 環境
## バージョン
Unity **2022.3.11.f1**

## 導入したSDK（v62）
SDKは以下のURLから無料でダウンロードできる
https://assetstore.unity.com/publishers/25353

- Meta XR Core SDK
- Meta Interaction SDk
- Meta Interaction SDK OVR Intergration

いずれも **v62** でダウンロードしている

# 実装方法
冒頭であげた5つに分けて解説していく
プロジェクトへのSDKのインポートなどは終わっているものとする

## 1. ハンドトラッキングの有効化
<img width="609" alt="image.png (56.3 kB)" src="https://img.esa.io/uploads/production/attachments/13979/2024/02/24/148413/e2ce0ebc-9e6a-471f-b4d6-e50951f015a7.png">

Oculus > Building Blocksを選択し、以下のものを追加する
- CameraRig
- Hand Tracking
- Virtual Hands

PassThrough は有効にしなくても問題ないが、以下の通りになる
<img width="1440" alt="image.png (1.3 MB)" src="https://img.esa.io/uploads/production/attachments/13979/2024/02/24/148413/eb90a8df-08f5-4a1b-8026-8394fef45082.png">

パススルーを有効にした場合は Build の設定や Android の設定を変更する必要があるため注意

## 2. Poke （押し込み）の実装
Poke については非常に分かりやすい解説をされている方がいたのでそちらのリンクを貼っておく
[ダイレクトタッチ(ポーク)を実現する Poke Interaction の概説【Oculus Interaction SDK】](https://xrdnk.hateblo.jp/entry/interaction_sdk_poke)

今回は Building Blocks を流用して実装する
BB のウィンドウから Poke を選択し、シーンに追加する

シーンに追加すると以下のような構成になる
<img width="400" alt="image.png (5.2 kB)" src="https://img.esa.io/uploads/production/attachments/13979/2024/02/24/148413/5a0acb72-0171-4a19-a70b-3cf2dae644c3.png">

Surface が実態に指先で触れられる範囲を示し、Visuals 中に表示するボタンが入っている

今回は Surface のサイズを テキストの入力エリアを覆うように設定し、Visuals は Mesh を解除し表示しないようにした
<img width="540.75" alt="image.png (4.8 kB)" src="https://img.esa.io/uploads/production/attachments/13979/2024/02/24/148413/9c5bbd2a-5e00-4a97-8962-d83e5ef93fc5.png">

これで テキスト入力エリア に触れたかどうかの判定が可能になる

## 3. スクリプトでのOSのキーボードを呼び出し
概要としては InputField を読み込んで、キーボードでの変更内容を順次保存していく方法である

以下にサンプルコードを示す
```cs:UseKeyboard.cs
using TMPro;
using UnityEngine;

public class UseKeyboard : MonoBehaviour
{
    // InputField の読み込み
    [SerializeField] private TMP_InputField inputField;
    
    // 保存先
    private string Output;

    public bool isOpen = false;

    // キーボードの宣言
    private TouchScreenKeyboard _overlayKeyboard;

    // キーボードの変更時のみ動く
    private void Update()
    {
        if (_overlayKeyboard.text == "") return;

        if (isOpen)
        {
            inputPassField.text = _overlayKeyboard.text;
            Output = inputField.text;
        }
    }

    // キーボードを呼び出す関数
    public void SetKeyboard()
    {
        _overlayKeyboard = TouchScreenKeyboard.Open(Output, TouchScreenKeyboardType.URL);
        isOpen = true;
    }
}
```

## 4. Poke（押し込み）の判定を外部で利用可能にする
先ほど実装した Poke の一番外側の要素に `InteractableUnityEventWrapper` を Add Component する

Interactable View に追加した要素をドラッグする
以下の写真の通りになる
<img width="543" alt="image.png (41.1 kB)" src="https://img.esa.io/uploads/production/attachments/13979/2024/02/24/148413/306d82b5-68ee-459c-8d16-e97fe9c66fc2.png">

それぞれの要素については割愛するが、今回は When Select() を使う

When Select () で + を選択し、さきほど書いたコードを追加した要素をドラッグ
`UseKeyboard > SetKeyboard` を選択する

変数名などが若干異なるが、以下のようになる
<img width="532.5" alt="image.png (7.7 kB)" src="https://img.esa.io/uploads/production/attachments/13979/2024/02/24/148413/b131cbf8-ef37-4132-83fd-eb5d6821098d.png">

## 5. キーボードの入力内容とInputを同期
1 つしか テキストを入力エリア がない場合は先ほどの実装で問題ないが、2 つ以上の要素がある場合は Update が実行されたときに両方の処理が走らないように処理を分岐させる必要がある

またキーボードを呼び出したとき以下の関数の実装を行ったが、
```cs:UseKeyboard.cs
    // キーボードを呼び出す関数
    public void SetPassKeyboard()
    {
        _overlayKeyboard = TouchScreenKeyboard.Open(Output, TouchScreenKeyboardType.URL);
        isOpen = true;
    }
```
`TouchScreenKeyboard.Open()` の第一引数（今回では `Output` ）が正しく指定されていない場合、予期せぬ挙動を起こす場合があるので注意

# おわりに
以上でおそらくハンドトラッキングのみでのテキストの入力が可能になると思います
Unity も MetaQuest も c# もほとんどからっきしの状態から検証を含めた実装までに3日を要したので大変でしたが、書き出してみるとあっさりとしたものですね
既存の記事はコントローラーを使用するものが多く、入力の判定がハンドトラッキングほど面倒ではないようで便利そうだなと感じました
今回のアプリの要件としてハンドトラッキングでの入力しか対応させないのであまり関係のない話なのですけどね...
同じところでつまづく人がいませんように

# 参考文献
Unity + Meta Quest開発メモ

https://tech.framesynthesis.co.jp/unity/metaquest/

Meta XR SDK(V62)でQuestアプリを開発

https://www.docswell.com/s/Ovjang/KLLYLG-MetaXRSDK62

Unity Meta SDK ダウンロードリンク（v60以降）

https://assetstore.unity.com/publishers/25353

Building Blocks による Meta Quest 3 アプリの作成

https://note.com/npaka/n/nb9f2834c1fda

ダイレクトタッチ(ポーク)を実現する Poke Interaction の概説【Oculus Interaction SDK】

https://xrdnk.hateblo.jp/entry/interaction_sdk_poke

Meta Questでシステムのバーチャルキーボードを呼び出す方法

https://qiita.com/OKsaiyowa/items/f5c18327ebbe96b259f8

キーボードオーバーレイを有効にする

https://developer.oculus.com/documentation/unity/unity-keyboard-overlay/?locale=ja_JP