I'm making a simple JavaScript game (Space Invaders style) (Top-down space shooter) and I'm trying to make my character shoot a bullet per each 'space' key press. How can I do that?
I have tried multiple approaches, setting a flag, using onkeypress instead of keydown, Google searches (have also encountered this similar question yet it didn't help: Javascript onkeydown event fire only once?)
Below is an example of one solution I have tried.
document.onkeydown = function(e)
{
if(e.keyCode == 32 && space == false)
{
space = true;
}
}
document.onkeyup = function(e)
{
if(e.keyCode == 32) space = false;
}
window.requestAnimationFrame(gameLoop);
function gameLoop(timeStamp)
{ if(space === true)
{
p.shoot();
}
window.requestAnimationFrame(gameLoop);
}
Expected results: Key being fired only once.
Actual results: Key is being fired multiple times.
There's really 3 states to a bullet - and, taking into consideration that in classic Space Invaders the player can only have one bullet in flight at a time, this makes things relatively simple
The bullet can be
NONE - doesn't exist
FIRED - i.e. need to create one
EXISTS - it's in flight
The code to handle it is relatively simple too
var BulletState = {
NONE: 0,
FIRED: 1,
EXISTS: 2
};
var bullet = BulletState.NONE;
document.onkeydown = function(e) {
if (e.keyCode == 32 && bullet === BulletState.NONE) {
bullet = BulletState.FIRED;
}
}
window.requestAnimationFrame(gameLoop);
function gameLoop(timeStamp) {
if (bullet === BulletState.FIRED) {
bullet = BulletState.EXISTS;
p.shoot(); // p.shoot needs to set bullet = BulletState.NONE when the bullet expires
}
window.requestAnimationFrame(gameLoop);
}
document.onkeyup = function(e)
{
// not required at all
}
edit: To fire a bullet every press of spacebar (allowing multiple bullets)
var space = false;
var fireBullet = 0;
document.onkeydown = function(e) {
if (e.keyCode == 32 && !e.repeat && !space) {
fireBullet++;
space = true;
}
}
window.requestAnimationFrame(gameLoop);
function gameLoop(timeStamp) {
while (fireBullet) {
p.shoot();
fireBullet--;
}
window.requestAnimationFrame(gameLoop);
}
document.onkeyup = function(e)
{
space = false;
}
I use fireBullet++ and fireBullet-- because I guess it's plausible that someone could press and release and press the spacebar within 16ms (single frame) :p
You could also do
function gameLoop(timeStamp) {
if (fireBullet) {
p.shoot();
fireBullet--;
}
window.requestAnimationFrame(gameLoop);
}
that way you still handle multiple bullets OK, but only fire off one per frame - really depends on how you want to handle someone with a very fast trigger finger :p
As #JaromandaX suggested, space = false; after p.shoot(); will give you the desired result:
Reproducing the issue:
var space = false;
var element = document.querySelector('input');
element.onkeydown = function(e) {
if (!space && e.keyCode == 32) {
space = true;
console.log('event fired');
}
};
element.onkeyup = function(e) {
if (e.keyCode == 32)
space = false;
}
window.requestAnimationFrame(gameLoop);
function gameLoop(timeStamp) {
if (space == true) {
console.log('shoot');
}
window.requestAnimationFrame(gameLoop);
}
<input />
EDIT: Added another isShot variable to keep track of shot fired and space key is down in requestAnimationFrame event:
var space = false;
var element = document.querySelector('input');
var isShot = false;
element.onkeydown = function(e) {
if (!space && e.keyCode == 32) {
space = true;
console.log('event fired');
}
};
element.onkeyup = function(e) {
if (e.keyCode == 32) {
space = isShot = false;
}
}
window.requestAnimationFrame(gameLoop);
function gameLoop(timeStamp) {
if (space && !isShot) {
console.log('shoot');
isShot = true;
}
window.requestAnimationFrame(gameLoop);
}
<input />
Here you have two event, on each key press three are three even that get's fired
1. keydown (when user press the key but not yet release the key)
2. keypress (its fired after the keydown event)
3. keyup (When user release the key)
If you want to fire only once for each keypress
implement on of those if implement all of the three all three methods will fired.
Additionally: for safety you can write e.preventDefault().
Related
So what I need is to be able to hold down the spacebar key and the script needs to repeatedly spam the spacebar key as if I was doing it by tapping.
I have some code already written but I can't get it to work like I want.
document.addEventListener("keydown", function(event) {
if (event.keyCode == 32) {
var i = 0;
setInterval(() => {
var e = document.createEvent('HTMLEvents');
e.keyCode = 32;
e.initEvent(++i % 2 > 0 ? 'keyup' : 'keydown', false, true);
window.dispatchEvent(e);
}, 35);
}
});
So basically this works, when I press the spacebar it repeatedly presses the spacebar up and down, however it doesn't stop doing it when I release the space bar, however it doesn't actually start until I press the spacebar which means it's somewhat working.
I know the problem is with this line e.initEvent(++i % 2 > 0 ? 'keyup' : 'keydown', false, true);
but I've actually recovered this code from another project and can't figure out what it's doing or how.
Does anyone have any suggestions?
Not sure if this is what you need. Have a look. Event has a repeat property.
let test = 1;
let temp = document.querySelector('.test');
temp.innerHTML = test;
document.addEventListener("keydown", function(event) {
if (event.keyCode == 32) {
console.log(event.repeat);
test += 1;
temp.innerHTML = test;
}
});
<div class = 'test'>
<div>
I have code for 3 different task which I want to execute by clicking and pressing a key, so there will be 3 different combination of clicking and pressing. For example-
window.addEventListener("keydown", function(e){
if(e.keyCode === 16) {console.log('Yap! Shift works...');}
if(e.keyCode === 17) {console.log('Yap! Ctrl works...');
document.addEventListener('click',function (event) {
console.log(event.target.className);
}, false);
}
},false);
Now, when I press click shift key, I get related output, when I click Ctrl key and then click, I get the class name of the object I click on.
But the problem is, the output keeps coming as much I hold the key!! I want to execute the part of my code for once, and exactly when the key is pressed and a clicked is occurred.
How can I do that?
In general, how can I execute 3 part of code for three different tasks by clicking and pressing efficiently?
Adding an event handler while handing an event, is often the wrong way to solve a problem. Imagine how you will accumulate adding handlers... in your case there will eventually be many bindings to the same click handler.
It is better to bind the handlers you need immediately, and then work with keeping state on what exactly needs to happen while handling the event.
In these key handlers (keydown, keyup), keep track of whether the Shift/Control keys are depressed or not.
Also, use e.key as e.keyCode is deprecated.
Here is how that could work:
let keys = {
"Shift": false,
"Control": false
};
function keyToggle(e) {
if (!(e.key in keys)) return; // not ctrl or shift
let isKeyDown = e.type === "keydown";
if (isKeyDown === keys[e.key]) return; // key position did not change
keys[e.key] = isKeyDown;
console.log(e.key + (isKeyDown ? " pressed" : " released"));
}
document.addEventListener("keydown", keyToggle, false);
document.addEventListener("keyup", keyToggle, false);
document.addEventListener('click', function (e) {
if (keys["Control"]) console.log(event.target.className);
}, false);
<main class="main">Main</main>
<aside class="aside">Aside</aside>
As you addEventListener you can also removeEventListener.
For that you need a reference to your event handler, so you cannot use anonymous functions, but named functions or functions stored in a variable.
Edit
Here is an example of using CTRL+click:
// CTRL + CLICK implementation
let hasCtrl = false;
// Store the handler in a constant or variable
const handleClick = function(event) {
console.log(event.target.className);
}
// Use named function
function handleKeyDown(e) {
if (e.keyCode === 16) {
console.log('Yap! Shift works...');
}
}
const setCtrlInactive = (e) => {
if (!hasCtrl && e.keyCode === 17) {
console.log('Nope! Ctrl does not work...');
document.removeEventListener('click', handleClick);
hasCtrl = true;
}
}
const setCtrlActive = (e) => {
if (hasCtrl && e.keyCode === 17) {
console.log('Yap! Ctrl works...');
document.addEventListener('click', handleClick);
hasCtrl = false;
}
}
document.addEventListener("keyup", setCtrlInactive);
document.addEventListener("keydown", setCtrlActive);
document.addEventListener("keydown", handleKeyDown);
<main class="main">Main</main>
<aside class="aside">Aside</aside>
Well you can easily create an variable to lock it:
var locked = false;
window.addEventListener("keydown", function(e){
if(e.keyCode === 16 && !locked) {console.log('Yap! Shift works...'); locked =
true;}
if(e.keyCode === 17 && !locked ) {console.log('Yap! Ctrl works...');
locked = true;
}
},false);
document.addEventListener('click',function (event) {
if(locked){
// do something
console.log(event.target.className);
}
}, false);
window.addEventListener("keyup", function(){
locked = false;
}
That's because you called addEventListener('click') in the keydown event handler.
let ctrl = false;
window.addEventListener("keydown", function(e) {
if (e.keyCode === 16) {
console.log('Yap! Shift works...');
}
if (e.keyCode === 17 && ctrl === false) {
console.log('Yap! Ctrl works...');
ctrl = true;
}
});
window.addEventListener("keyup", function(e) {
if (e.keyCode === 17) {
ctrl = false;
}
});
document.addEventListener('click', function(event) {
if (ctrl) {
console.log(event.target.className);
}
}, false);
Instead, you should use keyup event and flag variable.
How can I catch, for example, tab+t combination with jQuery? I've found a lot of examples with alt, shift and ctrl, since event object contains special flags in order to understand if, for example, alt was pressed. But there is not such thing for tab.
This should work. It's a bit convoluted and there is likely an easier way, but it works fine.
JSFiddle: https://jsfiddle.net/spybhhxc/
var tabdown = false;
var tdown = false;
$(document).keydown(function(e) {
if(e.which == 9) {
tabdown = true;
}
if(e.which === 84)
{
tdown = true;
}
if(tabdown && tdown)
{
//do your thing
}
});
$(document).keyup(function(e) {
if(e.which == 9) {
tabdown = false;
}
if(e.which === 84)
{
tdown = false;
}
});
This presents a problem though, as once you press tab, the document is unfocused as the tab key navigates to elements in a browser. You would be much better off using something like alt or ctrl which don't interact with the browser.
We can have a tab key pressed [tabPressed] variable which will be set to true on key down and unset the same on its key up event. We will using the tab key pressed[tabPressed] variable to check whether it is in pressed state during the other key press activities. The tab keycode is 9.
jsfiddle link http://jsfiddle.net/e3Lveyj2/
var tabPressed=false;
function handleKeyDown(e) {
var evt = (e==null ? event:e);
if(evt.keyCode == 9){
tabPressed=true;
}
if ((tabPressed) && (evt.keyCode == 84)) {
alert ("You pressed 'Tab+t'")
}
}
function handleKeyUp(e) {
var evt = (e==null ? event:e);
if(evt.keyCode == 9){
tabPressed=false;
}
}
document.onkeydown = handleKeyDown;
document.onkeyup = handleKeyUp;
I am developing a game where you must press the left and right arrow keys alternatively to make the character move, the faster you do it, the quicker he runs. I have however ran into a problem whereby the key is being "held down" in a sense so no matter how quick you press the key it still manages to execute it multiple times.
So I am looking for a way to make the key only be pressed once per press rather than updating if you hold down the key.
here is the code for my key capturing and what it executes at the moment (Which is just an update of points and an update of the image used for the character.
KEY_CODES = {
37: 'left',
39: 'right',
40: 'down',
}
KEY_STATUS = {};
for (code in KEY_CODES) {
KEY_STATUS[KEY_CODES[code]] = false;
}
document.onkeydown = function (e) {
var keyCode = (e.keyCode) ? e.keyCode : e.charCode;
if (KEY_CODES[keyCode]) {
e.preventDefault();
KEY_STATUS[KEY_CODES[keyCode]] = true;
}
}
document.onkeyup = function (e) {
var keyCode = (e.keyCode) ? e.keyCode : e.charCode;
if (KEY_CODES[keyCode]) {
e.preventDefault();
KEY_STATUS[KEY_CODES[keyCode]] = false;
}
}
function move() {
movecounter++;
// Determine if the action is move action
if (KEY_STATUS.left || KEY_STATUS.right ||
KEY_STATUS.down || KEY_STATUS.up) {
// Redraw the canavs background images ready for the new ones to be placed ontop.
paintCanvas();
// to have diagonal movement.
if (KEY_STATUS.left) {
ctx.drawImage(imageStore.snowWalk, playerPosW, playerPosH);
snowStand = true;
score += 10;
} else if (KEY_STATUS.right) {
ctx.drawImage(imageStore.snowWalk, playerPosW, playerPosH);
score += 10;
} else if (KEY_STATUS.down) {
ctx.drawImage(imageStore.snowCrouch, playerPosW, playerPosH);
snowStand = false;
}
}
};
Hope you understand the problem I am facing, I have tried to explain it as best as I can here.
Thanks!
A simple solution would be to have a variable outside of the scope of the event listener that tells you if you're pressing the key for the first time:
var pressed = false;
Then, in the listener, toggle the state of that variable when the key is pressed:
.keydown(function() {
if (pressed) return;
pressed = true;
}
.keyup(function() {
pressed = false;
}
so right now I'm using a function that will set a value to true if one key being pressed, another being pressed regardless of whether or not the first one is still depressed.
function doc_keyUp1(e) {
if (e.keyCode == 37){
lPunch = true
}
}
function doc_keyUp2(e) {
if (e.keyCode == 39){
rPunch = true
}
}
document.addEventListener('keyup', doc_keyUp1, false)
document.addEventListener('keyup', doc_keyUp2, false)
The thing is, I want to be able to have it make sure that if the second key is being pressed, that the first one must still be down, so that someone can't just press one then the other quickly and make it seem as if they were both pressed down at the same time.
Any ideas?
Assuming you have some kind of "game loop" something like the following works (or perhaps I should say "should work", in that I haven't coded something like this for a long time and so haven't tested it with current browsers - definitely used to work):
var keyPressed = {};
document.addEventListener('keydown', function(e) {
keyPressed[e.keyCode] = true;
}, false);
document.addEventListener('keyup', function(e) {
keyPressed[e.keyCode] = false;
}, false);
function gameLoop() {
if (keyPressed["39"] && keyPressed["37"]) {
// do something (update player object state, whatever)
}
// etc
// update display here
setTimeout(gameLoop, 5);
}
gameLoop();
I'd suggest you use an Array to hold key states.
var keyStates = [ ];
document.addEventListener('keydown', function(e) {
keyStates.push( e.keyCode );
}, false);
document.addEventListener('keyup', function(e) {
var pos = null;
if( (pos = keyStates.indexOf( e.keyCode )) > -1 )
keyStates.splice( pos, 1 );
}, false);
So with that, you can always check that array for keys currently beeing pushed.
var currentKeyCodes=new Object();
function keyDown(e) {
currentKeyCodes['x'+e.keyCode]=true;
}
function keyUp(e) {
//Real check here
if ((e.keyCode==39) && currentKeyCodes['x37']) {
do_whatever_you_want();
}
//Housekeeping
var s='x'+e.keyCode;
if (currentKeyCodes[s]) currentKeyCodes[2]=false;
}