JavaScript move delay and multiple keystrokes - javascript

Here is my problem: http://testepi.kvalitne.cz/test/
I do not want the delay between a keypress and the movement of the square. I would also like to know how to move diagonally (pressing two keys at same time).
My code:
$(function(){
document.addEventListener("keydown", move, false);
var x = 0;
var y = 0;
function move(event){
if(event.keyCode==37){
x -= 10;
$("#square").css("left", x);
}
if(event.keyCode==39){
x += 10;
$("#square").css("left", x);
}
if(event.keyCode==38){
y -= 10;
$("#square").css("top", y);
}
if(event.keyCode==40){
y += 10;
$("#square").css("top", y);
}
}
});

First, to avoid the keypress/repeat delay, you have to wrap your program in a loop, and make the state of the keyboard available inside the scope of that loop, secondly to monitor multiple keypresses you need to keep track of individual keydown and keyup events:
var x = 0;
var y = 0;
// store the current pressed keys in an array
var keys = [];
// if the pressed key is 38 (w) then keys[38] will be true
// keys [38] will remain true untill the key is released (below)
// the same is true for any other key, we can now detect multiple
// keypresses
$(document).keydown(function (e) {
keys[e.keyCode] = true;
});
$(document).keyup(function (e) {
delete keys[e.keyCode];
});
// we use this function to call our mainLoop function every 200ms
// so we are not relying on the key events to move our square
setInterval( mainLoop , 200 );
function mainLoop() {
// here we query the array for each keyCode and execute the required actions
if(keys[37]){
x -= 10;
$("#square").css("left", x);
}
if(keys[39]){
x += 10;
$("#square").css("left", x);
}
if(keys[38]){
y -= 10;
$("#square").css("top", y);
}
if(keys[40]){
y += 10;
$("#square").css("top", y);
}
}

If you are trying to implement game-like 2d sprite movement, I suggest you have a notion of x and y velocity, rather than moving the sprite a fixed amount on keypress.
So on keypress, add or subtract from x or y velocity.
var xvel = 0,
yvel = 0,
x = 0,
y = 0;
setInterval(function () {
y += yvel;
x += xvel;
$("#square").css("left", x);
$("#square").css("top", y);
}, 16); //run an update every 16 millis
document.addEventListener("keydown", move, false);
function move(event){
if(event.keyCode==37){
xvel -= 10;
}
if(event.keyCode==39){
xvel += 10;
}
if(event.keyCode==38){
yvel -= 10;
}
if(event.keyCode==40){
yvel += 10;
}
}
You would also need to worry about a keyup event, however, as the velocity would stay until you cancelled it out.
You can use setInterval to update the position of the sprite every x milliseconds. (Game tick)
To move diagonally, just add/subtract from the velocity of both x and y simultaneously.
This is just an example, but there are more examples out there on sprite movement.

have you looked here? you should be able to do diagonal moving by checking if multiple keys have been pressed down without being released.

Related

How to tell on which part of the screen divided to N rectangles am I (with mousemove)

I need to divide screen to 20 parts (horizontally) and set the value of mouse position from 1 to 20 to update sprite background-image position (for a smooth rotation animation). The code below is working, but there is a problem, when I move mouse very fast - than it can skip a few points, and I need to always change the number by one step. How can I achieve that?
https://codepen.io/kgalka/pen/vbpoqe
var frames = 20;
var frameWidth = Math.round(window.innerWidth / frames);
var xIndex;
function updatePosition(x) {
if (xIndex != x) {
xIndex = x;
document.getElementById('val').innerText = xIndex;
}
}
document.onmousemove = function(e) {
updatePosition(Math.round(e.clientX / frameWidth));
}
Ok i saw th example and i think that i understand the problem and here is how i would fix this.
Have a look and let me know if it work.
var frames = 20;
var frameWidth = Math.round(window.innerWidth / frames);
var xIndex;
var timeout;
function updatePosition(x) {
if (xIndex != x) {
xIndex = x;
document.getElementById('val').innerText = xIndex;
}
}
document.onmousemove = function(e) {
// clear the prev call if the mouse was to fast.
clearTimeout(timeout);
// Now ini new call to updatePosition
timeout= setTimeout(()=> updatePosition(Math.round(e.clientX / frameWidth)), 1 )
// you could play with the timeout and increase it from 1 to 100ms. and see what you prefere
}
<p id="val"></p>

Phaser P2 Sprite Scale Change not working properly for all group children

I hope this code explains what I'm trying to do. I have a pool table, and I want the balls to accelerate into the pockets if they are close enough. At this point I'm not yet checking the distance, just working to figure out how to do it.
I'm sure there is a better way!
balls.forEachAlive(
pockets.forEachAlive( moveBallTowardPocket, this), this);
Update: The following code is working except one thing, the scale change for balls on the first five pockets. Acceleration is working for all balls to all pockets. Scale change is only working on the last pocket, not the first five.
function update() {
pockets.forEachAlive(function(pocket) {
accelerateBallToPocket(flipper, pocket, 60);
balls.forEachAlive(function(ball) {
accelerateBallToPocket(ball, pocket, 60);
});
});
//...
}
function accelerateBallToPocket(ball, pocket, speed) {
if (typeof speed === 'undefined') {
var speed = 120;
}
var pocket_body_x = pocket.body.x;
var pocket_body_y = pocket.body.y;
var ball_body_x = ball.body.x;
var ball_body_y = ball.body.y;
// move ball toward pocket if close enough
var dx = ball_body_x - pocket_body_x; //distance ship X to enemy X
var dy = ball_body_y - pocket_body_y; //distance ship Y to enemy Y
var dist = Math.sqrt(dx*dx + dy*dy); //pythagoras
if (dist < pocket_radius * pocket_leniency_factor) {
// accelerate ball to pocket on right angle
var angle = Math.atan2(pocket.y - ball.y,
pocket.x - ball.x);
ball.body.rotation = angle + game.math.degToRad(90);
ball.body.force.x = Math.cos(angle) * speed;
ball.body.force.y = Math.sin(angle) * speed;
// change scale
// FIXME only works on the last pocket lower right
if (ball === flipper) {
ball.scale.setTo(Math.tan(pocket.x - ball.x),
Math.tan(pocket.y - ball.y));
} else {
ball.scale.setTo(Math.sin(pocket.x - ball.x),
Math.cos(pocket.y - ball.y));
}
} else {
// reset the scale when the ball is out of range of the pocket
ball.scale.setTo(1.0, 1.0);
}
}
2nd Update:
The following, based on the solution, has me going in the right direction again, I think...
for (var i = 0; i < pockets.children.length; i++) {
accelerateBallToPocket(cue, pockets.children[i], 60);
if (cue.pocketing) break;
}
for (var i = 0; i < balls.children.length; i++) {
if (balls.children[i].pocketing) continue;
for (var j = 0; j < pockets.children.length; j++) {
accelerateBallToPocket(balls.children[i], pockets.children[j], 60);
if (balls.children[i].pocketing) return;
}
}
Ok, the problem is that you set the scale to 1 if the ball isn't close to a pocket. And, as you check each ball against each pocket, there will always be one pocket (that is checked later in the loop) that the ball is not close too, except the last pocket in the pocket list. So, even if the ball scale is set to the correct value, it will be reset when the next pocket is being checked.
What you can do is check whether a ball is close to at least one pocket, if it is then it can't be close the the other pockets so you don't check again agaist the other pockets.
// Consider that every ball is not inside a pocket
balls.forEachAlive(function(ball) {
ball.inPocket = false;
});
flipper.inPocket = false; // You should really add the flipper to the balls group to remove duplicate code
pockets.forEachAlive(function(pocket) {
if(!flipper.inPocket) accelerateBallToPocket(flipper, pocket, 60);
balls.forEachAlive(function(ball) {
if(!ball.inPocket) accelerateBallToPocket(ball, pocket, 60);
});
});
Then, in your move function you have to set the inPocket member to true if a ball is close to the pocket.
function accelerateBallToPocket(ball, pocket, speed) {
...
if (ball === flipper) {
ball.scale.setTo(Math.tan(pocket.x - ball.x),
Math.tan(pocket.y - ball.y));
ball.inPocket = true;
} else {
ball.scale.setTo(Math.sin(pocket.x - ball.x),
Math.cos(pocket.y - ball.y));
ball.inPocket = true;
}
} else {
// reset the scale when the ball is out of range of the pocket
ball.scale.setTo(1.0, 1.0);
}
}
And alternative would be to revers the loop order, first iterate through all balls and for each ball check each pocket, once you find that is in a pocket continue the outer loop (skipping the check for the other pockets). In order to do this your accelerateBall function should return true or false, being true when the ball is close enough to the pocket and false otherwise.
I would re-write your iterations like this:
for (var i = 0; i < pockets.children.length; i++) {
accelerateBallToPocket(cue, pockets.children[i], 60);
if (cue.pocketing) break;
}
// Stumped...
for (var i = 0; i < balls.children.length; i++) {
// No need for the check here, each ball should have pocketing=false, set at the top of the update loop
// This means, that balls.children[i].pocketing will always be false here
for (var j = 0; j < pockets.children.length; j++) {
accelerateBallToPocket(balls.children[i], pockets.children[j], 60);
if (balls.children[i].pocketing) break; // stop checking the rest of the pockets for this ball
}
}

How to get a collision function to work properly in Javascript?

I'm pretty close to finish this program using Canvas. This program is simply a ball that falls down from top to bottom and there's a basket that catches it, that is it. However, I have the following issues.
1) When I press the left or right arrows from keyboard for more than couple of times somehow the basket will go all the way to either left or right and disappear.
2) When the ball hits the basket nothing happens (my Collision detection function doesn't work properly). However, I should say that my collision detection works just fine when the balls hits the ground (alert message shows up saying "Ball hit the ground").
Is there a way to show a message on the top of the canvas like "1 point" every time the basket catches a ball ( if there are 5 balls then I should get a message to say "5 points")
Can someone tell me what I am doing wrong please? Thank you so much in advance!!
LIVE CODE HERE
http://codepen.io/HenryGranados/pen/QNOZRa
Here's my code :
//create the constructor for the class pad
function Pad() {
//initialisation code will go here
//create private variables for the x and y coordinates
var x = 200,
y = 200,
vx = 0,
vy = 0,
padX = (canvas.width - 20) / 2;
rightPressed = false,
leftPressed = false;
//create the draw function to give us the draw method
//it accepts one parameter which is the context from the canvas it is drawn on
Pad.prototype.draw = function (context) {
//save the state of the drawing context before we change it
context.save();
//set the coordinates of the drawing area of the new shape to x and y
context.translate(x, y);
//start the line (path)
context.beginPath();
context.fillStyle = "#800000"; // This is the basket
context.moveTo(15, 20);
context.bezierCurveTo(20, 100, 150, 100, 150, 20);
//close the path
context.closePath();
context.fill();
//go ahead and draw the line
context.stroke();
//restore the state of the context to what it was before our drawing
context.restore();
}
//create a public property called X (note caps!)
Object.defineProperty(this, 'X',
{
//getter
get: function () {
//return the value of x (lower case)
return x;
},
//setter
set: function (value) {
//ste the value of x (lower case)
x = value;
}
}
)
//create a public property called Y (note caps!)
Object.defineProperty(this, 'Y',
{
//getter
get: function () {
//return the value of y (lower case)
return y;
},
//setter
set: function (value) {
//ste the value of y (lower case)
y = value;
}
}
)
padX = function () {
if (rightPressed && padX < canvas.width - 20) {
padX += 5;
}
else if (leftPressed && padX > 0) {
padX -= 5;
}
}
Pad.prototype.move = function () {
//change the x axis by the x velocity
x += vx;
//change the y axis by the y velocity
y += vy;
}
Pad.prototype.setVector = function (vector) {
//set the vx value based on this vector
vx = vector.VX;
//set the vy value based on this vector
vy = vector.VY;
}
//public method to set the vector of the saucer
Pad.prototype.accelerate = function (Acceleration) {
//set vx
vx += Acceleration.AX;
////set vy
//vy += Acceleration.AY;
}
//create a public property called Top
Object.defineProperty(this, 'Top',
{
//getter
get: function () {
//return the y posn less the height
return y - 10;
}
}
)
//create a public property called Bottom
Object.defineProperty(this, 'Bottom',
{
//getter
get: function () {
//return the y posn plus the height
return y + 10;
}
}
)
//create a public property called Left
Object.defineProperty(this, 'Left',
{
//getter
get: function () {
//return the x posn less the width
return x - 80;
}
}
)
//create a public property called Right
Object.defineProperty(this, 'Right',
{
//getter
get: function () {
//return the x posn plus the width
return x + 80;
}
}
)
}
(1) There are at least two options to solve this problem
in your Pad.move function you could limit the change of x. You change it only when its within canvas width:
Pad.prototype.move = function() {
//change the x axis by the x velocity
var canvasWidth = 400,
padWidth = 150;
if (x + vx < canvasWidth - padWidth && x + vx >= 0)
x += vx;
//change the y axis by the y velocity
y += vy;
}
or similarly as you create ground you could create walls on both sides and collide pad with them.
(2) There is no collision handling between ball and pad:
place it in function drawFrame():
if (collision.Overlapping(ball, pad)) {
context.strokeText('ball hit pad!',20,100)
//..do some other stuff here
}
(3)Which brings us to showing message on canvas, you can just draw text on canvas
var ctx = canvas.getContext("2d");
ctx.font = "30px Arial";
ctx.fillText("Hello World",10,50);
Demo: http://codepen.io/anon/pen/RaxwLp?editors=1011
Pad was blocked because when key is pressed acceleration is always increased, so in order to move in opposite direction first it must go to 0 which takes quite some time. I have added keyup event and when key is released acceleration is zeroed:
if(leftPressed){
acceleraton.HThrust(.01);
}else if(rightPressed){
acceleraton.HThrust(-.01);
}else{
acceleraton.Halt();
}

Continue keydown event even when an additional button is pressed

Let's say we have a ball that can be moved left and right around the screen. When you click space, the ball should jump.
I got the ball to move left and right in a canvas. However, when the ball is moving left (for example) and I hit the space bar before anything, the ball stops moving left.
Check out my example so far!
I am using the KeyboardJS library to handle my key events:
KeyboardJS.on("left", function () {
cc();
x -= xv;
if (x < r) {
circle(x + width, y, r);
if (x + width <= width - r) {
x = x + width;
}
}
circle(x, y, r);
});
KeyboardJS.on("space", null, function () {
console.log("space!");
});
How could I get this behavior to stop so that when the space bar is hit, the ball jumps up but at the same time still moves to the left?
One thought added to everyone else's good ideas:
Separate your user input from your drawing.
Keyboarding:
If you’re having problems with KeyboardJS, check out Keydrown: http://jeremyckahn.github.io/keydrown/
Don’t do any drawing when capturing keys…just capture the user’s input of which direction they want the circle to go.
Set up an “direction” variable to hold how many times the user has pressed [left] or [right]:
var direction=0;
When [left] is pressed:
direction--;
When [right] is pressed:
direction++;
Direction is a net number. So if the user holds down the left key for 20 strokes and the right key for 15 strokes, direction will be -5 ( -20 + 15 ).
Set up an “altitude” variable to hold how many times the user has pressed [space]:
var altitude=0;
When [space] is pressed:
altitude-=10;
Altitude is a net number also.
Drawing:
Do all your drawing in a separate animation loop. Rather than using javascript’s setInterval, use the new and improved way of creating an imation loop -- requestAnimationFrame.
// set the starting circle positions
var currentX=canvas.width;
var currentY=canvas.height-r;
function animate(){
// even as we're executing this current animation loop
// request another loop for next time
requestAnimationFrame(animate);
// change the currentX position by the accumulated direction
currentX+=direction;
direction=0;
// change the currentY position by the accumulated altitude
currentY+=altitude;
altitude=0;
// draw the circle at its current position
cc();
circle(currentX,currentY,r);
// apply gravity to the circle
// to make it fall if its in the air
if(currentY<canvas.height-r){
currentY++;
}
}
Good Luck with your project!
What the problem is, is that if you press another key after pressing the first key, it will trigger that event, and stop triggering the other keydown event. This can be seen in this simplified example:
addEventListener('keydown',function(e) {console.log(e.keyCode, e.keyIdentifier)});
If you run that script, and then press left and then up, it will first show 37 Left a bunch of times, and then it'll show 32 U+0020 once and stop logging the left keydowns.
This is simply how the browser (and most other basic programs too) work. You can try doing the same thing in for example notepad, if you press the A key first, and then press space, it'll stop adding more As. This also means that you can't rely on key events (or key event libraries) to do this for you.
What you could do though, is make a global object that holds all keys that are pressed. For example:
window.KeysDown = {
37: false, //Left
39: false, //Right
38: false, //Up
40: false, //Down
32: false, //Space
};
addEventListener('keydown', function(e) {
var keyCode = e.keyCode||e.charCode||e.which;
if (keyCode == 32 && !window.KeysDown[keyCode])
onSpace();//This will only run when the spacebar is first pressed down.
if (window.KeysDown.hasOwnProperty(keyCode))
window.KeysDown[keyCode] = true;
});
addEventListener('keyup', function(e) {
var keyCode = e.keyCode||e.charCode||e.which;
if (window.KeysDown.hasOwnProperty(keyCode)) window.KeysDown[keyCode] = false;
});
var interval = setInterval(function() {
for (var i in window.KeysDown) {
if (window.KeysDown[i]) switch (i+'') {
case '37': onLeft(); break;
//case '38': window.KeysDown[i] && onUp(); break;
case '39': onRight(); break;
//case '40': window.KeysDown[i] && onDown(); break;
}
}
}, 50);
The syntax window.KeysDown[i] && onLeft() causes the onLeft function only to run if window.KeysDown[i] is true. I hope this solution works for you.
EDIT: I've changed the code to the working one. I've also made a JSFiddle that demonstrates this. The problem in my previous code was that apparently a switch doesn't handle integer values well, so I needed to convert i to a string.
EDIT: I've also added an extra part to the script that makes the onSpace function only run when the spacebar is first pressed down, and so that it won't run again until the spacebar is released and pressed again. I've also updated my JSFiddle to include these changes.
I would create a main function, which runs at a regular interval, and each time it runs, it updates the position of the circle based on what keys are currently down.
The main function can be done like this:
var keyDown = {};
function mainLoop() {
cc();
//pseudo code
if (keyDown["left"]) {
x -= 5;
}
if(keyDown["space"]) {
y -= 10;
}
// redraw circle at new location
circle(x,y,r);
}
setInterval(mainLoop, 30) //sets the function to be called every 30 milliseconds
// key event handler, first function handles the keydown, second function handles keyup
KeyboardJS.on("left", function() {
keyDown["left"] = true;
}, function() {
keyDown["left"] = false;
});
With this example, if the user had the left arrow key and space bar pressed when the mainLoop function ran, then the circle would move to the left 5 pixels, and up 10 pixels.
jsFiddle Demo
You are going to have to manually create a framework for this. KeyboardJS just wasn't cutting it. I guess I kind of set that up here. It uses an Action "class" coupled with key event triggers.
Action "class"
function Actions(){
this.count = 0;
this.running = {};
this.interval = undefined;
}
Actions.prototype.start = function(action){
if( typeof(this.running[action]) == "undefined"){
this[action]();
this.running[action] = action;
this.count++;
}
var me = this;
if( typeof(this.interval) == "undefined"){
this.interval = setInterval(function(){
for( var act in me.running ){
me[act]();
}
},50);
}
};
Actions.prototype.stop = function(action){
this.running[action] = void 0;
delete this.running[action];
this.count--;
if( this.count == 0 ){
clearInterval(this.interval);
this.interval = void 0;
};
};
Actions.prototype.left = function(){
cc();
x -= xv;
if (x < r) {
circle(x + width, y, r);
if (x + width <= width - r) {
x = x + width;
}
}
circle(x, y, r);
};
Actions.prototype.right = function(){
cc();
x += xv;
if (x >= width - r) {
circle((x - r) - (width - r), y, r);
if ((x - r) - (width - r) > r) {
x = (x - r) - (width - r);
}
}
circle(x, y, r);
};
Actions.prototype.space = function(){
cc();
y -= yv;
circle(x, y, r);
};
key event triggers
document.onkeydown = checkKeyDown;
function checkKeyDown(e) {
e = e || window.event;
if (e.keyCode == '37') {
// left arrow
actions.start("left");
}
if (e.keyCode == '39') {
// right arrow
actions.start("right");
}
if (e.keyCode == '32') {
// space bar
actions.start("space");
}
}
document.onkeyup = checkKeyUp;
function checkKeyUp(e) {
e = e || window.event;
if (e.keyCode == '37') {
// left arrow
actions.stop("left");
}
if (e.keyCode == '39') {
// right arrow
actions.stop("right");
}
if (e.keyCode == '32') {
// space bar
actions.stop("space");
}
}

Change animation frames to only when tank moves

I've rendered a tile map and a tank on a screen in canvas:
http://www.exeneva.com/html5/movingTankExample/
However, you'll notice that the tank's animation movement (moving tracks) are regularly occurring. How would you change it so that the movement of the tank tracks occurs only when the tank is moved? Note that there is no physics at the moment.
your startUp function is calling drawScreen every 100 ms where the tank movement gets animated. You need to extract the animation logic from drawScreen into its own function, e.g. animateMovement and call it from your onkeydown handler.
Something like:
function animateMovement(){
var int = setInterval(function(){
// Tank tiles
var tankSourceX = Math.floor(animationFrames[frameIndex] % tilesPerRow) * tileWidth;
var tankSourceY = Math.floor(animationFrames[frameIndex] / tilesPerRow) * tileHeight;
// Draw the tank
context.drawImage(tileSheet, tankSourceX, tankSourceY, tileWidth, tileHeight, tankX, tankY, tileWidth, tileHeight);
// Animation frames
frameIndex += 1;
if (frameIndex == animationFrames.length) {
frameIndex = 0;
}
},100);
setTimeout(function(){clearInterval(int);}, 1000);
}
Put this just before your drawScreen function and call it from your document.onkeydown handler just after you call drawScreen. Obviously, you will also need to remove the animation code from your drawScreen function:
function drawScreen() {
// Tile map
for (var rowCtr = 0; rowCtr < mapRows; rowCtr += 1) {
for (var colCtr = 0; colCtr < mapCols; colCtr += 1) {
var tileId = tileMap[rowCtr][colCtr] + mapIndexOffset;
var sourceX = Math.floor(tileId % tilesPerRow) * tileWidth;
var sourceY = Math.floor(tileId / tilesPerRow) * tileHeight;
context.drawImage(tileSheet, sourceX, sourceY, tileWidth,
tileHeight, colCtr * tileWidth, rowCtr * tileHeight, tileWidth, tileHeight);
}
}
/*tank animation was here*/
}
​
You will have to implement a basic state machine that controls the atcual state of the tank.
Eg.
On the STOPPED state, the tank doesn't animate and it starts with this state;
When you press a key, you toggle the state to MOVING, so the animation function will use this flag to know when to animate your sprite;
When you release a key, you toggle the state back to STOPPED.
Take a look at this link (mostly the second part for the real action, the first part is more theorical): http://active.tutsplus.com/tutorials/actionscript/the-power-of-finite-state-machines-concept-and-creation/
It's about Flash, but the concept is universal.

Categories