IVRC2024_Hataage

展示用ユーティリティ

概要

SEED Stage および LEAP Stage での展示において効率化を図るために以下のような展示用ユーティリティを作成し、活用した。

展示用ユーティリティ

このユーティリティでは複数のQuestに対して以下の操作を行える。

これらの操作をQuestを装着することなく行えることで、展示にあたっての手間を大幅に削減することができた。

なお、ユーティリティのコードは mirroring-tool のブランチで管理しているのでコード全文はそちらから。

つかった技術

本ツールではいくつかのソフトやツールを利用している。

またミラーリングや近接センサを扱う部分については、リーダーが個人的につくっているミラーリングツールを基にしている。

adb

PCに接続した Android デバイスに対して様々な操作を行える開発者用ツール。いろいろできる。

例えば Android 端末にアプリをインストールしたいときは、コマンドプロンプト (Macならターミナル) を開いて adb install [apkファイルのパス] と実行すれば、Android 端末を一切触らずにアプリのインストールができる。 (ちなみにコマンドプロンプト/ターミナルにapkファイルをD&Dすれば勝手にパスを入れてくれるので楽)

基本的に adb で書き始めればいいが、複数のデバイスが接続されているときはその端末のシリアル番号を指定して実行しないと怒られる。 adb -s[シリアル番号] のように始めよう。

adbコマンドでできることをたくさんまとめてくれているサイトがある。

scrcpy

Android デバイスの画面をPC上にミラーリングしたり、PC上で操作したりできるソフト。内部的には一部 adb の仕組みをつかっているらしい。

展示用ユーティリティでは Quest の画面をミラーリングする部分に用いた。

ところで、Quest 3 のスクリーンは以下のように左右が傾いて配置されているため、そのままミラーリング&クロップすると斜めの映像になってしまう。そこでこれを回転させて表示させたいのだが、制作時点 (2024年9月) の scrcpy では、画面を傾ける機能は提供されていない。(90度単位での回転は可能)

alt

そこで、有志が開発中の機能を開発者の rom1v 氏に紹介してもらい、こちらを用いることで Quest 3 や Pro に合わせた傾き補正を行うことができた。

flet

Python のGUIライブラリのひとつ。

Flutter という Google が開発したオープンソースのUIソフトウェア開発キットを基にしており、クロスプラットフォームのアプリケーションを作成できる。

今回は Windows 上でコマンドを実行するためのGUIを作成する目的で利用している。

仕組み

本ツールの動作は大きく以下のように説明できる。

  1. adb devices -l でPCに接続された Android端末 (Quest を含む) を取得し、「名前 (シリアル番号)」の形でリスト管理
  2. シリアル番号を用いて adb もしくは scrcpy のコマンドを生成し、実行する

各種コマンドの中身については次のとおりだ。

ミラーリングを行うとき

scrcpy -s [シリアル番号] というコマンドだけで画面全体のミラーリングを行うことはできる。ここに、GUIで設定したオプションを追加したコマンドを実行する。

具体的には以下のようなオプションが追加される。

このようなオプションが追加され、最終的なコマンドは例えば以下のようになる。

scrcpy -s XXX12345 -m 1024 --window-title="Quest 3 (XXX12345)" --no-audio --crop=2000:2000:2000:0 --rotation-offset=-20 --scale=132 --position-x-offset=-170 --position-y-offset=-125

長い。こういうのを手打ちとかコピペの必要なくGUIでポチポチしておくだけでいいので楽ですね。

ゲーム操作を行うとき

ゲームの操作は、実はこちら側の実装というよりは Unity 側の実装を基にしている。

開発時にPCでデバッグするために Unity 側で用意していたキーボード入力操作を Android でエミュレートしているに過ぎない。

ということでキー入力を送信するだけなので、adb -s [シリアル番号] shell input keyevent [キーコード] のコマンドを実行する。

例えばキーボードの “A” の入力を送信したいときは adb shell input keyevent KEYCODE_A を実行すれば、Android で動いているゲームアプリ内で “A” が押されたことになる。

キーコードの一覧はAndroid の開発者ページにまとまっている。

なお本ツールでは、これを簡単に実行するための関数として send_key_event(key_code) を定義しておき、引数としてキーコードを渡すだけでコマンドを組み立てて実行してくれるようにしている。

無線通信を有効にするとき

adbは通信はUSBによる有線接続のほかに TCP/IP による無線通信もサポートしている。USBケーブルを挿すのが面倒なときにおすすめ。

まず、無線接続したい Android 端末をTCPモードに切り替える必要がある。ポート番号として慣例的に 5555 を使うらしい。

adb -s [シリアル番号] tcpip 5555

次に、TCPモードが有効になった端末に対してIPアドレスとポート番号を指定して接続する (これがUSBケーブルを挿すことと同義になる)

adb connect 192.168.XXX.YYY:5555

なお、端末のIPアドレスを確認するのが面倒なので、コマンドで引っ張り出している。 adb -s [シリアル番号] shell ip route を実行すると、

192.168.0.0/24 dev wlan1 proto kernel scope link src 192.168.XXX.YYY

のように返ってくるので、これの標準出力から正規表現を用いてIPアドレスの部分だけを吐き出すようにしている。実装は以下のとおり。

result = subprocess.run([adb, "-s", serial_number, "shell", "ip", "route"], capture_output=True, text=True)

output = result.stdout

ip_match = re.search(r'src (\d+\.\d+\.\d+\.\d+)', output)
if ip_match:
    return ip_match.group(1)

近接センサを切り替えるとき

Quest には装着状態を判定する近接センサ (Proximity Sensor) が内蔵されている。

(レンズの中間上部にあるところを指で塞ぐと画面が点くのがわかるとおもう)

これを無効にすることで、装着していないときも画面を点灯させることができ、ミラーリング画面が黒くならない。もちろんバッテリー消費も激しくなるので扱いには注意。

近接センサを無効にする (装着していないときも点灯させる) ときは adb -s [シリアル番号] shell am broadcast -a com.oculus.vrpowermanager.prox_close

有効にするときは adb -s [シリアル番号] shell am broadcast -a com.oculus.vrpowermanager.automation_disable

センサは Quest 側で任意に切り替えられないので、ミラーリング終了時などにソフト側で適切にセンサが有効に戻るようにしている。一応 Quest を再起動すれば戻るらしい。

アプリを起動/終了するとき

アプリのパッケージ名とクラス名をつかってアプリの起動や終了ができる。

パッケージ名

adb shell pm list package でインストールされているアプリを一覧で出力してくれる。

あるいは Unity で任意に設定もできる。方法は Edit > Project Settings > Player から Company Name と Product Name に設定した文字列がそのままパッケージ名になる。例えば Company Name を hogehoge、Product Name を MyProject とした場合は com.hogehoge.MyProject がパッケージ名になる。

alt

クラス名

よくわからん。AndroidManifest.xml ファイルに activity android:name="hogehoge" として載ってるらしい?

Unity 製のアプリならたぶん標準で com.unity3d.player.UnityPlayerActivity になるとおもうのでこれを使ってる。


これらのパッケージ名/クラス名を用いて、アプリを起動する際は adb -s [シリアル番号] shell am start -n パッケージ名/クラス名、終了する際は adb -s [シリアル番号] shell am force-stop パッケージ名/クラス名 のコマンドを実行する。