JavaScript/CSS/HTML で簡単なゲームを作成しようとしています。キーの押下を処理するために jQuery (および少しのアンダースコア) を使用しています。プレーヤーは矢印キーを使用してブロックを制御します。複数のキープレスを同時に処理する際に問題が発生しました。クロージャーが押されたすべての矢印キーを追跡するシステムを導入しています。これは、プレーヤーが次の順序でキーを押す場合にうまく機能します。
- プレーヤーが下を押す (ブロックが下に移動)
- プレーヤーが左を押す (ブロックが左斜め下に移動する)
- プレーヤーが下を離す (ブロックが左に移動)
- プレーヤーが左を放します (ブロックが停止します)
ただし、ステップ 3 と 4 を逆にすると、ブロックは停止します。その場合、実際に何が起こるかを次に示します。
- プレーヤーが下を押す (ブロックが下に移動)
- プレーヤーが左を押す (ブロックが左斜め下に移動する)
- プレイヤーが左を離す (ブロックが止まる)
予想される動作は、ステップ 3 でブロックが完全に停止するのではなく、下に移動し続けることです。
コードに入力したトレースから、指で矢印キーの 1 つを押したままにしている場合でも、keyup イベントが実際にそれ以上の keydown イベントの伝播を停止しているように見えます。
関連するコードのスニペットを次に示します。問題がどこにあるのか誰か教えてもらえますか?
// Creates an animation handler for a specific element.
// Animation reacts to any changes as they are submitted
var getMovementAnimator = function(element) {
var params = {},
$element = $(element);
return function(changes) {
_.each(changes, function(val, key) {
// Remove null or zeroish keys from animation params
if ( (val == 0 || !val) && _.has(params, key)) {
delete params[key];
} else {
params[key] = '+=' + val + 'px';
}
});
$element.animate(params, {duration: 0, queue: false});
console.log(params);
};
};
// Determines direction and speed of movement for an element
// after a keypress event
var getMovementChange = function(keyEvent, keydown) {
var isMoving = !!keydown,
params = {},
dir = '',
speed = keydown ? 5 : 0,
arrows = {left: 37, up: 38, right: 39, down: 40};
switch (keyEvent.which) {
case arrows.left:
dir = 'left';
speed = -speed;
break;
case arrows.up:
dir = 'top';
speed = -speed;
break;
case arrows.right:
dir = 'left';
break;
case arrows.down:
dir = 'top';
break;
}
// If key hit not one of above, do nothing
if (!dir) {
return false;
}
if (!speed) {
console.log('key up: ', dir);
}
params[dir] = speed;
return params;
}
// Sets up key handlers
$(document).ready(function() {
var board = $('#board'),
animatePlayer = getMovementAnimator('.player');
$(document).keydown(function(e) {
e.preventDefault();
var changes = getMovementChange(e, true);
animatePlayer(changes);
}).keyup(function(e) {
e.preventDefault();
var changes = getMovementChange(e, false);
animatePlayer(changes);
});
});