LoginSignup
9
0

More than 1 year has passed since last update.

こちらはクラスター Advent Calendar 2022(1ページ目)の17日目の記事です。
昨日は @Hashimoto_Mami さんの「ボクセル作成ツール「MagicaVoxel」を活用して3Dモデリングをしてみよう!」でした。ボクセルは手軽に作れる上に味があって良いですよね。

はじめに

こんにちは、スワンマンです。クラスター株式会社でディレクターとかカスタマーサポートとかスワンマンをしています。
今回のAdvent Calendarではすでに「Slackで同じワークフローのショートカットを複数チャンネルに設置する」と「Unityのエディタ拡張で動的にメニューを追加・削除する」という2つの記事を書いてるんですが、ちょっと中身が堅すぎたので今回は肩の力を抜いてSwitchで遊んでいきたいと思います!

ところが両の手にはVRのコントローラが!

手が塞がる問題

なんてことだ…
人間の手は2つ…
別のコントローラをさらに持つことはできない…
構造的欠陥…

ということで今回は神による設計ミスのワークアラウンドとして、このコントローラを使ってSwitchで遊ぶ方法を考えたいと思います。

VRデバイスの入力を取得する

OpenVRを使うとVRデバイスの入力を簡単に取れると聞いたので、今回はこれを使ってみます。
headersの中に「openvr_api.cs」があるので、こいつをC#のプロジェクトに取り込んで、bin/win64の「openvr_api.dll」をフォルダに配置したら以下のようなコードでざっくり入力が取れます。
(これはLegacy APIらしいけど、今回は遊べればいいのでよしとする)

EVRInitError error = EVRInitError.None;
OpenVR.Init(ref error, EVRApplicationType.VRApplication_Overlay);

// 左手コントローラの状態を取る
var leftHandIndex = OpenVR.System.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.LeftHand);
VRControllerState_t leftControllerState = default;
TrackedDevicePose_t leftTrackedDevicePose = default;
OpenVR.System.GetControllerStateWithPose(ETrackingUniverseOrigin.TrackingUniverseSeated, leftHandIndex, ref leftControllerState, (uint)Marshal.SizeOf(typeof(VRControllerState_t)), ref leftTrackedDevicePose);

// ulButtonPressedに各ボタンの押下状態がビットフラグで入るのでいい感じに取る
if ((leftControllerState.ulButtonPressed & 1ul << (int)EVRButtonId.k_EButton_A) != 0)
{
    ...
}

// スティックのアナログ入力はleftControllerState.rAxis0に-1.0~1.0で入ってる
// xは左が-1.0で右が1.0
// yは下が-1.0で上が1.0
...

// 右手も同様に
...

入力をSwitchに送る

世の中にはかしこい人がいるので、NXBTというライブラリを使うとPCに生やしたBluetoothアダプタをコントローラとしてSwitchに認識させることができます。すごい。
Windows上で使うには仮想環境にUbuntuを入れてその中で立ち上げる必要があるんですが、vagrantを使って簡単にセットアップする仕組みが同梱されており、順調に進めば恐らく30分もかからずに接続までいけます。
(僕は運悪く非対応Bluetoothアダプタを使っていたせいで3時間くらい無駄に格闘した果てにAmazonで別のBluetoothアダプタを買いました…)

普通のゲームコントローラを使うだけであれば自身でコードを書くまでもなく使えてしまうんですが、今回は自前で取得した入力データを送りたいので以下のような感じのサーバーを書きます。
(送り手はこのDIRECT_INPUT_PACKETに入力値を詰めてjsonとして送ればOK)

from http.server import HTTPServer
from http.server import BaseHTTPRequestHandler
import json
import nxbt

def run():
    nx = nxbt.Nxbt()
    controller_idx = nx.create_controller(nxbt.PRO_CONTROLLER)
    nx.wait_for_connection(controller_idx)
    
    class Handler(BaseHTTPRequestHandler):
        def log_message(self, format, *args):
            pass
        
        def do_PUT(self):
            content_len = int(self.headers.get('content-length'))
            recv_data = json.loads(self.rfile.read(content_len).decode('utf-8'))
            nx.set_controller_input(controller_idx, recv_data)
            self.send_response(204)
    
    server = HTTPServer(('0.0.0.0', 8000), Handler)
    server.serve_forever()

if __name__ == '__main__':
    run()

試してみる

やったぜ。

今回わかったこと

こいつが一番遊びやすいや。

プロコン

おわり

明日はクラスター広報 @MIRINPR さんの「なんか」です!何が書かれるか楽しみ!

9
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
0