バーチャルSNS「cluster」主催のGAMEJAMに応募した作品が、デジタルハリウッド大学賞を受賞しました。
デジタルハリウッド大学様のご厚意により「Orbital2」を賞品として頂いたので、感想を書き連ねていこうと思います。
#目次
- Orbital2とは
- Orbital2の感想
- Orbital2への要望
- Orbital2の振る舞いを観察する
- ハードウェアを確認する
- シリアル通信を読む
- Pythonで制御する
- キーボードとマウスを操る
- BlenderのShiftキーに対応する
- アプリごとに自動でプロファイルを切り替える
- ジョイスティックの読み取り
- リストメニュー
- グロウリングについて
- バイブレーションについて
- Blenderで活用する
- BlenderにOrbital2を召喚する
- おわりに
#Orbital2とは
Orbital2は、株式会社BRAIN MAGIC (Blackmagicじゃないよ) が開発した左手デバイスです。
詳細は公式サイトまで。
フラットリングと呼ばれる8つのスイッチや、ジョイスティックを倒す・回す・押す動作を組み合わせて色々できるみたいですね。
1658万色に輝くグロウリングも、ゲーマーにとっては嬉しいポイントです。
画像には書かれていませんが、振動によるフィードバック機能もあります。
#Orbital2の感想
Orbital2の使い方・導入方法は公式サイトに掲載されているので省略します。
Orbital2の使い方・導入 for Windows
Orbital2の使い方・導入 for Mac
まずは、作業の様子を御覧ください
画面だけでは何をやっているのかわかりにくいですね。
ジョイスティックを傾けるとパイメニューが出てくるので、そのままダイヤルを回転させてショートカットを実行しています。
物理的にダイヤルを増やすデバイスが多い中、ジョイスティックを傾けてダイヤルの動作を選択する仕組みは洗練されていて気に入りました。
ショートカット名は画面にオーバーレイ表示されるので、どこに何の機能を登録したか一目瞭然です。
ポップアップとして、リスト形式のメニューを使用することも出来ます。
##質感について
つや消し加工のさらさらした質感は、HTC VIVEを彷彿とさせます。
ダイヤルにはクリックタイプのロータリーエンコーダが採用されており1、コトコトとした静かな回し心地です。
#Orbital2への要望
Orbital2の開発チームは、要望、バグ報告をTwitterやGitHubで受け付けています。
私も以前、Orbital2 Coreソフトウェアの誤字を報告したのですが、わずか一週間ほどで修正ビルドが上がってきました2。
この対応の速さは素晴らしいです。
しかし、当たり前ではありますが、すべての要望が対応されるわけではありません。
この章では、2021年10月21日現在の、個人的なOrbital2への要望を挙げていきます。
##プロファイルを自動で切り替えたい
Orbital2 Coreには、アクティブなアプリケーションを検出して自動でプロファイルを切り替える機能がありません。
「ひとつのアプリにつき複数のプロファイルを使用することを想定している3」とのことですが、オプションで欲しい機能ではあります。
##BlenderのShiftキーについて
標準のBlenderのビューポートは、Shiftキーを押すことで視点を操作します。
ところが、Orbital2のボタンにShiftキーを割り当てると、Blenderの視点操作に想定外の挙動がみられます。
【お知らせ】BlenderにてOrbital2をご利用の際に、「Shiftキー」の長押しが解除されない不具合が発生しています。
— BRAINMAGIC(ブレインマジック)公式 #Orbital2 (@Brain_Magi) October 3, 2019
現在改修を行っておりますので、ご不便をおかけし誠に申し訳ございませんが、改修完了まで今しばらくお待ちくださいませ。
この問題は、2年以上前から指摘されていますが、未だ修正されずにいます。
##Linuxで使いたい
個人的に、作業をUbuntuで行うことが多いので、Linuxがサポートされると嬉しいです。ただ、ディスプレイサーバやディストーションは星の数ほどありますので、対応の手間を考えるとサポートが難しいのは理解できます。
#Orbital2の振る舞いを観察する
##ハードウェアを確認する
前置きが長くなりましたが、前述の問題を解決すべくOrbital2の振る舞いを観察していきます。
まずはじめに、Orbital2をLinuxに接続し、dmesgコマンドでデバイスがどのように扱われたかを確認します。
$ dmesg
[ 4040.701637] usb 5-2: USB disconnect, device number 6
[ 4040.701837] cp210x ttyUSB0: cp210x converter now disconnected from ttyUSB0
[ 4040.701853] cp210x 5-2:1.0: device disconnected
[ 4043.033322] usb 5-2: new full-speed USB device number 8 using xhci_hcd
[ 4043.197246] usb 5-2: New USB device found, idVendor=10c4, idProduct=ea60, bcdDevice = 1.00
[ 4043.197253] usb 5-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 4043.197255] usb 5-2: Product: CP2103 USB to UART Bridge Controller
[ 4043.197257] usb 5-2: Manufacturer: Silicon Labs
[ 4043.197258] usb 5-2: SerialNumber: 0005
[ 4043.202251] cp210x 5-2:1.0: cp210x converter detected
[ 4043.205256] usb 5-2: cp210x converter now attached to ttyUSB0
どうやらSilicon Labs製のUSBシリアル「CP2103」が使われているようです。
また、コンバータがttyUSB0に割り当てられていることも確認できました。
これからttyUSB0の通信を眺めていきます。
##シリアル通信を読む
シリアル通信を解析するツールはたくさんありますが、私はArduino IDEを使いました。
ポートがbusyになることを防ぐため、モニタリングする前にOrbital2 Coreの接続を切りましょう。
WSL2はシリアルやUSBデバイスがサポートされていないので注意
シリアルモニタを開いて、適当にOrbital2を操作してみます。
J S X <8b> Y ニ ; J S X <8b> Y テ ; J S X <87> Y テ ; S W 8 = 1 ; J S X <87> Y セ ; J S X <83> Y セ ; J S X <80> Y サ ; R E - R ; J S X { Y キ ; J S X x Y イ ; J S X u Y ョ ; S W 6 = 1 ; J S X u Y ォ ; J S X r Y ォ ; J S X r Y ッ ; J S X u Y エ ; J S X u Y ク ; J S X u Y ス ; J S X x Y チ ; S W 6 = 0 ; J S X x Y ナ ; J S X ~ Y ナ ; J S X <81> Y ナ ; S W 6 = 0 ; J S X <85> Y ナ ; J S X <89> Y ハ ; S W 6 = 0 ; S W 6 = 0 ;
改行されていないため、非常に見辛いですね。ボタンを一つずつ押して確認していきます。
####ダイヤル右回転
R E - R ;
####ダイヤル左回転
R E - L ;
####フラットリングの手前のボタン (押した後離さない)
S W 7 = 1 ;
S W 7 = 1 ;
が1回だけ流れてきました。
連続して1が流れてくるわけではないみたいです。
####フラットリングの手前のボタン (クリック)
S W 7 = 1 ; S W 7 = 0 ; S W 7 = 0 ; S W 7 = 0 ; S W 7 = 0 ;
クリックするたびに、1,0,0,0,0 のセットが流れてきます。
####フラットリングの手前のボタン (長押し)
S W 7 = 1 ; S W 7 = 0 ; S W 7 = 0 ; S W 7 = 0 ; S W 7 = 0 ;
ボタンを押し込むとS W 7 = 1 ;
が1回流れ、離すとS W 7 = 0 ;
が4回流れます。
####オービタルエンジンの頂点押し込み
R C 4 = 1 ; R C 4 = 0 ; R C 4 = 0 ; R C 4 = 0 ; R C 4 = 0 ;
オービタルエンジンの頂点押し込みも、フラットリングのボタンと同じ挙動です。
####ジョイスティック
J S X { Y <81> ; J S X x Y <81> ; J S X u Y <81> ; J S X u Y <84> ; J S X r Y <84> ; J S X r Y <87> ; J S X n Y <87> ; J S X n Y <8a> ; J S X n Y <8d> ; J S X k Y <8d> ; J S X k Y <90> ; J S X h Y <90> ; J S X h Y <93> ; J S X h Y <96> ; J S X e Y <96> ; J S X e Y <99> ; J S X b Y <99> ; J S X b Y <9e> ; J S X _ Y <9e> ; J S X _ Y 「 ; J S X \ Y 「 ; J S X \ Y ・ ; J S X \ Y ゥ ; J S X Y Y ュ ; J S X Y Y ー ; J S X Y Y ウ ; J S X Y Y キ ; J S X Y Y コ ; J S X Y Y ソ ; J S X Y Y ツ ; J S X \ Y ナ ; J S X _ Y ナ ; J S X b Y ナ ; J S X b Y ネ ; J S X e Y ネ ; J S X h Y ネ ; J S X l Y ネ ; J S X l Y ヒ ; J S X o Y ヒ ; J S X r Y ホ ; J S X v Y ム ; J S X z Y ム ; J S X z Y ヤ ; J S X ~ Y ヤ ; J S X ~ Y ミ ; J S X <83> Y ミ ; J S X <87> Y ヘ ; ...
JS
,X
,Y
などの文字が見えます。16進数でも確かめてみましょう。
4a 53 58 7b 59 81 3b 4a 53 58 78 59 81 3b 4a 53 58 75 59 81 3b 4a 53 58 75 59 84 3b 4a 53 58 72 59 84 3b 4a 53 58 72 59 87 3b 4a 53 58 6e 59 87 3b 4a 53 58 6e 59 8a 3b 4a 53 58 6e 59 8d 3b 4a 53 58 6b 59 8d 3b 4a 53 58 6b 59 90 3b 4a 53 58 68 59 90 3b 4a 53 58 68 59 93 3b 4a 53 58 68 59 96 3b 4a 53 58 65 59 96 3b 4a 53 58 65 59 99 3b 4a 53 58 62 59 99 3b 4a 53 58 62 59 9e 3b 4a 53 58 5f 59 9e 3b 4a 53 58 5f 59 a2 3b 4a 53 58 5c 59 a2 3b 4a 53 58 5c 59 a5 3b 4a 53 58 5c 59 a9 3b 4a 53 58 59 59 ad 3b 4a 53 58 59 59 b0 3b ...
以下の図は、4a 53
をJS
, 58
をX
, 59
をY
, 3b
を;
へ置き換えた様子です。
ジョイスティックの読み取りはチームメンバーのおにさわさんからご助言いただきました。ありがとうございます。
#Pythonで制御する
ざっくりとシリアル通信が読めたので、Pythonを使ってOrbital2 Coreの代替スクリプトを書いていきましょう。(Python 3.10.0
で検証)
Pythonでシリアル通信するために、pySerialというライブラリを使います。
$ pip install pyserial
import serial
import time
ser = serial.Serial('接続しているポート番号','9600',timeout=0.05)
time.sleep(1)
while True:
SR = (ser.readline())
SRstr = str(SR)
if "RC4=1" in SRstr:
print('exit')
ser.close()
exit()
elif "SW7=1" in SRstr:
print('Key7が押されました')
フラットリングの手前のボタンを押すと、コンソールに「Key7が押されました」と表示されるはずです。
オービタルエンジンの頂点を押すことで、スクリプトの実行を終了します。
import serialが通らない場合は、serialとpySerialが競合していないか確認しましょう
シリアル通信をするスクリプトだからといって、スクリプト名を「serial.py」にするのはやめましょう
##キーボードとマウスを操る
Pythonでキーボードとマウスを操るには、PyAutoGUIというライブラリが便利です。
$ pip install pyautogui
import pyautogui
pyautogui.press('space')
キーボードのSpaceを押すだけのスクリプトです。先程書いたシリアル通信のスクリプトといい感じに組み合わせてみてください。
長押ししたい場合は
SW7=1
のときにpyautogui.keyDown('space')
して、
SW7=0
のときにpyautogui.keyUp('space')
すると良いでしょう。
PyAutoGUIの詳しい使い方はドキュメントをご確認ください。画像認識もできるので、ショートカットキーの登録ができないアプリケーションも力技でなんとかすることができます。
##BlenderのShiftキーに対応する
PyAutoGUIが送信する仮想キーコードでは、Shiftキーによる視点操作ができません。
対応策として、PyDirectInputを使用します。
$ pip install pydirectinput
import pyautogui
import pydirectinput
pydirectinput.keyDown('shift')
pydirectinput.keyUp('shift')
これで、BlenderのShiftキーでの視点操作が正しく機能するはずです。
##アプリごとに自動でプロファイルを切り替える
アプリごとに自動でプロファイルを切り替えるには、アクティブウィンドウの情報を取得する必要があります。
詳しい方法は、きむら しのぶ (@mix_dvd) さんの記事に記載されています。
Pythonを使ってWindowsやMacを監視し、操作中のアプリの情報を収集してみる
Firefox Developer Edition
TweetDeck at DuckDuckGo — Firefox Developer Edition
TweetDeck — Firefox Developer Edition
Spotify Premium
山口百恵 - 横須賀ストーリー
Opening - Excel
Microsoft Excel
サンプル.xlsx - Excel
sample.py - Visual Studio Code
アプリ名だけでなく、様々な情報が取得できました。この情報をもとに条件分岐させましょう。
##ジョイスティックの読み取り
前章でジョイスティックの信号を読み取りました。
PythonでジョイスティックのXY座標を取得するには、以下のコードを記述します。
if "JS" in SRstr:
x = ord(SR[3:4])-128
y = ord(SR[5:6])-128
print("X",x,"Y",y)
##リストメニュー
Orbital2 Coreのリストメニューが便利だったので、似たようなものを作ります。
(長くなるので割愛)
GUIのフレームワークは何でも良いと思いますが、生成するウィンドウのlocationにはPyAutoGUIで取得したマウス座標を指定するのが良いでしょう。
window = PySimpleGUI.Window('ListMenu', layout, location=pyautogui.position())
##グロウリングについて
左手デバイスには、実用器としての価値と、鑑賞品の両面があるということを知らねばなりません。
大衆実用器として生まれたものには、往々、極めて低級な美を盛るに過ぎない大量的なものが多く、芸術的価値に富んで世間に騒がれているものは、その数こそ少ないですが、眼高の士の心をゆさぶるもの、即ち思想的個性の発露になるものが多いのでありまして、この両者を明確に区別してかかることが肝要であります。
実際、優れた左手デバイスに接しますと、名巌のような、松樹のような、琅玕竹のような、梅花のような、その美しさに打たれるものであって、それがどんな素材で出来ているとか、どういうふうにして作られたかというようなことは、第一義的には念頭に浮んで来ないのであって、立派な絵や建築を見る場合と少しも違わないのであります。第二義的には機能に渉り様々吟味もしますが……。
その中でもOrbital2などは日常自分でも使用して、あるいは愛玩したり、なんとなく親しみも生じて手にとって見たり、ダイヤルを廻してみたり、様々に変化のあるグロウリングの具合など賞めそやしたりして鑑賞の対象となり、嬉しさを感ずるのであります。
(改変元 : 陶器鑑賞について 北大路魯山人)
かようにして、独自のスクリプトからグロウリングのLEDの色を操ることは、刻下の急務であろうと考えられます。
正規のOrbital2 Coreソフトウェアからグロウリングへ向かう信号を解析することも考えましたが、生憎なことに、Orbital2 Coreソフトウェアの利用規約第4条は、プロトコルの解析を禁じているのです。
それならばLEDの操作は諦めるべきであろうか。どうしたものであろうか……なぞと、思案に暮れておりましたところへ、BRAIN MAGICの方から
「できる範囲ではありますが、Orbital2について何か提供などご協力ができればと存じます」と言う旨の連絡が来ました。
このような機会以外に、私がグロウリングのLEDを操る術を得ることは、将来、滅多に来ないような気がしましたから、「グロウリングのシリアル通信について教えてほしい」と言う意味のメールを出しましたところ、一週間程経ったあとに、「LEDの色を変更するには0x54, 0x00, red, 0x00, green, 0x00, blue, 0x00, 0x3b, 0x00
(red, green, blueは0x00〜0xFFまでのRGB)を送信すればよい」との返事を受け取りました。
正直のところを云うと「お答えできかねます」と追い返されるのが関の山だろうと考えておりましたので、その返事の予想外なのに、また寛容であることに驚いたものでありました。
ser.write(b"T,\x58,\xb6,\xd2,; ")
ser.write(b"T,\x00,\x00,\x00,; ")
これで、特定の曲が流れたときに色が変わるような芸当ができますね。
psutilでCPUやメモリの使用率を取得し、それをもとに色変更するのも楽しそうです。
##バイブレーションについて
バイブレーションを動かすためのコマンドついては聞きそびれてしまいましたが、LEDのコマンドからある程度推測出来ます。
総当りしてみたところ、0x4d, 0xff, 0x3b, 0x00
というコマンドを送信することで、Orbital2が振動することがわかりました。
ser.write(b"M,\xff,; ")
#Blenderで活用する
Blender内蔵のPythonでも、Orbital2とシリアル通信することができます。
詳しくは、Checkered Bugによるチュートリアルや、しましまさんの記事にて解説されています。
##BlenderにOrbital2を召喚する
BlenderにOrbital2召喚できた。 pic.twitter.com/PJCuHgAVcn
— ْ (@kame404) September 15, 2021
このツイートは、
① 現実世界のジョイスティックのXY座標を、Blender上のEmptyのXY座標へ割り当て
② Emptyの動きに合わせて、Blender上のジョイスティックオブジェクトが傾くよう、ドライバで紐付ける
ことにより実現しています。
import bpy
import serial
ser = serial.Serial('COM3','9600',timeout=0.0)
obj = bpy.data.objects["Empty"]
while True:
SR = (ser.readline())
SRstr = str(SR)
if "RC4=1" in SRstr:
obj.location = (0,0,0)
ser.close()
elif "JS" in SRstr:
x = (ord(SR[3:4])-128) / 100
y = (ord(SR[5:6])-128) / 100
print("X",x,"Y",y)
obj.location.x = x
obj.location.y = y
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
#おわりに
改めまして、GAMEJAMを企画し開催して下さったcluster様、
スポンサー賞に「田んぼのある生活」を選んで下さったデジタルハリウッド大学様、
当記事の公開を許可して下さったBRAIN MAGIC様に厚く御礼申し上げます。
チーム「おにさわランド」の「田んぼのある生活」をどうぞよろしくお願いします!
こちら、【デジタルハリウッド大学賞】を頂きました!
— ْ (@kame404) September 1, 2021
「田んぼのある生活」です。ありがとうございます!https://t.co/WYOpSfxqEU#ClusterGAMEJAM https://t.co/VcagZVYyGl