べるもダイアリー

Cave Putorium

ジョイスティックを何個も使う飛行機オタク向けのゲーム設定アプリを作った話 #4

使用したライブラリ

  • DirectX SDK www.microsoft.com ジョイスティックの入力値をとるのにこれがどうしても必要だった。今はWindows APIでなんとかなるっぽいのでそのうち .Net Core 5 対応のために移行したい。
  • MahApps mahapps.com せっかく作るならメトロ風UIでモダンなランチャーにしたい、ゲームなんだから見た目はかっこよくあってしかるべきだ。

物理を鍛える脳筋プレイからゲームシステムを理解して正しい魔法を組み合わせるとサクサク進むようになるのじゃ

自分が触ったことのある言語は2つ

会社で触ったVisual Basic 会社で毎日触ってるMATLAB 大学の授業で単位のために触ったJava(覚えてない)

3つだった。 でもJavaはなんかあまり覚えてないしMATLABはライセンス購入にお金がかかる、よしVisualBasicでつくったろ!

こうしてジョイスティックの軸の入力値をスライダーに反映できるGUIまでプロトタイプとして作成したところであることに気が付く。 自分が探したやりたいことのサンプルコードはみんなC#で書かれていた。なんとなく同じ.NETでほぼ置き換えが出来ることは知っているので、VBならどういう文法になるだろうと考えて手で変換しながら打ち込んでみた。でもこれ最初からC#で書いたほうが早いんじゃないか?

C#にすべて変換して計画を再スタートしてみる。

この class ってなんだ?structure とどう違うんだ?????? new ってなに????????

そう、会社の仕事でも構造化プログラミングのひどいやり方みたいなコードしか見てこなかった自分にとっては、未知のテクノロジーとの遭遇だった。

「要は……構造体に関数がくっついて、どのデータはどの関数で動かせばいいかひとまとまりになる……ってコト?」「これ考えた人、もしかして頭いいのでは???」

自分はこれからジョイスティックの設定をデバイスごとに管理しないといけない、するとジョイスティックの割り当て管理データとその処理方法を、ジョイスティックの概念を管理するクラス!というやつにしてあげると便利なのでは……

こうして彼らはデスクリムゾン……ではなくクラスベースのOOPを手に入れたのであった

(まあVBもクラスベースのOOPできますけど)

Class Diagram

  • DeviceControl
    • DeviceList devList DirectX SDKジョイスティックのオブジェクトをとるために必要なデバイスのリスト的なもの
    • Device[] joyStick DirectX SDK側から提供される各ジョイスティックのオブジェクト、こいつを叩くと入力値とか返してくれる。ぶっちゃけDeviceオブジェクトはjoyAssignに参照を渡したのでフィールドとして持ってなくていい気がする……歴史的な経緯で念のため残してある
    • JoyAssgn[] joyAssign AL側で設定を管理するための各ジョイスティックのインスタンス
  • JoyAssgn なんか予約語とかあったら怖いなーと思ってAssignのiを省略している。たぶん無用の心配だった。AL側で設定を管理するための各ジョイスティックのオブジェクト。この中に割り当てた項目のデータとかが入る。このオブジェクト自体をXMLとして出力したり読み込んだりして記録を保存している。
    • string productName 製品名
    • Guid productGUID プロダクトGUIDはデバイス製品ごとに固有のID、個体が違っても同じ製品なら同じIDである。
    • Guid instanceGUID インスタンスGUIDはデバイス個体ごとに固有のID、製品が同じでも違う個体なら違うIDである。
    • DetentPosition detentPosition 戦闘機のスロットルにはエンジンを切る位置とアフターバーナー開始位置があり、その辺には溝があって普通に動かしたときにはそのまま進まないようになっている。ジョイスティックのディテント位置を入力値の位置として保存する役目がある。
    • AxAssgn[] axis
    • DxAssgn[] dx
    • PovAssgn[] pov
  • AxAssgn 物理軸にどのゲーム内軸が割り当てられているか記録する
    • string axisName 割り当てられたゲーム内軸名
    • DateTime assgnDate AL上で割り当てられた瞬間の時間、BMSのゲーム内軸は排他的に割り当てられるため、BMSで複数のデバイスのいずれかの物理軸が同じゲーム内軸に割り当てられていると困る。これによってAL上では一番最近割り当てられた物理軸のみを、ひとつのゲーム内軸に割り当てるようにしている。
    • bool invert 物理軸の入力反転
    • AxCurve saturation 物理軸の入力飽和
    • AxCurve deadzone 物理軸の入力デッドゾーン
  • DxAssgn DXボタンにどの操作項目が割り振られているか記録する
    • Assgn[] assign = new Assgn[4] 4つの動作に対する割り当てが定義されている。ひとつの物理的な動作に対する割り当ては排他的とした。そのほうが設定がこんがらがらないし、一つのボタンに複数の操作項目を割り当てたいケースは滅多にない。
      • [1] ボタン押し下げ
      • [2] ピンキー同時押しでのボタン押し下げ
      • [3] ボタンを離す
      • [4] ピンキー同時押しでボタンを離す
  • PovAssgn Povという方向キーにどの操作項目が割り振られているか記録する
    • DirAssgn[]direction = new DirAssgn[8] 時計回りに12時から始まる8方向への方向キーの入力を記録する
  • DirAssgn Povという方向キーのひとつの方向側にどの操作項目が割り振られているか記録する
    • string[] callback 要素2つの配列になっているが、最初の要素はボタンを押したときの、最後の要素はピンキー同時押しでの操作項目割り当てとなっている。たしかBMSではPOVを離したときの割り当ては対応していない……はず

ところでこれ巨大な神クラスになってないよね?大丈夫かな。

BMS起動時の設定オーバーライド

この辺のデータはOverrideクラスとの連携によっていい感じに設定ファイルが生成され、多態性によって各バージョンとの互換性がとれるようになっている。

情報量「無」みたいな図

めちゃめちゃ簡単に説明すると次の図のようなシーケンスになっている (説明のためにわざと実際のコードとは順序や関数名が異なる部分あり) 実際にはもうちょっと細かいコンフィグファイルとかもいじってる