⚔️

『RPGツクールMZ』の入力機器周り

2025/02/21に公開
2

『RPGツクールMZ』関連記事 目次

『RPGツクールMZ』はキーボード、ゲームパッド、マウス、タッチスクリーンの入力に対応していて、それ自体は非常に良いことですが、デメリットとしてプログラムがややこしいことになっています。
今回そのややこしい入力周りのコードを読み解く場合の一助となる記事を書ければな…というか自分の理解のための記事です!(いつもそうだという話もある)

『RPGツクールMZ』で自作のインタフェースを実装したいという場合には、意外と役に立たない知識であることを事前にご了承ください(笑)

自作インタフェースの作成に関しては次の記事を参照してください。
『RPGツクールMZ』シーンのウィンドウ生成を調べてみた

『RPGツクールMZ』非公式JavaScriptリファレンスJavaScript - MDN、適宜これらのリファレンスのページにクラスなどリンクしていこうと思います。

基本的な用語については『RPGツクールMZ』用語集も参照ください。

入力機器ごとのAPI

JavaScript はキーボードとマウスの入力を前提として設計されましたが、その後ゲームパッドやタッチスクリーンに対応する拡張が行われています。
『RPGツクールMZ』での扱いを知る前に、JavaScript での扱いをおさらいしておきましょう。

キーボード

JavaScript 初期から入力に対応していますが、とりあえず入力できればいいという感じで用意されたものなので使い勝手は良くなく、騙し騙し使っているというのが現状です。
イベント発生タイミングはkeydownkeyupしかなく、キーボードのディレイ(リピート開始までの時間)やリピート(押しっぱなしで連続入力になる間隔)の設定もありませんし、現在キーが押されているか離されているかすらわかりません(なおkeypressは非推奨機能)

ちなみに直接のキーボードの機能ではありませんが、日本語入力モードの切り替えもできません。

使いやすいキーボード APIの提案もなされていますが、2025年現在も実験段階です。

なので必要な機能は『RPGツクールMZ』のコアスクリプトの方で拡張してあります。

ゲームパッド

JavaScript はゲームパッドの入力を受け取るためのクラスを持っています。

ゲームパッド API - MDN

Safariが2017年に対応して、ひととおりのプラットフォームで使えるようになったという、わりと新し目の機能です。
なので、2015年発売の『RPGツクールMV』はもちろん2020年の『RPGツクールMZ』でもボタン割当てや複数のゲームパッドへの対応などもなく「一応使えないこともないですよ」ぐらいの対応度です。

マウス

マウスの他トラックボールなどのポインティングデバイス共通のAPIです。

ボタンの数やスクロールホイールの入力方向など、ハードの差が大きいので、全部対応しようとすると意外に大変です。
イベントはmouseUp``mouseDown``mouseClick``dblclickまたmouseEnter``mouseOverなどなど、それなりに充実しています。
大抵はボタンを押すために使うので、マウスの機能というより画面上のボタンの機能として理解した方がわかりやすいかもしれません。

タッチスクリーン

タッチスクリーンによる入力に対応する部分です。
マウスの入力に対応していれば、大抵の部分で不都合なく動作しますが、一部タッチ入力専用の処理が必要になり、そのためのAPIも用意されています。

タッチイベント - MDN

イベントはtouchend``touchmove``touchcancel``touchstartがあります。

マウスのようにポインタがなく、タッチとドラッグしか存在しないので、マウスオーバー(ポインタがボタン上に乗る)が発生しないところに気を使う必要があります。
具体的にはマウスだと1回クリックで決定するのが、タッチだと2回タッチで決定になるというパターンです。

入力チェックはどこから呼ばれているのか

SceneManager.updateInputData()メソッドを見てみましょう。

SceneManager.updateInputData = function() {
    Input.update();
    TouchInput.update();
};

『RPGツクールMZ』はSceneManagerがフレーム実行の起点なので、そこから入力関連のオブジェクトも呼ばれています。
1フレーム1/60秒で呼ばれていて、これはアクション性のないRPGとしては十分な入力検知間隔と言えます。

Inputクラス

キーボードとゲームパッド入力は Inputクラスが担っています。
このクラスはメソッドやプロパティがすべて静的( Static )な、いわゆるユーティリティクラスです。

ちなみにアナログスティックに直接対応はしていません。

Input.update()

Input.update()を見てみましょう。

Input.update = function() {
    this._pollGamepads();
    if (this._currentState[this._latestButton]) {
        this._pressedTime++;
    } else {
        this._latestButton = null;
    }
    for (const name in this._currentState) {
        if (this._currentState[name] && !this._previousState[name]) {
            this._latestButton = name;
            this._pressedTime = 0;
            this._date = Date.now();
        }
        this._previousState[name] = this._currentState[name];
    }
    if (this._virtualButton) {
        this._latestButton = this._virtualButton;
        this._pressedTime = 0;
        this._virtualButton = null;
    }
    this._updateDirection();
};

まず_pollGamepads()が呼ばれています。
ゲームパッドからの入力状態を_gamepadStatesプロパティに登録し、同時に対応したキーボードからの入力として_currentStateに登録します。

_currentStateプロパティにkeydownが行われた時に押されたキーが登録され、keyupで離されたキーが削除されるという方法で「現在押されているキー」を判定(Input.isPressed())しています。
なお、keydownkeyupのイベントはInput._setupEventHandlers()メソッドで受け取るように設定されています。

Input._setupEventHandlers = function() {
    document.addEventListener("keydown", this._onKeyDown.bind(this));
    document.addEventListener("keyup", this._onKeyUp.bind(this));
    window.addEventListener("blur", this._onLostFocus.bind(this));
};

_previousStateプロパティには直前の状態が入っているので、_currentStateプロパティとの差をチェックすることにより「最後に(ON/OFFが)変更されたキー」が判別できます。
加えて_pressedTimeプロパティにはキーが押され始めてからの時間が入っているので、それを使って「キーが押された直後」を判定(Input.isTriggered())します。
その他にも_pressedTimeisRepeated()でキーリピートの判定にも使われています。
この辺のトリガやキーリピート処理は JavaScript 本体でやっておいて、パラメータの変更だけで使えるようにして欲しかった部分ですが、仕方がないのでこのようにコアスクリプトで地味に実装してあるわけです。

_virtualButtonプロパティには実際に押されたキーではなくGUI上のボタンがクリックされた場合に送られてくるキーが登録されているので、何か登録されていたら_latestButtonに代入してそのキーが押されたものとして後の処理を行います。

最後に_updateDirection()が呼ばれています。
ここではこれまでの入力から、入力方向を判定し8方向の_dir8プロパティと4方向の_dir4プロパティに保存します。
これらはInput.dir8およびInput.dir4プロパティからテンキー対応の入力方向(たとえば↓は2)として取り出せます。

Inputクラスのメソッド・プロパティ

以上に書いたように、現在の入力情報がInputクラスに保存されています。

そして、次の表にまとめたメソッド・プロパティをチェックすることで取り出し入力を反映した動作がなされます。

メソッド・プロパティ 説明
Input.isPressed(keyName) 押されている
Input.isTriggered(keyName) トリガーされている
Input.isRepeated(keyName) リピートされている
Input.isLongPressed(keyName) 長押しされている
Input.dir8 8方向入力
Input.dir4 4方向入力
Input.date 最後に入力された時刻(ミリ秒)

ちなみにInput.dir4 === 8Input.isPressed("up") === trueはほぼ同じ意味になります。
ただし、Input.dir4は押しっぱなしをチェックするのでプレイヤーキャラクターの移動入力に使われます。

またInput.virtualClick()メソッドを使うことで、前述のようにスクリプトから擬似的なボタン入力が発生可能です。

keyNameのテーブル

さてここでメソッドでチェックされているkeyNameは何かというと、以下のプロパティに定義されている文字列です。

キーボードからの入力(キーコード)を『RPGツクールMZ』が扱っているkeyNameに変換するための対応表です。

Input.keyMapper = {
    9: "tab", // tab
    13: "ok", // enter
    16: "shift", // shift
    17: "control", // control
    18: "control", // alt
    27: "escape", // escape
    32: "ok", // space
    33: "pageup", // pageup
    34: "pagedown", // pagedown
    37: "left", // left arrow
    38: "up", // up arrow
    39: "right", // right arrow
    40: "down", // down arrow
    45: "escape", // insert
    81: "pageup", // Q
    87: "pagedown", // W
    88: "escape", // X
    90: "ok", // Z
    96: "escape", // numpad 0
    98: "down", // numpad 2
    100: "left", // numpad 4
    102: "right", // numpad 6
    104: "up", // numpad 8
    120: "debug" // F9
};

必要な機能をあらかじめ割り当ててあるので使いやすくなっています。
しかしキーボードからの入力はこれらのキーだけしかチェックしていないので、キーボードを使っていても、いちいち文字を選んで決定という回りくどい入力方法が必要になっています。

同様にゲームパッドからの入力(キーコード)を『RPGツクールMZ』が扱っているkeyNameに変換するための対応表です。

Input.gamepadMapper = {
    0: "ok", // A
    1: "cancel", // B
    2: "shift", // X
    3: "menu", // Y
    4: "pageup", // LB
    5: "pagedown", // RB
    12: "up", // D-pad up
    13: "down", // D-pad down
    14: "left", // D-pad left
    15: "right" // D-pad right
};

キーボードとほぼ同じkeyNameが割り当てられていることがわかります。
こうすることで、ゲームパッドかキーボードどちらかに対応しておけば、もう一方でも違和感なく操作できるというわけです。

利用方法

『RPGツクールMZ』ではWindow_Selectableでキー入力をチェックして、ウィンドウ内の操作に利用する、あるいはGame_Playerでプレイヤーキャラクターを操作するという使い方がなされています。

Inputクラスの具体的な使い方の参考となるプラグインとしてトリアコンタンさん作の、キー入力によってスイッチを切り替える'KeySwitch.js'があります。

ダウンロード : https://github.com/triacontane/RPGMakerMV/tree/mz_master/KeySwitch.js

このプラグイン使えば、大抵の場合は自分でスクリプト書く必要ない気もしますね(笑)

TouchInputクラス

マウスとタッチスクリーン入力は TouchInputクラスが担っています。

また画面の状態にかかわらず入力が取れるキーボードと違い、入力を受け取るボタンのような画面上の部品が必要なのでそちらの知識も必要になります。

TouchInput.update()

TouchInput.update()を見ていきましょう。

TouchInput.update = function() {
    this._currentState = this._newState;
    this._newState = this._createNewState();
    this._clicked = this._currentState.released && !this._moved;
    if (this.isPressed()) {
        this._pressedTime++;
    }
};

キーボードと比べるとかなりシンプルです。
1フレームが過ぎる間に起きたイベントによる変化は_newStateプロパティに記録されているので、それを_currentStateプロパティにコピーしてチェックするだけ。

マウスの動作はどうやって知るのかというと、JavaScript にはマウスの各種動作に対応したイベントが用意されています。
幸いなことにキーボードに比べるとその種類は潤沢です。
TouchInput._setupEventHandlers()メソッドでマウスで発生する各種イベントを対応するイベントリスナ(メソッド)と結びつけています。

TouchInput._setupEventHandlers = function() {
    const pf = { passive: false };
    document.addEventListener("mousedown", this._onMouseDown.bind(this));
    document.addEventListener("mousemove", this._onMouseMove.bind(this));
    document.addEventListener("mouseup", this._onMouseUp.bind(this));
    document.addEventListener("wheel", this._onWheel.bind(this), pf);
    document.addEventListener("touchstart", this._onTouchStart.bind(this), pf);
    document.addEventListener("touchmove", this._onTouchMove.bind(this), pf);
    document.addEventListener("touchend", this._onTouchEnd.bind(this));
    document.addEventListener("touchcancel", this._onTouchCancel.bind(this));
    window.addEventListener("blur", this._onLostFocus.bind(this));
};

で、イベント発生に対応したメソッドの中で_newStateプロパティを書き換えているので、1フレーム経過した間の状況はupdate()_currentStateプロパティに退避されることになります。

タッチスクリーンとマウスとで異なる部分は、マウスの場合「タッチせずにカーソルが動く状態がある」ぐらいなので、入力機器による差も最小限です。

TouchInputのメソッド・プロパティ

後は、必要なところで_currentStateプロパティを調べればマウスでの操作が可能になるというわけです。
チェック用の専用のメソッドは次のとおりです。

メソッド・プロパティ 説明
TouchInput.isClicked() クリックされている
TouchInput.isCancelled() 右クリックされている
TouchInput.isTriggered() トリガーされている
TouchInput.isPressed() 押されている
TouchInput.isRepeated() リピートされている
TouchInput.isReleased() 離されている
TouchInput.isLongPressed() 長押しされている
TouchInput.isMoved() マウスドラッグ中か
TouchInput.isHovered() マウスが移動中か

ただし最初に書いたとおり、キーボードと違ってボタンなどのGUI部品とのやり取りを考えなければいけません。

Sprite_Clickable

マウス入力を受け取っているもののひとつはSprite_Clickableクラスです。
次のようにいくつかの子クラスを持っており、実際は子クラスとして運用されていますが、使い方は共通しています。

update()processTouch()メソッドでTouchInputの状態をチェックして、自身に関係あるマウスの状態(ボタン上でクリックなど)ならば、適切なメソッドを呼びます。

メソッド・プロパティ 説明
onClick クリックされた
onMouseEnter ポインタが入った
onMouseExit ポインタが出た
onPress 押された

Window_Selectable

ウィンドウ周りでマウス入力を受け取っているのが、Window_Selectableです。
こちらも実際に使われているのは子クラスの方ですが、基本的な動作はすべてWindow_Selectableに含まれています。

Window_Selectable
(子クラスは大量にあるので省略。詳しく知りたい場合はリンク先を参照ください)

update()processTouch()メソッドでTouchInputの状態をチェックして、自身に関係あるマウスの状態(ボタン上でクリックなど)ならば、適切なメソッドを呼びます。

メソッド・プロパティ 説明
onTouchCancel() キャンセルされた
onTouchOk() 決定された
onTouchSelect() 選択された

onTouchOk()メソッドで項目がクリックされた他、実行可能な状態かのチェックをします。
onTouchOk()メソッドの中ではhitIndex()メソッドでクリック(タッチ)された場所に存在する項目の番号を取得します。
hitIndex()メソッドの中ではさらにhitTest()メソッドで、クリック位置がどの項目の枠内であるかの判定をしています。
以上で、クリックされた箇所の項目の番号を決定して、クリックの処理を実行するわけです。

クリックの処理はprocessOk()メソッドが担っていて、これはキー入力での選択でも呼ばれる処理です。

まとめ

具体的な動作は各 Window_〇〇 クラスや、Scene_〇〇 クラスの実装でなされていますが、今回は割愛しました。
というか、まだよく分かってないので書けません。

最初のあたりで書きしましたが、JavaScript の(キーボード)入力関連の作りは雑で、入力処理の前のかなりの部分を自前で実装する必要があります。
幸い、今回解説したようにその部分は『RPGツクールMZ』がやってくれています。

そのうち、入力の処理を自前で実装する方法もかけたら良いなと思っています(書けたら書くは書かないの法則)

レッツエンジョイ ツクールライフ!

Discussion