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へ直接書かなかった」ことが効いた例として残しておく。