やってみる

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

JSにおけるキーイベントのリピート遅延

リピートの遅延や間隔はイベントだけでは制御できない。

成果物

keydown, keyup, keypressイベントにおけるリピートの遅延を確認する。

きっかけ

JavaScriptテトリス作ってみたい。

探してみた

たぶん誰かが作っているだろうと思って探してみた。

意外にもJSで実装されていてサイトで遊べるものが見つからない。上記はCSS3で作られたらしい。JSでないが、すごい。

プレイしてみたが……

入力に問題あり。

長押し時に文字入力用ウェイトが生じてしまい連続移動できない。たとえば→キー押下して右へ連続移動したいが、一マス分移動した直後、しばらく止まってしまう。移動が遅れてゲームオーバーになる。

この待機時間はOSのキーボード入力設定で「リピートの遅延時間」に当たる。また、連続入力される間隔も「リピートの間隔」などで設定する。ミリ秒単位で指定できるはず。Raspbian8.0なら以下のような設定画面になる。

f:id:ytyaru:20180806095739p:plain

本来、リピートの遅延や間隔はゲーム内で一律に指定すべき。もしOS毎に変わってしまったらゲーム性として致命的な欠陥となる。

長押しの挙動はバラバラ

どうやらJSで長押しするときのイベント発火は、OSやブラウザ毎に挙動が異なるらしい。え、HTML5とかの規格で統一されているんじゃないの……。

http://shain.blog.conextivo.com/2007/03/javascript.html

キーイベント

JSのキーイベントについて仕様を調べてみる。

https://developer.mozilla.org/ja/docs/Web/API/KeyboardEvent

三種類

名前 概要
KeyDown キーを押した時に発火する
KeyUp キーを離した時に発火する
KeyPress Ctrlなど修飾キーでなかった場合、キーを押した時に発火する

auto-repeat

長押しで連続入力される現象を「auto-repeat」と呼ぶらしい。

JSにおけるauto-repeatはOSレベルで制御されるらしい。つまりJSでは制御できない……。

リピート遅延や間隔における時間の仕様もなさそう。ブラウザの実装によって挙動が違うのは仕様がないせいだろう。

確認用サイトを作ってみた

リピート遅延

以下の環境にて、KeyDown, KeyPress, ともにOSで設定したリピート遅延が生じてしまうことが確認できてしまった……。

リピート遅延が生じてしまうと何がマズイ?

イベント駆動開発できない。

たとえばキー押下時、100ms毎にcounterをインクリメントしたい場合。

以下ではダメ。OSで指定したリピート遅延と間隔の時間が適用されてしまう。

window.document.onkeydown = function(e){
    counter++;
}

以下のように設定できたら楽だったのになぁ……と妄想した。こんなコードは書けないので注意。

onkeydown.repeat.delay = 1000; // リピート遅延1000ms
onkeydown.repeat.interval = 200; // リピート間隔200ms
window.document.onkeydown = function(e){
    counter++;
}

では、どうやってリピート遅延や間隔を制御するのか?

いわゆるゲームループが必要になる。1/60秒ごとに実行するループ文のことだ。

純化すると、以下のような実装が必要になると思われる。

IsOnKeyA = false;
window.document.onkeydown = function(e){
    if (e.code == 65) { IsOnKeyA = true; }
}
window.document.onkeyup = function(e){
    if (e.code == 65) { IsOnKeyA = false; }
}
while (1/60秒ごとに1回実行する) {
    if (IsOnKeyA) { counter++; }
}

これで1/60秒ごとにcounterがインクリメントされる。

これの何が問題かって? イベント駆動で実装できていないことだ。処理を一箇所にまとめられないことだ。

本当はonkeydownの関数にcounter++;文を実行させるだけで終わらせたかった。しかし時間制御するためメインループとしてwhile文を使わざるを得ないため、煩雑なコードになってしまった。

理想としては、以下のようにオブジェクト指向で開発できたら嬉しい。

game.fps = 60;
game.loop = function() {
    if (game.key.down.A) { counter++; }
}
game.start();

たぶんググれば見つかると思う。「JS ゲーム フレームワーク」とかで。

結論

JSにおけるキーイベントのリピート遅延は、イベントだけでは制御できない。制御用のコードを実装する必要がある。

おまけ

FPS

1秒間における描画回数を「FPS」と言う。「Frame per second」の略。ゲームジャンルのFPS(First Person shooter)とは別。

1秒間に60回描画するなら「FPS:60」と言う。FPS:60もあれば不満なくゲーム体験できるらしい。FPS:30以下は遅く、FPS:120はヌルヌル動くらしい。

しばしばPCの処理速度によって遅くなることがある。また、ディスプレイのHz数にも影響される。

人間が知覚できる光の点滅周期が1秒間に55回以下と言われている。