Contents — 8 sections

VRC ActionBarには、キーボードの1〜9でSkillを発動する機能があった。

ただし問題が一つ。

Consoleにfocusがあるときしか効かない。

VRChatを前面にすると、当然Consoleはキー入力を受け取れない。

そこでGlobal Keyboard Hookを入れる代わりに、localhostへ小さなHTTP APIを生やした。

Stream Deckからそこを叩く。

経路はこれだけ

Stream Deck
  ↓ HTTP GET
localhost API
  ↓
Action Engine
  ↓ OSC
VRChat

追加したのは、

GET /cast/<1-9>
GET /state

の2系統。

/cast/1 ならslot 1。

Skillが発動できれば200。

Cooldown中なら409。

存在しないslotなら404。

/state では全Skillの残りCooldownを返す。

Gestureと別系統にしなかった

HTTP requestを受けたからといって、直接OSCを送らない。

必ず同じAction Engineへ入れる。

Gesture。

Keyboard。

Stream Deck。

全部同じCore。

だからCooldownも共有する。

GestureでFireballを撃った直後にStream Deckを押しても、同じCooldown判定へ入る。

入力経路ごとにSkill stateを持たない。

localhostだけで待ち受ける

APIはLANへ公開しない。

待受はlocalhostだけ。

外部端末からSkillを撃つ用途ではない。

同じWindows PC上のStream Deck softwareから、VRChat前面のままAction Engineを呼びたいだけ。

範囲を狭くする。

9180を使おうとしたら埋まっていた

最初は別のportを使おうとした。

確認すると、Logicool G HUB updaterが使っていた。

そこで既定portを9190へ変更した。

かなり小さい話だけど、ローカルToolではこういう常駐ソフトとの衝突が普通に起きる。

「コードが動かない」と思って調べると、既に別Processが待っている。

Stream Deck側はWebsite action

Stream Deck MK.2側は、Website actionからbackground GETを送る。

Skillごとのボタンを作る。

ActionBar側にはMMO hotbar風のkey iconも用意した。

VRの中からStream Deck本体は見えない。

そこは購入後に気づいた別の問題だった。

でも「VRChatを前面にしたまま物理ボタンでSkillを撃つ」入力デバイスとしては使える。

実VRChatまで通した

dry-runでは、

  • cast成功
  • Skill cooldown reject
  • Global cooldown reject
  • state
  • unknown slot

をcurlで確認。

Testは当時、

  • VrcActionBar: 65
  • RealityLink: 54
  • PicoTransform: 75

がPASS。

その後、実機では、

Stream Deck MK.2
→ ActionBar
→ OSC
→ VRChat

を通し、5 Skillsすべてで対応する Skill_* parameterがTrueになることをOSCQuery上で確認した。

これはGesture recognitionの実機試験ではない。

でも、

物理ボタンからAction Engineを経由し、実VRChatのAvatar parameterまで届く

ところは確認できた。

Global HookではなくHTTPにした理由

他の方法もある。

Windows全体でHotkeyを拾う。

専用Pluginを書く。

低レベルinputをhookする。

でも今回欲しかったのは、

「Consoleにfocusを戻さなくてもslotをcastできる」

だけ。

localhost HTTPなら、

  • 実装が単純
  • curlで試せる
  • Stream Deck以外からも叩ける
  • Coreと分離できる
  • LANへ晒さなくてよい

という利点があった。

必要以上にOSへ入り込まない。

いまの結論

Stream Deckを買ったときは、VR中の操作デバイスとしてもっと直接的に使えると思っていた。

実際にはVR世界から本体は見えない。

そこは期待と違った。

ただ、VRC ActionBarの入力としては結果的に使えた。

しかも、

入力源をAction Engineから分離していたので、後からHTTPを一枚足すだけで済んだ。

大きな設計思想というほどではない。

でも、あとから別デバイスを繋ぐときに「Coreへ直接書かなかった」ことが効いた例として残しておく。