やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Joystickの入力を受付してみた

Joystick(GamePad)の入力を受け付けてみた。 JoyStickの動作確認、構造体の値を表示するアプリができた。

JoyStickの動作確認、構造体の値確認ができるアプリ

入手先

GitHub MEGA

使い方

GitHubのReadMe参照。 実際にJoyStickを挿して入力してみたほうが早い。

実装パターン

  • 方法
    • Windows API
      • Raw Input API
      • joy~関数
        • joyGetPos
        • joyGetPosEx
        • joySetCapture
    • DirectX
      • Direct Input
      • XInput

Raw Input API

Raw Input API にて Joystick を使用するときは、WinDDKを使う。 WinDDK の hidsdi.h をインクルードすることでHidP_GetValueCaps関数などを使用する。

によって、追加インクルードフォルダと追加ライブラリファイルを設定する。

C:\WinDDK\7600.16385.1\inc\crt;C:\WinDDK\7600.16385.1\inc\api;

hid.lib;gdiplus.lib;

ところが、hidsdi.h をインクルードすると以下のエラーがでる。

1>  Program.cpp
1>c:\winddk\7600.16385.1\inc\api\imm.h(707): error C2143: 構文エラー : ';' が '}' の前にありません。
1>c:\winddk\7600.16385.1\inc\api\imm.h(707): error C4430: 型指定子がありません - int と仮定しました。メモ: C++ は int を既定値としてサポートしていません

どうやらインクルードファイルの順序を変える必要があるらしい。 http://so-zou.jp/software/tech/device/wii/library/wiiyourself/

プロジェクト・プロパティ→構成プロパティ→VC++ディレクトリ インクルードディレクト

$(VCInstallDir)include
$(VCInstallDir)atlmfc\include
$(WindowsSdkDir)include
$(FrameworkSDKDir)\include

後1

C:\WinDDK\7600.16385.1\inc\ddk
$(VCInstallDir)include
$(VCInstallDir)atlmfc\include
C:\WinDDK\7600.16385.1\inc\api
$(WindowsSdkDir)\include
$(FrameworkSDKDir)include

後2

C:\WinDDK\7600.16385.1\inc\crt
$(VCInstallDir)include
$(VCInstallDir)atlmfc\include
C:\WinDDK\7600.16385.1\inc\api
$(WindowsSdkDir)\include
$(FrameworkSDKDir)include

後3

C:\WinDDK\7600.16385.1\inc\crt
$(VCInstallDir)include
$(VCInstallDir)atlmfc\include
C:\WinDDK\7600.16385.1\inc\api
$(WindowsSdkDir)\include
$(FrameworkSDKDir)include

上記パターンを試してみたが、エラーは解決しなかった。 どうやら、WinDDKのwindows.hをインクルードすると当該のエラーが発生するようだ。

実際に「;」を「}」の前に追記してみたが、新たなエラーが出て解決しなかった。 Raw Input API は諦めて、joy~関数にて実装を試みる。

joy~関数

関数 ボタン数 取得契機
joyGetPos JOYINFO 関数実行時
joyGetPosEx JOYINFOEX 関数実行時
joySetCapture JOYINFO 指定時間毎(ポーリング)
  • JOYINFO
  • JOYINFOEX

JOYINFOはボタンが4つしか認識できない。 手持ちのJoystickはボタンが12ある。 JOYINFOEXを使いたい。 対応する関数はjoyGetPosExしかない。 自前でポーリングする必要がある。

ポーリングは、タイマースレッドを立てることになるのだろうか。 面倒そう。

JOYINFOEX

JoyStick装置 構造体メンバ
ボタン1~32 dwButtons 各ボタンに2n値が設定されている。フラグとしてor演算子で同時押しを表現できる。
十字キー dwPOV 360度*100。0=↑、9000=→、18000=↓、27000=←。4500=→↑、13500=→↓、22500=←↓、31500=←↑
スティック dwXpos,dwYpos,dwZpos,dwRpos,dwUpos,dwVpos 0x0000~0xFFFF。中間値が中央の状態を意味する。0x0000は↑or←の端。0xFFFFは↓or→の端。

http://akiba.geocities.jp/directx7help/devdoc/live/directx/diover_44vo.htm
http://sackys-blog-extend.cocolog-nifty.com/blog/2012/10/post-5d05.html

JOYCAPS

wMid:1133
wPid:49686
szPname:Microsoft PC ジョイスティック ドライバ
wXmin:0
wXmax:65535
wYmin:0
wYmax:65535
wZmin:0
wZmax:65535
wNumButtons:12
wPeriodMin:10
wPeriodMax:1000
wRmin:0
wRmax:65535
wUmin:0
wUmax:65535
wVmin:0
wVmax:65535
wCaps:51
wMaxAxes:6
wNumAxes:4
wMaxButtons:32
szRegKey:DINPUT.DLL
szOEMVxD:
変数 意味
wMid Manufacturer Identifiers(製造者ID) 1133(Logicool, Inc.?)
wPid Product Identifiers(製品区分) 49686(Logicool DUAL ACTION?)
szPname Product Name(製品名) 今回は専用ドライバを入れていないせいかMicrosoftのドライバ名が入っている
wPeriodMin 最小ポーリング時間隔 10ミリ秒間隔まで反応できる

https://msdn.microsoft.com/ja-jp/library/windows/desktop/dd757103(v=vs.85).aspx

joySetCapture

WindowsMessageで制御できるようになる。 でも、JOYINFO構造体なのでボタンが4つまでしか認識できない。 今回は使わない。紹介だけ。

Windows Message

  • joySetCapture

上記の関数はポーリングの時間隔を入力するだけでポーリングできる。 ポーリングの時間隔には、JOYCAPSのwPeriodMin~wPeriodMaxの値を入力すればいい。

ボタン入力はUP/DOWNの状態が区別されてWindows Messageとして飛んでくる。 それをキャッチすればいい。

どう設計したらいいか

今回の実装とは関係ない話。

もし、JoyStickのclassをつくるなら、どんなインタフェース設計がいいか妄想してみた。

Joystickの入力受付インタフェースはどのように設計すべきか。 考える項目を出してみた。

性能的に、joyGetPosExを使うことになる。 ボタンを4つしか使えないjoyGetPos、joySetCaptureは論外。 すると、自前ポーリングの実装が必要になる。

  • joyGetPosEx
  • 自前ポーリングの実装

そもそも、どのような入力を受け付けるようにするかで異なる。 複雑になるのは以下の場合。

  • 単キーDown
  • 単キーUp
  • 単キーPress(Down&Up)
  • 同時押し
  • 長押し

連射するか否かも考慮する必要がある。 押しっぱなしのとき、連射するのか、しないのか。

  • 連射(する/しない。ON/OFF)
  • 連射速度

アナログ入力があるなら、ON/OFFスイッチだけでなく加速度などの量を入力できる。 アナログ入力は遊びの部分をうまく調整しないとピーキーすぎて誤動作してしまうこともあるっぽい。

  • 方向

バイスによっては振動(バイブレーション)機能もある。 でもAPIで制御できない。

アプリケーションの機能を呼び出すコマンドとして紐付ける必要がある。 キーバインドはユーザが任意に変更できたほうが望ましい。 たとえば、ジャンプコマンドは、↑キー押下か、1キー押下か、1と2の同時押しかなど。 場面ごとに異なるキーバーインドを用いる。いくつものキーバインドがあり、場面ごとに使い分ける。

アプリで使用する論理キーは、物理キーが発行する論理キーのいずれかである必要がある。 さもなければ、Joystickで実行できないコマンドが出てしまう。 キーバインドは必ず物理キーにあわせる必要がある。

  • Joystickが刺さっているか(なければキーボードなどのデバイスで操作できるようにする)
  • 物理キーが発行する論理キーは何か
  • 物理キーが発行できるキーだけで操作できるキーバインドの用意

また、Joystickの種別ごとにどのボタンでどの仮想キーが送られてくるか異なる。 デバイスごとに物理的なキーと仮想キーを調整できるようにしたい。 Joystickの機種ごとに、どの位置にあるボタンが、何の仮想キーを送出するかは統一されていない。 ユーザは異なる仕様のJoystickでも同じ位置にあるボタンで同じ意味として使いたいことがあるだろう。 ユーザ自身がJoystickの個体ごとに適切に設定する必要がある。 毎回設定しなおすのは大変。 できることならデバイスの固有IDか何かで紐付けた設定ファイルを読書することで使いまわせるようにしたい。

  • 物理キーと論理キーの紐付け

要素

  • Joystickデバイ
    • バイス仕様
      • ボタンの数(使用するフラグ値)
      • スティックの数(使用する変数)
    • 接続状態
  • 接続アルゴリズム(デバイスIDで識別できるか否かに関わる)
  • Joystickデバイス設定
    • バイスIDと設定ファイルの対応表
    • 物理キーと論理キーの対応表
  • コマンド
    • キー・コマンド(キーの組み合わせや時間)
    • アプリ・コマンド(アプリの機能)
  • 場面で使うフラグなどの変数(SceneState)

カプセル化の度合い

  • TCHAR化
  • メモリ確保・解放

  • 仮想コード作成

    • JOYSTICKID1, JOYSTICKID2, を enum化する
    • BUTTON1, ...
  • スティック値の使用変数パターン https://msdn.microsoft.com/ja-jp/library/windows/desktop/dd757112(v=vs.85).aspx

    • JOY_CAL_READ3
    • JOY_CAL_READ4
    • JOY_CAL_READ5
    • ...
  • 契機

    • WindowsMessageを発行するか
    • コールバック関数を用意するか
  • キー・コマンド

    • 状態変数そのまま
    • Down/Up/Press
    • 同時押し/長押し
    • 連射/速度
  • アプリ・コマンド

    • コマンドclass
    • 名前文字列
  • 場面変数(アプリ・コマンドで用いる状態変数)

  • キー・バインディング

    • アプリ・コマンド
    • キー・コマンド
  • ポーリング

    • メインループ
    • 独自タイマースレッド

所感

満足。かなり勉強になった。 今までの中で一番、自分で作った感がある。

以下のような項目を学習できた。

  • C++でclass作成
  • C++で文字列を扱う
    • basic_stringstream
    • basic_string
  • GDI+のDrawStringでテーブル表示
  • joy~関数にてJoyStickの情報取得