Removing the Delay After Keydown Event

Removing the delay after KeyDown event?

The answer in the proposed duplicate is incorrect, unfortunately. It doesn't ignore repeated KeyDown events, and so will gradually increase the "delta" value in the direction being handled by each key case. It also doesn't respond to the keypress immediately (i.e. no action happens until the first timer tick).

This answer to Holding Arrow Keys Down For Character Movement C# .Net ISSUES explains how to ignore the subsequent KeyDown events, but doesn't explain how then your character would move.

In other words, I couldn't find a duplicate question that actually correctly answers your question. So…

The basic technique you want to do is:

  1. Don't move on the actual key input. Instead, generate your own timing logic that will move the object.
  2. Instead of using the KeyDown event to actually move the object, use it to set a movement direction, which is then processed by your timing logic.

There are a variety of ways to accomplish this. One version would look like this:

private bool _moveUp;
private bool _moveDown;
private bool _moveLeft;
private bool _moveRight;

// You can add the Timer in the Winforms Designer instead if you like;
// The Interval property can be configured there at the same time, along
// with the Tick event handler, simplifying the non-Designer code here.
private System.Windows.Forms.Timer _movementTimer = new Timer { Interval = 100 };

public MainForm()
{
InitializeComponent();

_movementTimer.Tick += movementTimer_Tick;
}

private void movementTimer_Tick(object sender, EventArgs e)
{
_DoMovement();
}

private void _DoMovement()
{
if (_moveLeft) Player.MoveLeft();
if (_moveRight) Player.MoveRight();
if (_moveUp) Player.MoveUp();
if (_moveDown) Player.MoveDown();
}

// You could of course override the OnKeyDown() method instead,
// assuming the handler is in the Form subclass generating the
// the event.
public void MainForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.IsRepeat)
{
// Ignore key repeats...let the timer handle that
return;
}

switch (e.KeyCode)
{
case Keys.Up:
_moveUp = true;
break;
case Keys.Down:
_moveDown = true;
break;
case Keys.Left:
_moveLeft = true;
break;
case Keys.Right:
_moveRight = true;
break;
}

_DoMovement();
_movementTimer.Start();
}

public void MainForm_KeyUp(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Up:
_moveUp = false;
break;
case Keys.Down:
_moveDown = false;
break;
case Keys.Left:
_moveLeft = false;
break;
case Keys.Right:
_moveRight = false;
break;
}

if (!(_moveUp || _moveDown || _moveLeft || _moveRight))
{
_movementTimer.Stop();
}
}

Do note that the timer objects in .NET have limited resolution. I show an interval of 100 ms (10 times per second) above (same as in the other question's answer), and this is about as frequent an update as you're going to reliably get. Even then, the timer's Tick event may not (and probably won't) be raised on exactly 100 ms intervals. There will be some variation back and forth. But it will be close enough for a basic game.

If you need more precision than that, you will have to implement your own state-polling-and-animation loop somewhere. That's a whole other ball o' wax. :)

Remove keydown delay in JavaScript?

you could start an event on keydown and stop it on keyup

$('#mycanvas').on('keydown', function() { 
$(document).trigger('start');
});

$('#mycanvas').on('keyup', function() {
$(document).trigger('stop');
});

$(document).on('start', startAnimation);
$(document).on('stop', stopAnimation);

function startAnimation(e) { //start something }
function stopAnimation(e) { //stop something }

Remove key press delay in Javascript

If you want key repeat in a controllable fashion, you will have to implement it yourself, as keypress events are fired dependent on the OS's idea of how keys should repeat. That means there may be variable initial and following delays, and holding down two keys at once will cause only one of them to repeat.

You will have to keep a record of whether each key is currently pressed, and ignore keydown events when the key is already down. This is because many browsers will fire a keydown as well as a keypress event when an autorepeat occurs, and if you're reproducing key repeat yourself you'll need to suppress that.

For example:

// Keyboard input with customisable repeat (set to 0 for no key repeat)
//
function KeyboardController(keys, repeat) {
// Lookup of key codes to timer ID, or null for no repeat
//
var timers= {};

// When key is pressed and we don't already think it's pressed, call the
// key action callback and set a timer to generate another one after a delay
//
document.onkeydown= function(event) {
var key= (event || window.event).keyCode;
if (!(key in keys))
return true;
if (!(key in timers)) {
timers[key]= null;
keys[key]();
if (repeat!==0)
timers[key]= setInterval(keys[key], repeat);
}
return false;
};

// Cancel timeout and mark key as released on keyup
//
document.onkeyup= function(event) {
var key= (event || window.event).keyCode;
if (key in timers) {
if (timers[key]!==null)
clearInterval(timers[key]);
delete timers[key];
}
};

// When window is unfocused we may not get key events. To prevent this
// causing a key to 'get stuck down', cancel all held keys
//
window.onblur= function() {
for (key in timers)
if (timers[key]!==null)
clearInterval(timers[key]);
timers= {};
};
};

then:

// Arrow key movement. Repeat key five times a second
//
KeyboardController({
37: function() { Move(-1, 0); },
38: function() { Move(0, -1); },
39: function() { Move(1, 0); },
40: function() { Move(0, 1); }
}, 200);

Although, most action-based games have a fixed-time main frame loop, which you can tie the key up/down handling into.

How to fix delay in javascript keydown

Rather than trying to react directly to the keydown event, I'd suggest you use the keydown and keyup events to maintain a list of which keys are presently down. Then implement a "game loop" that checks every x milliseconds which keys are down and update the display accordingly.

var keyState = {};    
window.addEventListener('keydown',function(e){
keyState[e.keyCode || e.which] = true;
},true);
window.addEventListener('keyup',function(e){
keyState[e.keyCode || e.which] = false;
},true);

x = 100;

function gameLoop() {
if (keyState[37] || keyState[65]){
x -= 1;
}
if (keyState[39] || keyState[68]){
x += 1;
}

// redraw/reposition your object here
// also redraw/animate any objects not controlled by the user

setTimeout(gameLoop, 10);
}
gameLoop();

You'll notice that this lets you handle multiple keys at once, e.g., if the user presses the left and up arrows together, and the problem of delay between subsequent keydown events when a key is held down goes away since all you really care about is whether a keyup has occurred.

I realise you may not be implementing a game, but this "game loop" concept should work for you as shown in this very simple demo: http://jsfiddle.net/nnnnnn/gedk6/

JS Keydown have some strange 1 second delay before firing events after the first event

The delay is a default behavior.

So for gaming, you need to use the keydown and keyup events just to implement a key state by yourself. Then with an interval, check every x milliseconds and update the position.

In vue.js the code will be something like this (JSFIDDLE DEMO)

new Vue({
keys: {}, // this variable should be outside of reactive data
data: { // Your data here }
created () {
window.addEventListener('keyup', this.keyup);
window.addEventListener('keydown', this.keydown);

setInterval(this.customKeysChecker, 20);
},
methods: {
keydown (e) {
this.$options.keys[e.keyCode] = true;
},
keyup (e) {
this.$options.keys[e.keyCode] = false;
},
customKeysChecker() {
const { keys = {} } = this.$options;

if (keys[37]) this.onWalkLeftPressed();
if (keys[39]) this.onWalkRightPressed();
},

onWalkLeftPressed() { //Your code here },
onWalkRightPressed() { //Your code here }
}
})

Here you are some helpful links to check:

  • remove key press delay in javascript
  • How to fix delay in javascript keydown

How can I fix keydown delays in Javascript game?

I briefly looked into your code. You render 10x a second, so if you manage to press more than one key during that interval, the described issue will occur:

For example, (while going right) hitting the up arrow and then the left arrow key too fast will make my snake turn around completely and run into itself, ignoring the up arrow key press.

There are two possible solutions:

  1. Run the render loop faster, so that no one can ever press two keys during that interval.
  2. Do not store only the last key, but all keys that were pressed between since the last render call.
  3. Avoid half-turns.

I think solution 1 is not ideal as you should never say never. So let's continue with number 3 (a hack) and then with number 2 (the correct and clean way).

Avoid half-turns (alternative 3)

This little hack does not solve the root of the problem, but it will make the snake behave kind-of correct. The snake can move in 4 directions, but it can always turn only in two directions. You could either use a two-keys control to trigger CW/CCW change, something like

let currentDir = "RIGHT"; //note I renamed your d to currentDir
let nextDir = undefined;
document.addEventListener("keydown", direction);

function direction(event) {
const key = event.keyCode;
while (~currentDir) {}; //wait until the control function is finished
switch (currentDir) {
case "LEFT": nextDir = (key === 37 ? "DOWN" : (key === 39 ? "UP" : nextDir)); break;
case "UP": nextDir = (key === 37 ? "LEFT" : (key === 39 ? "RIGHT" : nextDir)); break;
case "RIGHT": nextDir = (key === 37 ? "UP" : (key === 39 ? "DOWN" : nextDir)); break;
case "DOWN": nextDir = (key === 37 ? "RIGHT" : (key === 39 ? "LEFT" : nextDir)); break;
}
}

//and later in the movement control function:
currentDir = undefined; //avoid overwriting nextDir during this update,
// i.e. the while-loop inside of direction() will wait
switch (tmp) {
case "LEFT": snakeX -= box; break;
case "UP": snakeY -= box; break;
case "RIGHT": snakeX += box; break;
case "DOWN": snakeY += box; break;
}
currentDir = nextDir;
nextDir = undefined;

The four-keys version would work in a similar way, you can easily intergrate it to your code. The key is to use the pair of currentDir and nextDir and keeping currentDir constant over the whole 0.1s time between the render calls. But your problem would kind-of stay. A snake heading right would only continue up if you would press ↑ and ← immediately after each other.

let currentDir = "RIGHT";
let nextDir = undefined;
document.addEventListener("keydown", direction);

function direction(event) {
const key = event.keyCode;
while (~currentDir) {}; //wait until the control function is finished
switch (currentDir) {
case "LEFT":
case "RIGHT":
nextDir = (key === 38 ? "UP" : (key === 40 ? "DOWN" : nextDir)); break;
case "UP":
case "DOWN":
nextDir = (key === 37 ? "LEFT" : (key === 39 ? "RIGHT" : nextDir)); break;
}
}

Keys buffer (alternative 2)

The correct solution is even easier, but requires an array. It stores all keys pressed since the last render call in a queue.

keysPressed = [];
document.addEventListener("keydown", event =>
keysPressed.push(event.keyCode); //enqueues the key pressed

Having two or three keys pressed, you could virtually update the snake position inside of the 0.1s interval applying one valid turn in each frame. This could lead to delayed snake movement if you would be able to fill the buffer quickly with commands. It can be interesting to try out as a fun excercise. The movement function for the four-keys control would look like this:

{
if (keysPressed.length > 0 {
const key = keysPresses.shift(); //dequeues the oldest key
//if there are more keys in the queue, they have to wait until next time
switch (d) {
case "LEFT":
case "RIGHT":
d = (key === 38 ? "UP" : (key === 40 ? "DOWN" : d)); break;
case "UP":
case "DOWN":
d = (key === 37 ? "LEFT" : (key === 39 ? "RIGHT" : d)); break;
}
}

switch (d) {
case "LEFT": snakeX -= box; break;
case "UP": snakeY -= box; break;
case "RIGHT": snakeX += box; break;
case "DOWN": snakeY += box; break;
}
}

How to avoid keydown delay with jQuery?

A little walkthrough the situation :

Assuming that <input> value is "x" and you type backspace :
- When the keydown event fires the input's value is still "x".

- When the keypress fires, it still "x".

If you don't release the key :

__ keydown fires again, after some delay, depending on os I guess value is now "".

__ keypress fires again, value is still "".

__ When you release the key, keyup fires, value is "".

If you do release the key :

__ keypress fires directly, value is "".

The solution For IE10+ is to use the input event which will fire when the textEditable element's content has changed or, as suggested by @Mayhem, the change event, which won't even listen for key inputs and has a better browser support than input

$('#tbox').on('input change',function(e){    if($('#tbox').val() !== '') {        $('#btn').css({'display':'block'});    } else {        $('#btn').css({'display':'none'});    }});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><input type="text" id="tbox"></text><button type="button" id="btn"  style="display:none;">push me</button>

Is it possible to override the keydown repeat delay, in JavaScript?

Well, I figured out why my example wasn't looping. In the keydown loop, it was clearing the timeout before it expired:

if( repeatRateTimer != null )
{
clearTimeout( repeatRateTimer );
repeatRateTimer = null;
}
else
{
repeatRateTimer = setTimeout( function( ){ repeating = false; }, 1000 );
}

The timeout should be cleared only after it expires, so the if condition needed to be moved into the timeout function:

if( repeatRateTimer == null )
{
repeatRateTimer = setTimeout( function( ) {
repeating = false;
clearTimeout( repeatRateTimer );
repeatRateTimer = null;
}, 1000 );
}

I'll leave this bounty open in case someone can improve upon this, or provide a better alternative.



Related Topics



Leave a reply



Submit