How do I make the multiple keydown event work without breaking? - javascript

$(document).on('keydown', function(event) {
const current = audios[Number(event.key) // audios is array of music files
current.currentTime = 0;
current.play();
});
I'm creating a drum application. If I press the number 2 with the keydown event, it will be activated. And while I hold down the number 2 and press 3, the number 2 will stop sounding. How can I make it happen? And Why do not this?

It looks like there's a syntax error in your code. const current = audios[Number(event.key) is missing a closing ].
Here is how I would approach it.
const pressedKeys = {};
$(document.body).keydown(function (evt) {
pressedKeys[Number(evt.key)] = true;
playSongs(pressedKeys);
});
$(document.body).keyup(function (evt) {
pressedKeys[Number(evt.key)] = false;
});
function playSongs(dict) {
for (let song in dict) {
if (dict[song] === true) {
audios[song].currentTime = 0;
audios[song].play();
}
}
}
This code keeps track of keys in a dictionary. Whenever a keydown is registered it adds it to the dictionary. Right afterwards, playSongs finds all keys that are true and plays that song.

Related

How to make audio element more responsive?

Introduction
Greetings, I have looked at some similar questions that are on this platform and none of them seem to match my problem or maybe I missed something but I will try my best to give it a try because I am new in this platform.
OverView
This audio app maps the keyboard keys to audio samples.
Problem
There is a audio delay when pressing the keys rapidly and after each press the sample sound keeps playing for 4 to 6 second before it calls another sound and doesn't kill the sound immediately after the keyboard button is released.
Code
This is what I tried:
const dump = console.log.bind(console);
const sample = new Object
({
drum: "/samples/drums/trance01/BD_Trance.wav",
clap: "/samples/drums/trance01/Clap_trance.wav",
});
const keymap = new Object
({
"KeyD": "drum",
"KeyC": "clap",
});
Object.keys(sample).forEach((smpl)=>
{
let node = document.createElement("audio");
node.id = (smpl+"Sample");
node.className = "instrument";
node.src = sample[smpl];
document.body.appendChild(node);
});
document.body.addEventListener("keydown", function keyListener(event)
{
event.preventDefault(); // kill it
event.stopPropagation(); // seal it's ashes in a capsule
event.stopImmediatePropagation(); // and hurl it into the sun!
let key = event.code;
// console.log("pressed: "+key);
let tgt = keymap[key];
if (!tgt){ dump(key+" - is unused"); return };
var intervalID = setInterval(myCallback, 500, 'Parameter 1', 'Parameter 2');
function myCallback(a, b)
{
let nde = document.getElementById(tgt+"Sample");
nde.play();
dump("play: "+tgt);
}
});
Following what happens in the logic and how elements respond in their own way (and time) is important for analysing what the issue may be.
In this case I believe this can be solved by cloning the source-node, not expecting it to play multiple instances of itself by itself (or that is what I believe should happen) - but we can force it to:
const dump = console.log.bind(console);
const sample = new Object
({
drum: "/samples/drums/trance01/BD_Trance.wav",
clap: "/samples/drums/trance01/Clap_trance.wav",
});
const keymap = new Object
({
"KeyD": "drum",
});
(Object.keys(sample)).forEach((item,indx)=>
{
let node = document.createElement("audio");
node.id = (item+"Sample");
node.className = "instrument";
node.src = sample[item];
document.body.appendChild(node);
});
function keyHandler(event)
{
if (event.ctrlKey){ return }; // whew .. that was annoying as f*ck
event.preventDefault(); // kill it
event.stopPropagation(); // seal it's ashes in a capsule
event.stopImmediatePropagation(); // and hurl it into the sun!
let key = event.code;
let tag = keymap[key];
let nde,tgt;
tgt = document.getElementById(tag+"Sample");
if (!tgt){ dump(key+" - is unused"); return };
nde = tgt.cloneNode(true);
nde.id = (tgt.id+"Clone");
nde.addEventListener("ended",function()
{
this.parentNode.removeChild(this);
});
document.body.appendChild(nde);
nde.play();
dump("playing: "+tgt);
}
document.body.addEventListener("keydown", keyHandler);

matter.js: collisionStart triggered many times for one collision

I am working on a game app using React native and Matter.js.
I am trying to implement a system that adds points every time a bullet hits a target.
In order to do this, I am trying to use collisionStart to detect the collision.
However, even though the bullet and target collide only once, the event seems to be triggered 41 times.
This is the code:
Matter.Events.on(engine, 'collisionStart', (event) => {
let pairs = event.pairs
for (const pair of pairs) {
if (pair.bodyA.label === 'bullet' && pair.bodyB.label === 'Worm') {
console.log("target hit");
}
}
})
In the end, I'm planning to replace console.log with something that adds points. At the current moment, one collision seems like it would trigger the add points 41 times, which is obviously not ideal.
Any ideas what is happening here and how I can get this to trigger only once for one collision?
Try next example. I take it from my own project [you need little adaptd from ts to js]:
Matter.Events.on(this.starter.getEngine(), "collisionStart", function (event) {
root.collisionCheck(event, true);
});
public collisionCheck(event, ground: boolean) {
const myInstance = this;
const pairs = event.pairs;
for (let i = 0, j = pairs.length; i !== j; ++i) {
const pair = pairs[i];
if (pair.activeContacts) {
if (pair.bodyA.label === "bullet" && pair.bodyB.label === "Worm") {
const collectitem = pair.bodyA;
this.playerDie(collectitem);
} else if (pair.bodyB.label === "bullet" && pair.bodyA.label === "Worm") {
const collectitem = pair.bodyB;
this.playerDie(collectitem);
}
// ....
}
}
}
public destroyBody = (destroyBody) => {
try {
Matter.Composite.remove(this.getWorld(), destroyBody);
} catch(err) {
console.log(err)
}
}
If you still have same problem , we can adapt also with flag PREVENT_DOUBLE_BY_1_SECOUND for example.

What is "onkeyup" in gamepad api?

I'm experimenting a little with gamepad api. When a button is pressed, it gets "fired" all the time until the button is released. This is cool for something like running or shooting in a game, but problematic, when I want to navigate inside a menu (e.g. going 16 menu items down instead of one).
On a normal keyboard, I would use onkeyup/onkeydown to see, when a button press has ended. But on gamepad api, I didn't find something like that.
JS:
var gamepadPressCounter = 0;
function update() {
const gamepads = navigator.getGamepads();
if (gamepads[0].buttons[0].pressed === true) {
gamepadPressCounter++;
}
window.requestAnimationFrame(update);
}
So, do you know a way to get the gamepadPressCounter only one up per press and not infinite times?
You have to check it against whether it was pressed last time you checked. It doesn't fire events, it is stateful. You are directly checking if the button is pressed or not.
Make a variable to store the state of the button last time you checked and compare it to see if it has changed from last time.
const anyButtonPressedChangeListener = {
listeners: [],
addListener(cb) {
this.listeners.push(cb);
},
signal(state) {
for (const cb of this.listeners) {
cb(state);
}
}
}
let buttonstate = false;
const loop = () => {
const gamepads = navigator.getGamepads();
const gamepad = gamepads[0];
if (gamepad) {
const statenow = gamepad.buttons.some(btn => btn.pressed);
if (buttonstate !== statenow) {
buttonstate = statenow;
anyButtonPressedChangeListener.signal(statenow);
}
} else {
console.log("Not connected");
}
setTimeout(loop, 1000);
}
anyButtonPressedChangeListener.addListener((buttonState) => {
console.log(`Any button pressed changed to ${buttonState}`);
});
loop();

Reset timeout on event with RxJS

I'm experimenting with RxJS (with the JQuery extension) and I'm trying to solve the following use case:
Given that I have two buttons (A & B) I'd like to print a message if a certain "secret combination" is clicked within a given timeframe. For example the "secret combination" could be to click "ABBABA" within 5 seconds. If the combination is not entered within 5 seconds a timeout message should be displayed. This is what I currently have:
var secretCombination = "ABBABA";
var buttonA = $("#button-a").clickAsObservable().map(function () { return "A"; });
var buttonB = $("#button-b").clickAsObservable().map(function () { return "B"; });
var bothButtons = Rx.Observable.merge(buttonA, buttonB);
var outputDiv = $("#output");
bothButtons.do(function (buttonName) {
outputDiv.append(buttonName);
}).bufferWithTimeOrCount(5000, 6).map(function (combination) {
return combination.reduce(function (combination, buttonName) {
return combination + buttonName;
}, "");
}).map(function (combination) {
return combination === secretCombination;
}).subscribe(function (successfulCombination) {
if (successfulCombination) {
outputDiv.html("Combination unlocked!");
} else {
outputDiv.html("You're not fast enough, try again!");
}
});
While this works fairly well it's not exactly what I want. I need the bufferWithTimeOrCount to be reset when button A is pressed for the first time in a new timeframe. What I'm looking for is that as soon as the secret combination is pressed (ABBABA) I'd like "Combination unlocked!" to be shown (I don't want to wait for the time window to be expired).
Throttle is the typical operator for the delaying with reactive resetting effect you want.
Here's how you can use throttle in combination with scan to gather the combination inputted before the 5 seconds of silence:
var evaluationStream = bothButtons
.merge(bothButtons.throttle(5000).map(function(){return "reset";})) // (2) and (3)
.scan(function(acc, x) { // (1)
if (x === "reset") return "";
var newAcc = acc + x;
if (newAcc.length > secretCombination.length) {
return newAcc.substr(newAcc.length - secretCombination.length);
}
else {
return newAcc;
}
})
.map(function(combination) {
return combination === secretCombination;
});
var wrongStream = evaluationStream
.throttle(5000)
.filter(function(result) { return result === false; });
var correctStream = evaluationStream
.filter(function(result) { return result === true; });
wrongStream.subscribe(function() {
outputDiv.html("Too slow or wrong!");
});
correctStream.subscribe(function() {
outputDiv.html("Combination unlocked!");
});
(1) We scan to concatenate the input characters. (2) Throttle waits for 5 seconds of event silence and emits the last event before that silence. In other words, it's similar to delay, except it resets the inner timer when a new event is seen on the source Observable. We need to reset the scan's concatenation (1), so we just map the same throttled Observable to "reset" flags (3), which the scan will interpret as clearing the accumulator (acc).
And here's a JSFiddle.

progressive konami code

I am trying to create a .js file for a website that upon entering the konami code Up, Up, Down, Down, Left, Right, Left, Right, B, A, Start(enter) it will embed a video.
However while entering the right keys the webpage should display something like "keep going", if a wrong key is entered it should display "wrong, try again", and allow them to start over.
I've manged to get the JavaScript working where upon entering the right code it displays an alert, and entering the wrong code displays a different code.
i've manged to get this much code using online resources but none of them explain how to get wrong, try again part
if (window.addEventListener) {
var keys = [],
konami = "38,38,40,40,37,39,37,39,66,65,13";
window.addEventListener("keydown", function(e){
keys.push(e.keyCode);
if (keys.toString().indexOf(konami) >= 0)
{
alert('Right');
keys = [];
};
if (keys.toString().indexOf(konami) < 0)
{
alert('Wrong');
keys = [];
}
}, true);
};
Any help would be greatly appreciated.
if (window.addEventListener) {
var index = 0;
var konami = [38,38,40,40,37,39,37,39,66,65,13];
window.addEventListener("keydown", function(e){
if (e.keyCode === konami[index])
{
index++; //valid key at the valid point
if (index == konami.length)
{
alert("Correct");
} else {
alert("Keep going");
}
} else {
// incorrect code restart
index = 0;
alert("Wrong");
}
});
}
You could do something like
if (window.addEventListener) {
var keys = [],
konami = "38,38,40,40,37,39,37,39,66,65,13".split(',');
window.addEventListener("keydown", function(e){
keys.push(e.keyCode);
console.log(e.keyCode);
var lengthOfKeys = keys.length -1;
if (konami[lengthOfKeys] == keys[lengthOfKeys])
{
alert('Right');
if(konami.length === keys.length){
alert('complete!');
}
}else{
alert('Wrong');
keys = [];
}
}, true);
};
fiddle here http://jsfiddle.net/b6kuZ/
This works for me:
if (window.addEventListener) {
var keys = [],
konami = "38,38,40,40,37,39,37,39,66,65,13";
konami_arr = konami.split(',');
window.addEventListener("keydown", function(e){
keys.push(e.keyCode);
var position = keys.length-1;
if(keys[position ] != konami_arr[position])
{
alert('Wrong');
keys = [];
}
else if (keys.join(',') == konami)
{
alert('Right');
keys = [];
};
}, true);
}
​jsFiddle exmaple
Having an alert come up every time a key is hit is very jarring. Instead, why mot have the validation of correct answers show up in a DIV, and only use an alert when the answer is incorrect.
function checker(){
if (kc==11){
kc=0; // This resets the sequence.
// The function for what you want to occur goes here.
}
}
function keyUp(e) {
var keynum;
if (window.event){keynum = event.keyCode;}
else if (e.which){keynum = e.which;}
for (i=0;i<222;i++){
// The 222 represents all the keys on the keyboard.
var kx=konamicode[kc]; // kx represents the current position in the code sequence.
var res=document.getElementById('response');
var dumb=wrong[kc];
if (keynum==i){
// Checks to see if key matches sequence, and resets sequence if it doesn't.
if (i!=kx){
res.innerHTML='';
alert(dumb); // Reprimands user, and resets the sequence.
kc=0;
}
else {
res.innerHTML=right[kc]; // Congratulates user, and advances the sequence.
kc++;
}
}
}
checker();
}
document.onkeyup = keyUp;
In the body of the page you will need to put a DIV to display that key strokes were validated as correct.
<div id="response"></div>

Categories

Resources