インプット周りを調べてみた

60分の1秒ごとに振り返るんです

ボタン配置が気に入らない

 RPGツクールMVは色々と痒いところがあって、そのひとつにUIが変なことがあります。
 個人的にはメニューボタンとキャンセルボタンが同じescキーに割当てられていること。
 初期ドラクエ風味のメニューを連続で押すとコマンドが実行される的な使い方が気持ちいいんで、そうしたい。
 どのような改造か可能か確認した後、しっかり配置の仕様を作ろうと思ってます。
 最終的に最初の配置になる可能性もありますが(笑)

 で、とても良い記事がありました。
 RPGツクールMVにおけるパッド入力、Inputクラスの理解と改造 (前編) - QiitaRPGツクールMVにおけるパッド入力、Inputクラスの理解と改造 (後編) - QiitaRPGツクールMVにおけるパッド入力、Inputクラスの理解と改造 (特別編) - Qiitaの3本。
 …んーだいたい説明してあるし、もう書くことなくね?(笑)

 マウス・タッチパネルの操作は、RPGツクールMVの中でも槍玉に挙がるダメUI筆頭なので改善したいのですが、キーボード・パッドと全然違うので今回はパスで。
 上記の記事に書いてないし。

 てな訳で以下、上記の記事を読みつつ、そんな記事があるなんて知らなかったという体で書きます。
 自分のための備忘録と、書かないとなかなか身につかないという面があるからです。

Inputクラス

 入力関連では Input というそのまんまな名前の静的クラスが存在するので、これを中心に見ていきます。
 ちなみに、 Graphics クラスで例外的に F2, F3, F4 キーの処理をやってますが、開発用のキーですし、本当に例外ですね。

初期化

 もう完全に理解したんですが、RPGツクールMVって何はともあれ SceneManager を見れば良いのです。
 その中に SceneManager.initInput() ってメソッドがあって、Input.initialize() ってのがあって、 Input クラスの初期化をしてます。

 そこで Input._setupEventHandlers() ってメソッドがあって、_onKeyDown(), _onKeyUp(), _onLostFocus() のイベントハンドラが結び付けられてます。
 キーボードのキーが押し下げられたり、離されたりすると呼ばれるんですね。
 あと、ウィンドウがアクティブじゃなくなった時も呼ばれるんですね。

ハンドラの処理

 Input._currentState というプロパティがあって、そこに今のキーの状態(ON/OFF)が記録されています。
 前述のハンドラは、これらの ON/OFF を記録しています。
 Input から次の動作を呼び出すということはしていません。
 だから動作を起こすには、動作する側から入力状況を読み取るという処理が必要になります。

 んで、Input.keyMapper という変換マップによって、キーと機能が結び付けられてます。
 最初に気に入らないといってた、メニューとキャンセルが同じボタンなの気にくわないというやつ、どうも Input._isEscapeCompatible() が関わってる部分がそうらしく、小さな親切大きなお世話という言葉が頭に浮かんできます。
 そこで変換マップで 'escape' となってるところを、'menu' や 'cancel' に割り当て直せばいい、ということみたいです。

 ん、待てよ。escでメニューが開いてescで閉じるとか、一太郎の「ESCメニュー」だよな。
 もしかして仕様作った人は一太郎ユーザか?
 だいたい'escape'って機能名に対してメニューとキャンセルを割り当てるセンスがおかしい。
 しかし一太郎ユーザなら、まるっきり自然だ。

 そして僕は一太郎ユーザではないので、プラグインの中で Input.keyMapper を次のように書き換えた。

Input.keyMapper[ 27 ] = 'cancel';
Input.keyMapper[ 77 ] = 'menu';

 これだけで、esc(キーコード:27) をキャンセル専用、M(キーコード:77) をメニュー専用にできるんだ!!
 もう自在に設定できるわけだから、escキーにこだわる必要ないな、メニューとキャンセルが共用のキーしか用意されてないのが問題であって、別にメニューキーとキャンセルキーが用意できるなら、escキーそのままでも構わない。
 ただ、パッドの設定は最初からメニューとキャンセルが分かれてるのが不思議。キーボードの方が断然ボタン(キー)の数多いから別に用意されてて当然なのに。

パッドからの入力

 キーボードの入力は分かったとして、ゲームパッドからの入力はどうしているのでしょう。
 最初の記事から読んでいる読者にはパターンは読めたと思いますが、基点となるオブジェクトの毎フレーム実行されるメソッドを見るのです。
 つまり SceneManager.update() です。
 このメソッドの中で、SceneManager.updateInputData() が呼ばれ、そこから案の定 Input.update() が呼ばれています。
 で、Input.update() から Input._pollGamepads() という、間違いようのない名前のメソッドが呼ばれてます。

 さて、Input._pollGamepads() の中ではループがくるくるしてて、全パッド入力を見に行ってます。
 ここをいじればテイルズオブシリーズみたいに協力プレイできるRPGも作れる! かも。
 というか今時のブラウザは、Gamepad って入力用オブジェクトが JavaScript に普通にあるんですね。リンク先のMDNのページによるとChromeだとバージョン35で実装されてるみたい。
 ちなみに、Template:Google Chromeのバージョンの変遷 - Wikipedia によるとChrome がゲームパッド対応したのは ということのようです。
 RPGツクールMVが出たのが だから、なかなか素早く取り入れてますね。

 W3C見にいったら、なんだか未だに仕様がふわふわしてて不安になります。
 実際、いろんなゲームパッドを繋げてみると、十字キーが他のパッドだと普通のボタンの番号で帰ってきたり、アナログスティックとして返ってきたり、他にも色々予想外の挙動します。

 そもそも Gamepad って名前おかしくないですか? スティックとか無視ですか?
 この半端な状態は、任天堂とかセガとか仕様決めに関わってないのか?
 でもマイクロソフトは偉い。Gamepad の Gamepad-Sample 動作確認ページも用意しているぐらいですし、Xboxパッドは正しいレイアウトで信号が返ってくるようです(僕は持たない)

 閑話休題。
 GamePad 毎に Input._updateGamepadState() が呼ばれてて、この中で ツクールで使う形式に書き直してる感じです。
 最終的に、キー名称と状態(ON/OFF)を対応づけた Input._currentState プロパティに格納しています。
 このプロパティでの入力情報は、キーボードとバッドで共通のキー名称に変換されています。
 だから、キーボードであってもパッドであっても、この後は入力装置を区別する必要がありません。
 また、プラグインでキーに割り当てる機能が必要になったら、このキー名称を増やしていく、といった対応が取れそうです。

入力のチェック

 実際利用する際は、プロパティを直接読むのではなく、Input.isLongPressed(), Input.isPressed(), Input.isRepeated(), Input.isTriggered() あたりのメソッドを使って行けばいいようです。
 特に Input.isTriggered() がキーやボタンが押された場合をチェックするので、これさえ使えばほとんど行けそうです。

まとめ

 _onKeyDown のようなイベントハンドラは、これらの入力情報を生成する際に使われるだけで、イベントハンドラを利用した操作は行ってません。
 RPGツクールMVは、いわゆるイベントドリブンなプログラムではない、ということです。
 アップデートが起動される順番を厳密にしたかったのか、単にプログラマが慣れていないのかはよくわかんないですが。

 SceneManager.update() メソッドを基点とし、必要なオブジェクトの update() を伝言ゲーム方式で呼んでいき、その中でInputの状態を監視(ポーリング)するというのがRPGツクールMV の入力処理の作法のようです。
 ちなみに、polling とは意見を聞くとか世論調査というような意味です。

 そこで結論。

ひたすら、update() で Input を監視だ