Keyboard Inputs [duplicate] - javascript

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 5 years ago.
I'm coding a project and I've got a problem when I try getting keyboard input in a dedicated class.
So this is my Controller class :
Controller = function() {
// Initialisation clavier
document.onkeydown = this.onKeyDown;
document.onkeyup = this.onKeyUp;
// Touches
this.left = false;
this.up = false;
this.down = false;
this.right = false;
}
// Touche appuyée
Controller.prototype.onKeyDown = function(event) {
if (event.keyCode == 37) {
this.left = true;
}
else if (event.keyCode == 38) {
this.up = true;
}
else if (event.keyCode == 39) {
this.down = true;
}
else if (event.keyCode == 40) {
this.right = true;
}
}
// Touche relâchée
Controller.prototype.onKeyUp = function(event) {
if (event.keyCode == 37) {
this.left = false;
}
else if (event.keyCode == 38) {
this.up = false;
}
else if (event.keyCode == 39) {
this.down = false;
}
else if (event.keyCode == 40) {
this.right = false;
}
}
... with which I create an occurence during initialization. But when I try to get boolean state in an other class :
// Déplacement
Player.prototype.move = function() {
if (controler.left) {
this.posHorizontal -= this.speed;
}
}
this isn't working ! When I display the state in the controller class it return 'true' but not in another classes. I'v got no error but only a 'false' displayed (i've tried with console.log but no way).
Thanks for help !

When the browser calls onKeyDown and onKeyUp methods, after the events happen, the this keyword inside the functions points to the document not to the Controller instance.
change the lines
document.onkeydown = this.onKeyDown;
document.onkeyup = this.onKeyUp;
to
document.onkeydown = this.onKeyDown.bind(this);
document.onkeyup = this.onKeyUp.bind(this);
and it should fix the issue.

Related

Prevent button from continuously firing while being held

I am trying to prevent specific buttons from continuously firing but have had no luck with any of my methods. I've tried several suggestions/answers from other stackoverflow posts but none have worked. I currently have a controller and for my left/right buttons I don't care if they repeat but for my jump button I do not want to be able to hold the button and have the character keep jumping. The closest I got to making this work was by using a Map and setting the button to it but unfortunately on keyup the character would also jump. Here is my current code:
class Controller {
constructor() {
this.left = false;
this.up = false;
this.right = false;
this.down = false;
this.pressed = {};
let keyDown = (e) => {
if (e.code == 'ArrowLeft') this.left = true;
if (e.code == 'ArrowRight') this.right = true;
if (e.code == 'ArrowDown') this.down = true;
if (e.code === 'ArrowUp') {
if (this.pressed[e.code] === false) { return }
else {
this.pressed[e.code] = false;
this.up = true;
}
}
}
let keyUp = (e) => {
if (e.code == 'ArrowLeft') this.left = false;
if (e.code == 'ArrowRight') this.right = false;
if (e.code == 'ArrowDown') this.down = false;
if (e.code === 'ArrowUp') {
this.pressed[e.code] = true;
this.up = false;
}
}
window.addEventListener('keydown', keyDown);
window.addEventListener('keyup', keyUp);
}
}
let controller = new Controller();
and some alternates that I've tried also
this.pressed = false;
let keyDown = (e) => {
if (e.code == 'ArrowLeft') this.left = true;
if (e.code == 'ArrowRight') this.right = true;
if (e.code == 'ArrowDown') this.down = true;
console.log(controller.pressed)
if (e.code === 'ArrowUp') {
if (!this.pressed) { return }
this.pressed = false;
this.up = true;
}
}
let keyUp = (e) => {
if (e.code == 'ArrowLeft') this.left = false;
if (e.code == 'ArrowRight') this.right = false;
if (e.code == 'ArrowDown') this.down = false;
if (e.code === 'ArrowUp') {
this.pressed = true;
this.up = false;
}
}
this.pressed = false;
let keyDown = (e) => {
if (e.code == 'ArrowLeft') this.left = true;
if (e.code == 'ArrowRight') this.right = true;
if (e.code == 'ArrowDown') this.down = true;
if (!this.pressed) {
this.pressed = true;
if (e.code == 'ArrowUp') {
this.up = true;
}
}
}
let keyUp = (e) => {
if (e.code == 'ArrowLeft') this.left = false;
if (e.code == 'ArrowRight') this.right = false;
if (e.code == 'ArrowDown') this.down = false;
if (e.code == 'ArrowUp') {
this.pressed = false;
this.up = false;
}
}
Logically I feel as though these should work but they don't. And in case it matters my character jumps based on this
if (controller.up && !this.jumping) {this.vy -= this.speed * 60; this.jumping = true;}
I've probably tried doing this at least 10 different ways and the closest I got was my character jumped twice before stopping (while holding arrowup).
EDIT: this gives 2 jumps then stops. Can anyone explain why he jumps twice before stopping?
this.keySet = new Set();
let keyDown = (e) => {
if (e.code == 'ArrowUp') {
if (this.keySet.has(e.code)) { this.up = false }
else { this.keySet.add(e.code); this.up = 'keydown' === e.type }
}
}
let keyUp = (e) => {
if (e.code == 'ArrowUp') { this.keySet.delete(e.code); this.up = false }
}
You can throttle the function
The way throttling works is when you call a function, it will execute the function like normal, but on subsequent calls it won't run again until enough time has passed since the last attempted execution.
Lodash has an implementation, but you can find versions of this all over the internet: https://lodash.com/docs/4.17.15#throttle

Javascript disable spacebar scrolling [duplicate]

This question already has answers here:
Pressing spacebar scrolls page down?
(3 answers)
Closed 2 years ago.
I am working on a WebGL application and I use the spacebar for movement of the camera. The problem is, when I press the spacebar the website also scrolls down. Is there a way to disable this feature?
None of the answers so far works reliably. They work for about a second, then the site scrolls down for a tiny amount of time and then the cycle repeats.
This is my code for the keypresses:
window.addEventListener("keydown", (e) => {
if(e.repeat) { return; }
if(e.which == 27 || e.which == 9) {
document.exitPointerLock();
checkMouse = false;
}
if(checkMouse) {
if(e.which == 87) { forwardPressed = true; }
if(e.which == 83) { backwardPressed = true; }
if(e.which == 65) { leftPressed = true; }
if(e.which == 68) { rightPressed = true; }
if(e.which == 32) { upPressed = true; event.stopPropagation(); event.preventDefault(); }
if(e.which == 16) { downPressed = true; }
}
});
As you can see, for the space key there already is one solution implemented but both types of answers I have gotten so far don't work.
You can do it something like this
$(document).keypress(function(event){
var keycode = (event.keyCode ? event.keyCode : event.which);
if(keycode == '32') {
event.preventDefault();
}
});
Add this to your javascript:
let checkMouse = true
window.addEventListener("keydown", (e) => {
if(e.repeat) {
return;
}
if(e.which == 27 || e.which == 9) {
document.exitPointerLock();
checkMouse = false;
}
if(checkMouse) {
if(e.which == 87) { forwardPressed = true; }
if(e.which == 83) { backwardPressed = true; }
if(e.which == 65) { leftPressed = true; }
if(e.which == 68) { rightPressed = true; }
if(e.which == 32) { upPressed = true; event.stopPropagation(); event.preventDefault(); }
if(e.which == 16) { downPressed = true; }
}
if (e.which == 32) {
return !(e.keyCode == 32);
}
});
checkMouse wasn't initialised before. It's working fine here.

Trouble with adding gravity to 2d platformer

I am trying to make a 2D platforming game and I am missing an essential part of it, gravity.
I have tried everything from look at questions on this website to looking at online tutorials and you-tube videos.
I really need help with getting gravity into this game as I am out of ideas.
As I said I have already tried looking at multiple tutorials and videos, including this one:
http://www.somethinghitme.com/2013/01/09/creating-a-canvas-platformer-tutorial-part-one/
but this one uses shapes created in canvas rather than images.
I already know that you have to use a velocity function as well as variable for the gravity. Then you have to put this all inside the key handler function so that it can get executed.
//spaceman variables
var sx = canvasWidth / 2; // start the spaceman in the centre
var sy = canvasHeight / 2;
var sdx = 0; // set initial speed to 0
var sdy = 0;
var sspeed = 3; // create a variable for speed
var gravity = 0.2;
var svX = 0;
var svY = (Math.random() * -10) - 5;
//set variables to use for key presses
//these are Boolean variables
//they can only be true or false
var rightPressed = false;
var leftPressed = false;
var upPressed = false;
var downPressed = false;
function keyDownHandler(e) {
if (e.keyCode == 39) {
rightPressed = true;
} else if (e.keyCode == 37) {
spaceman2Ready = true;
spacemanReady = false;
leftPressed = true;
} else if (e.keyCode == 38) {
upPressed = true;
} else if (e.keyCode == 40) {
downPressed = true;
}
}
function keyUpHandler(e) {
if (e.keyCode == 39) {
rightPressed = false;
sdx--;
} else if (e.keyCode == 37) {
spaceman2Ready = false;
spacemanReady = true;
leftPressed = false;
sdx--;
} else if (e.keyCode == 38) {
upPressed = false;
sdx--;
} else if (e.keyCode == 40) {
downPressed = false;
sdx--;
}
}
// add something to "listen" for an event
//an event is keypress, mouse movement, mouse click etc.
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (rightPressed == true) {
sdx = sspeed;
} else if (leftPressed == true) {
sdx = -sspeed;
} else if (upPressed == true) {
sdy = -sspeed;
} else if (downPressed == true) {
sdy = sspeed
}
if (rightPressed == false && leftPressed == false && upPressed == false && downPressed == false) {
sdx = 0;
sdy = 0;
}
sx = sx + sdx;
sy = sy + sdy;
}
if (spacemanReady) {
ctx.drawImage(spacemanImage, sx, sy);
if (spaceman2Ready) {
ctx.drawImage(spaceman2Image, sx, sy);
}
// basically the setTimeout part, but this time its requesting the function that we made at the top
requestAnimationFrame(draw);
}
// this is to get the loop for the animation to start going
window.addEventListener("load",
function() {
draw();
});
< /script>
</body >
</html>/
You should be using gravity to update your position every frame (in the draw function). This would look like:
if(sy > 0){
sdy += gravity
}
I have created a codepen with your game with gravity. I hope this helps!

Clearing setTimeout issues

I'm trying to set "mouseactive" to true less than a second after a key command, but I would like to cancel that action if the key is pressed within that time period. However I can't seem to figure out how to do this. This is what I have...
$(window).keydown(function(e) {
if (e.keyCode == 40) {
e.preventDefault();
mouseactive = false;
clearTimeout(t);
var t = setTimeout("mouseActive()",800);
} else if (e.keyCode == 38) {
e.preventDefault();
mouseactive = false;
clearTimeout(t);
var t = setTimeout("mouseActive()",800);
}
});
function mouseActive() {
mouseactive = true;
}
But this doesn't work, it doesn't set mouseactive back to true... can anyone tell me what I'm doing wrong here?
Edit: Cleaned up redundant code.
More Edits: Make sure your var t is defined outside any closure including $(document).ready. See below,
var t = null;
$(document).ready(function () {
//..below code except for var t = null
});
Declare var t outside the handler.
var t = null;
$(window).keydown(function(e) {
e.preventDefault();
if (e.keyCode == 40) {
mouseactive = false;
} else if (e.keyCode == 38) {
mouseactive = false;
}
if (t != null) clearTimeout(t);
t = setTimeout(mouseActive, 800);
});
function mouseActive() {
mouseactive = true;
}
Your problem is that t is not in scope the 2nd time the function runs. You need to make t a global variable .
var t;
$(window).keydown(function(e) {
if (e.keyCode == 40) {
e.preventDefault();
mouseactive = false;
clearTimeout(t);
t = setTimeout(mouseActive,800);
} else if (e.keyCode == 38) {
e.preventDefault();
mouseactive = false;
clearTimeout(t);
t = setTimeout(mouseActive,800);
}
});
function mouseActive() {
mouseactive = true;
}
P.S. Don't pass strings to setTimeout, pass functions. It uses eval when you pass strings.
you are redeclaring "t" all the time, try this:
var t = null;
$(window).keydown(function(e) {
if (e.keyCode == 40) {
e.preventDefault();
mouseactive = false;
if(t != null)
{
clearTimeout(t);
}
t = setTimeout("mouseActive()",800);
} else if (e.keyCode == 38) {
e.preventDefault();
mouseactive = false;
if(t != null)
{
clearTimeout(t);
}
t = setTimeout("mouseActive()",800);
}
});
function mouseActive() {
mouseactive = true;
}

Javascript keyboard event in an object

I got my keyboard working in a simple way:
rightPressed = false;
onKeyDown = function(pressEvent) {
if (pressEvent.keyCode == 39) rightPressed = true;
}
onKeyUp = function(pressEvent) {
if (pressEvent.keyCode == 39) rightPressed = false;
}
$(document).keydown(onKeyDown);
$(document).keyup(onKeyUp);
And it worked. Then i tried to put it all in a class:
function Tkeyboard(){
this.rightPressed = false;
this.onKeyDown = function(pressEvent) {
if (pressEvent.keyCode == 39) { this.rightPressed = true; alert("boom!"); }
}
this.onKeyUp = function(pressEvent) {
if (pressEvent.keyCode == 39) { this.rightPressed = false; }
}
$(document).keydown(this.onKeyDown);
$(document).keyup(this.onKeyUp);
}
In initialization I created an object:
keys = new Tkeyboard;
In main loop i put action:
if ( keys.rightPressed ) { rx+=1;}
And now it fails. The interesting part of the problem is that alert("boom!") is called, so variable should get modified too...
I would be grateful for any ideas.
The keydown/up callback loses its original scope when the it is actually run. You'll need to bind the callback to this. In the Prototype Framework, you would do this:
function Tkeyboard() {
this.rightPressed = false;
$(document).keydown(this.onKeyDown.bind(this));
$(document).keyup(this.onKeyUp.bind(this));
}
Tkeyboard.prototype.onKeyDown = function(pressEvent) {
if (pressEvent.keyCode == 39) { this.rightPressed = true; alert("boom!"); }
};
Tkeyboard.prototype.onKeyUp = function(pressEvent) {
if (pressEvent.keyCode == 39) { this.rightPressed = false; }
};
It should be similar in jQuery.
If you need an idea of how to build a full fledged keyboard class, check out the one I wrote.
In an event handler in jQuery (and in DOM events), this refers to the element the event is subscribed on (document in the sample). Use a closure if you want to refer to the original object.
function Tkeyboard(){
var self = this;
this.rightPressed = false;
this.onKeyDown = function(pressEvent) {
if (pressEvent.keyCode == 39) { self.rightPressed = true; alert("boom!"); }
}
this.onKeyUp = function(pressEvent) {
if (pressEvent.keyCode == 39) { self.rightPressed = false; }
}
$(document).keydown(this.onKeyDown);
$(document).keyup(this.onKeyUp);
}
this is set to the DOM element (in this case document) from which the event handler is called. In general, this is not bound to the object in javascript:
var a = {
f: function () {}
};
var b = { f: a.f};
var f = a.f;
a.f(); // this === a
b.f(); // this === b
f(); // this === window
One commonly used workaround is to bind this to a wrapper function:
function bind(func, that) {
return function() {
func.apply(that, arguments);
}
}
//...
$(document).keydown(bind(this.onKeyDown, this));
Or you could use closures:
function Tkeyboard() {
var that = this;
// use 'that' from here on
you can initiate new function it will work
function Tkeyboard() {
this.rightPressed = false;
this.onKeyDown = function (pressEvent) {
if (pressEvent.keyCode == 39) {
this.rightPressed = true;
console.log(this.rightPressed )
alert('boom')
}
}
this.onKeyUp = function (pressEvent) {
if (pressEvent.keyCode == 39) {
this.rightPressed = false;
console.log(this.rightPressed )
}
}
this.events = function(){
document.addEventListener('keydown', this.onKeyDown);
document.addEventListener('keyup', this.onKeyUp);
}
}
const keys = new Tkeyboard;
keys.events();

Categories

Resources