I'm trying to learn object oriented programming in javascript so I try to make a simple game. I would like to make a character that moves. There is the code in js:
function move(event)
{
var k=event.keyCode;
var chr = {
updown : function (){
var y=0;
if (k==38)
{--y;
}else if (k==40)
{++y;}
return y;
},
leftright : function (){
var x=0;
if (k==37)
{--x;
}else if (k==39)
{++x;}
return x;
}
};
chrId.style.top = (chr.updown())+"px";
chrId.style.left = (chr.leftright())+"px";
}
html:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="jumpOPP.css">
<script src="jumpOPP.js"></script>
</head>
<body onkeydown="move(event)">
<img id="chrId" src="TrackingDot.png" >
</body>
</html>
and CSS:
#chrId {
position: relative;
top: 0px;
left: 0px;
}
When I press and hold up, down, left, right the dot moves only for a one place. How to make it moving whole time I' m holding some key. I have made it without var char to move. I used function move(event) and then a switch, cases 38, 37, 39 and 40 and then it change style.top but I can't make it in one object.
Is it possible to make a object chr = {objekt movement, life, power...} and then a object ground = {some code that stops the chr} and other interacting objects ? Can somebody recomend a good tutorial for that? :)
Thank you
Here working jsfiddle - http://jsfiddle.net/t5ya4j26/
You error in define local variables in scopes that always will equal to 0. So for fix that, you must get current left and top of element, not define x = 0 and y = 0.
function move(event) {
var k = event.keyCode,
chrId = document.getElementById('test'),
chr = {
updown: function () {
var y = parseInt(getComputedStyle(chrId).top);
if (k == 38) {
--y;
} else if (k == 40) {
++y;
}
return y;
},
leftright: function () {
var x = parseInt(getComputedStyle(chrId).left);
if (k == 37) {
--x;
} else if (k == 39) {
++x;
}
return x;
}
};
chrId.style.top = (chr.updown()) + "px";
chrId.style.left = (chr.leftright()) + "px";
}
document.addEventListener('keydown', move);
I would recommend that you use the <canvas> element for stuff like this. But use window.setInterval(function, milliseconds) to have it repeatedly run your 'move' function and then when a key is released, window.onkeyup clear that interval.
clearInterval(intervalName);
This requires you to make a new event listener. Instead of having your event listener in body, use:
window.onkeydown = function(event) {
var k = event.which || event.keyCode; // This adds compatibilty across all browsers
// Code to be run
}
I know that you are looking for the function in an object, but moving an element is really quick and easy with this, I just made this today for my beginners game:
var change = (parseInt(chrId.style.left.replace('%',''),10) + 3).toString() + "%"
chrId.style.left = change
The % signs can be replaced with 'px' if you are using pixel values to move, and the ' + 3 ' is how many pixels or percentage points you want your element to move per execution.
The same can be done for up by changing the 'left' to 'top'.
My code might not be to your liking, but I'm just trying to demonstrate how I work around this problem, I am positively sure that there are hundreds of better ways, but this one seems to save me a lot of trouble for a lot of other stuff.
Hope I was able understand the question and help though, sorry if I couldn't :)
<!DOCTYPE html>
<html>
<head>
<meta charset = "utf-8">
<title> MOVEMENT </title>
</head>
<body>
<script type = "text/javascript">
//------------------------------------------------------------------------------
// VARIABLES are set here so they're GLOBAL (everything may access them)
//------------------------------------------------------------------------------
let lock_left = true
let lock_top = true
let lock_right = true
let lock_bottom = true
//------------------------------------------------------------------------------
let html; let htmls
let body; let bodys
let avatar; let avatars
//------------------------------------------------------------------------------
let avatar_x = 0
let avatar_y = 0
//------------------------------------------------------------------------------
// EVERY map will be an object, and every object needs a CREATE function that
// will happen only ONCE and an UPDATE function that will repeat itself
//------------------------------------------------------------------------------
const map_main =
{
create: function()
{
html = document.querySelector( "html" ); htmls = html.style
body = document.querySelector( "body" ); bodys = body.style
},
//--------------------------------------------------------------------------
update: function()
{
htmls.width = "100%"
htmls.height = "100%"
htmls.margin = "0"
bodys.width = "100%"
bodys.height = "100%"
bodys.margin = "0"
bodys.backgroundColor = "rgb( 120, 200, 80 )"
},
}
//------------------------------------------------------------------------------
const map_avatar =
{
create: function()
{
avatar = document.createElement( "div" ); avatars = avatar.style
body.appendChild( avatar )
},
//--------------------------------------------------------------------------
update: function()
{
avatars.width = "64px"
avatars.height = "64px"
avatars.backgroundColor = "rgb( 200, 80, 120 )"
avatars.position = "absolute"
avatars.top = avatar_y + "px"
avatars.left = avatar_x + "px"
},
}
//------------------------------------------------------------------------------
// BELOW are the 2 main gears of the engine
//------------------------------------------------------------------------------
// EVERY code that only needs to happen once is called here
const master_create = function()
{
map_main.create()
map_avatar.create()
}
//------------------------------------------------------------------------------
// EVERYTHING that needs constant updates is called here
const master_update = function()
{
map_main.update()
map_avatar.update()
movement()
window.requestAnimationFrame( master_update )
}
//------------------------------------------------------------------------------
// BELOW is showing how the keyboard affects the locks
//------------------------------------------------------------------------------
const press = function( pressed )
{
if( pressed.keyCode === 37 || pressed.keyCode === 69 ) lock_left = false
if( pressed.keyCode === 38 || pressed.keyCode === 82 ) lock_top = false
if( pressed.keyCode === 39 || pressed.keyCode === 70 ) lock_right = false
if( pressed.keyCode === 40 || pressed.keyCode === 68 ) lock_bottom = false
}
//------------------------------------------------------------------------------
const release = function( released )
{
if( released.keyCode === 37 || released.keyCode === 69 ) lock_left = true
if( released.keyCode === 38 || released.keyCode === 82 ) lock_top = true
if( released.keyCode === 39 || released.keyCode === 70 ) lock_right = true
if( released.keyCode === 40 || released.keyCode === 68 ) lock_bottom = true
}
//------------------------------------------------------------------------------
// BELOW will check the LOCKS and use them to change AVATAR_X and AVATAR_Y
//------------------------------------------------------------------------------
const movement = function()
{
if( lock_left === false ) avatar_x -= 10
if( lock_top === false ) avatar_y -= 10
if( lock_right === false ) avatar_x += 10
if( lock_bottom === false ) avatar_y += 10
}
//------------------------------------------------------------------------------
// BELOW we call the 2 gears and everything will work
//------------------------------------------------------------------------------
master_create() // will be called only ONCE
master_update() // will repeat forever due to "window.requestAnimationFrame()"
//------------------------------------------------------------------------------
// LISTENERS should go after the engine starts rolling
//------------------------------------------------------------------------------
body.addEventListener( "keydown", press, false )
body.addEventListener( "keyup", release, false )
//------------------------------------------------------------------------------
</script>
</body>
</html>
Related
I have this code which makes a square move, but when i for example press the right arrow key then the left arrow key and release the right arrow key, the square pauses for a while and then just moves the left.
is there a way to get rid of this pause?
this is the code i have now:
let velocity = 10
let x = 375
let y = 225
setInterval(function(){
document.getElementById("square").style.left = x + "px";
document.getElementById("square").style.top = y + "px";
}, 1)
var direction = ""
function currentDirection(movingToDirection){
if(movingToDirection != direction){
return true
}
else {
return false
}
}
function Sup() {
if(currentDirection("Sup")){
direction = "Sup";
var Sloopup = setInterval(function(){
y -= velocity/10
}, 1)
window.Sloopup = Sloopup
}
}
function Sdown() {
if(currentDirection("Sdown")){
direction = "Sdown";
var Sloopdown = setInterval(function(){
y += velocity/10
}, 1)
window.Sloopdown = Sloopdown
}
}
function Sleft() {
if(currentDirection("Sleft")){
direction = "Sleft";
var Sloopleft = setInterval(function(){
x -= velocity/10
}, 1)
window.Sloopleft = Sloopleft
}
}
function Sright() {
if(currentDirection("Sright")){
direction = "Sright";
var Sloopright = setInterval(function(){
x += velocity/10
}, 1)
window.Sloopright = Sloopright
}
}
function Break(Function) {
direction = ""
if (Function = "Sup") {
clearInterval(window.Sloopup)
} if (Function = "Sdown") {
clearInterval(window.Sloopdown)
} if (Function = "Sleft") {
clearInterval(window.Sloopleft)
} if (Function = "Sright") {
clearInterval(window.Sloopright)
}
}
document.addEventListener("keydown", event => {
if(event.key==="ArrowUp") {Sup()}
if(event.key==="ArrowDown") {Sdown()}
if(event.key==="ArrowLeft") {Sleft()}
if(event.key==="ArrowRight") {Sright()}
})
document.addEventListener("keyup", event => {
if(event.key==="ArrowUp") {Break("Sup")}
if(event.key==="ArrowDown") {Break("Sdown")}
if(event.key==="ArrowLeft") {Break("Sleft")}
if(event.key==="ArrowRight") {Break("Sright")}
})
and I also have a online example:
Online example
Any help is very appreciated!
Try this:
var Keys = {
up: false,
down: false,
left: false,
right: false
};
let y = 10;
let x = 10;
document.addEventListener("keydown", (event) => {
if (event.key === "ArrowLeft") Keys.left = true;
else if (event.key === "ArrowUp") Keys.up = true;
else if (event.key === "ArrowRight") Keys.right = true;
else if (event.key === "ArrowDown") Keys.down = true;
});
document.addEventListener("keyup", (event) => {
if (event.key === "ArrowLeft") Keys.left = false;
else if (event.key === "ArrowUp") Keys.up = false;
else if (event.key === "ArrowRight") Keys.right = false;
else if (event.key === "ArrowDown") Keys.down = false;
});
setInterval(() => {
if (Keys.up) {
y -= 1;
} else if (Keys.down) {
y += 1;
}
if (Keys.left) {
x -= 1;
} else if (Keys.right) {
x += 1;
}
}, 1);
setInterval(() => {
document.getElementById("app").style.left = x + "px";
document.getElementById("app").style.top = y + "px";
}, 1);
There might be some delay when you clear and set a new interval. Here I set 2 interval that keep listening to keydown, which you will need to clear later when the game ends
Here's the sandbox
so below my suggestion if I understand well your problem:
document.addEventListener("keydown", event => {
// the issue occurs here, you have to disable all current keydown event
Break("Sup");Break("Sdown");Break("Sleft");Break("Sright");
if(event.key==="ArrowUp") {Sup()}
if(event.key==="ArrowDown") {Sdown()}
if(event.key==="ArrowLeft") {Sleft()}
if(event.key==="ArrowRight") {Sright()}
})
BTW i tested it & it works, i hope that it helps you.
Good luck!
as well as we separate content from style same concept applies in different layers of the engine you need to separate the logic from the engine as well. The idea is separation so you can implement/debug much easier. Example code below:
const velocity = {x:0, y:0}
const position = {x:375, y:225}
const keys = { ArrowLeft:false, ArrowRight:false, ArrowUp:false, ArrowDown:false }
function UpdatePlayerInput()
{
velocity.x = ((+keys.ArrowRight) - (+keys.ArrowLeft)) * 10;
velocity.y = ((+keys.ArrowDown) - (+keys.ArrowUp)) * 10;
}
function PyhsicsUpdate(){
position.x += velocity.x / 10;
position.y += velocity.y / 10;
}
function RenderUpdate() {
if (document.getElementById("square").style.left !== position.x + "px")
document.getElementById("square").style.left = position.x + "px";
if (document.getElementById("square").style.top !== position.y + "px")
document.getElementById("square").style.top = position.y + "px";
}
setInterval(PyhsicsUpdate, 1)
setInterval(RenderUpdate, 10)
document.addEventListener("keydown", event => {
keys[event.key] = true;
UpdatePlayerInput();
})
document.addEventListener("keyup", event => {
keys[event.key] = false;
UpdatePlayerInput();
})
Javascript is a heavily event-driven, often asynchronous language.
window.setTimeout(() => console.log(1), 0);
console.log(2);
The above will log 2, then 1, despite the timeout having been set to 0 milliseconds. setTimeout and setTimeout delay execution of the passed function until the first tick of the event loop, after the specified interval / timeout having been exceeded. It is not expected to be immediately invoked, even with a zero-value time argument.
is there a way to get rid of this pause?
Several approaches exist.
Whichever you end up choosing, the key adjustments you have to make is to (a) separate application state from input state and (b) only update input state as a direct result from event handlers, derive position and velocity on the following (regularly scheduled) view state update. Have one data structure hold the current state of the key(s) pressed and another positional arguments.
Prior to writing code, formalize the expected outcome. What is supposed to happen, if for instance both left and right are pressed? No acceleration in either direction, because inputs cancel? Bail early and save yourself the superfluous zero-sum arithmetic.
First (last) pressed takes precedence? Don't use an object with boolean values to represent pressed keys, use a Set (or array if unavailable) and add / remove (push / prune) to it, encode order by time of insertion.
// wrap the pure string representations of keys in a common data structure
const Key = {
DOWN: 'ArrowDown',
LEFT: 'ArrowLeft',
RIGHT: 'ArrowRight',
UP: 'ArrowUp'
}
// holds currently pressed keys
const inputState = new Set([])
function handleKeyDown(event) {
inputState.add(event.key)
}
function handleKeyUp(event) {
inputState.delete(event.key)
}
document.addEventListener('keydown', handleKeyDown)
document.addEventListener('keyup', handleKeyUp)
const viewState = {
velocity: {
x: 10,
y: 10
},
position: {
x: 375,
y: 225
}
}
const targetDomNode = document.getElementById('square')
function updateTargetDomNode(x, y) {
targetDomNode.style.left = `${x}px`
targetDomNode.style.top = `${y}px`
}
function renderViewState() {
updateTargetDomNode(
viewState.position.x,
viewState.position.y
)
}
// you could abstract more, obviously
function accelerate() {
viewState.velocity.x += inputState.has(Key.LEFT) ? -1 : 0
viewState.velocity.x += inputState.has(Key.RIGHT) ? 1 : 0
viewState.velocity.y += inputState.has(Key.UP) ? -1 : 0
viewState.velocity.y += inputState.has(Key.DOWN) ? 1 : 0
}
function updatePosition(timeSinceLastUpdate) {
viewState.position.x += viewState.velocity.x * timeSinceLastUpdate
viewState.position.y += viewState.velocity.y * timeSinceLastUpdate
}
const UPDATE_INTERVAL = 50
setInterval(() => {
accelerate()
// note, this will be slightly more than 50ms,
// if you care for accuracy, use a measurement,
// rather than a static value
updatePosition(UPDATE_INTERVAL)
renderViewState()
}, UPDATE_INTERVAL)
I am making a classic snake remake in JavaScript just for weekend fun, and I ran into this problem that if I press buttons quite fast - the snake (caterpillar in my case) is able to change direction to opposite and run into itself.
The way to recreate this situation is as follows:
direction is for example 'left'
press up(or down) and press right quickly after
now the caterpillar goes backwards. And my goal is it should make U-turn
I made the checks for opposite dir, but this doesn't prevent this
update = function() {
if (cat.direction != 'right' && key[37] === true) {
cat.direction = 'left';
}
if (cat.direction != 'left' && key[39] === true) {
cat.direction = 'right';
}
if (cat.direction != 'down' && key[38] === true) {
cat.direction = 'up';
}
if (cat.direction != 'up' && key[40] === true) {
cat.direction = 'down';
}
};
the full code
I was using normal addEventListener for the key listening, but I changed it for another approach (found somewhere), where I do update on keys very often and caterpillar moving is happening only from time to time, as I thought it might be a problem to directly associate drawing, changing direction and moving in the same time interval. I hope I am understandable, sorry if something isn't clear - I would be happy to provide more info if so.
One solution is to not process more than one key per move, but to allow more responsiveness you could implement a key buffer, instead of maintaining the key states as you do know. You would only collect arrow key presses in that buffer, and not push any repetitions of the same key into it.
Here are the relevant changes to the code:
Initialise the key buffer:
var keyBuffer = [];
Push arrow keys into the buffer when pressed:
var keyDown = function(e) {
var keyCode = e.which ? e.which : e.keyCode;
// *** Queue the arrow key presses
if (keyCode >= 37 && keyCode <= 40 &&
keyCode !== keyBuffer[keyBuffer.length-1] && ) {
keyBuffer.push(keyCode);
}
};
Process one key from the buffer at a time:
var update = function() {
// *** Extract next key from buffer, and only treat that one
// -- could be `undefined`, but that is not a problem:
var key = keyBuffer.shift();
if(cat.direction != 'right' && key === 37){
cat.direction = 'left';
} else if(cat.direction != 'left' && key === 39){
cat.direction = 'right';
} else if(cat.direction != 'down' && key === 38){
cat.direction = 'up';
} else if(cat.direction != 'up' && key === 40){
cat.direction = 'down';
}
};
Only process next key when about to move:
function loop() {
board.resetCanvas();
if(counter > 1000){
update(); // ***only process buffered keys when moving
cat.move();
counter = 0;
}
cat.draw();
counter += 5*cat.multiplier;
};
That's it. See fiddle below:
var canvas = document.getElementById("board");
var context = canvas.getContext("2d", {alpha:false});
var pieceSideLength = canvas.width / 40;
var key = [];
var keyBuffer = [];
window.addEventListener('keyup', function(e) {
this.keyUp.call(this, e);
}, false);
window.addEventListener('keydown', function(e) {
this.keyDown.call(this, e);
}, false);
function Piece(x,y){
this.x = x;
this.y = y;
}
board = {
leftBound: 0,
rightBound: canvas.width / pieceSideLength,
topBound: 0,
bottomBound: canvas.height / pieceSideLength,
drawPiece: function(x, y, color){
context.fillStyle = color;
context.fillRect(x*pieceSideLength,y*pieceSideLength,pieceSideLength,pieceSideLength);
context.strokeStyle = 'white';
context.strokeRect(x*pieceSideLength,y*pieceSideLength,pieceSideLength,pieceSideLength);
},
resetCanvas: function(){
context.clearRect(0,0,canvas.width,canvas.height);
}
};
//cat as for caterpillar
cat = {
x: canvas.width/pieceSideLength/2, //initial x
y: canvas.height/pieceSideLength/2, //initial y
pieces: [],
direction: 'up',
color: '#5da03c',
shouldGrow: false,
multiplier: 5,
init: function(){
cat.pieces.push(new Piece(this.x, this.y));
},
move: function(){
if(cat.pieces.length <= 10){
cat.shouldGrow = true;
}
var newX = cat.pieces[cat.pieces.length-1].x;
var newY = cat.pieces[cat.pieces.length-1].y;
if(cat.direction=='up'){
cat.makeNewHeadAt(newX,newY-1);
}
if(cat.direction=='down'){
cat.makeNewHeadAt(newX,newY+1);
}
if(cat.direction=='left'){
cat.makeNewHeadAt(newX-1,newY);
}
if(cat.direction=='right'){
cat.makeNewHeadAt(newX+1,newY);
}
cat.grow();
},
makeNewHeadAt: function(x,y){
cat.pieces.push(new Piece(x,y));
},
grow: function(){
if(cat.shouldGrow == false){
cat.pieces.shift();
} else {
cat.shouldGrow = false;
}
},
draw: function(){
for(i=0;i<cat.pieces.length;i++){
var p = cat.pieces[i];
board.drawPiece(p.x,p.y,cat.color);
}
}
};
cat.init();
update = function() {
// *** Extract next key from buffer, and only treat that one
// -- could be `undefined`, but that is not a problem:
var key = keyBuffer.shift();
if(cat.direction != 'right' && key === 37){
cat.direction = 'left';
} else if(cat.direction != 'left' && key === 39){
cat.direction = 'right';
} else if(cat.direction != 'down' && key === 38){
cat.direction = 'up';
} else if(cat.direction != 'up' && key === 40){
cat.direction = 'down';
}
};
keyUp = function(e) {
var keyCode = e.which ? e.which : e.keyCode;
this.key[keyCode] = false;
};
keyDown = function(e) {
var keyCode = e.which ? e.which : e.keyCode;
// *** Queue the key presses
if (keyCode >= 37 && keyCode <= 40 &&
keyCode !== keyBuffer[keyBuffer.length-1]) {
keyBuffer.push(keyCode);
}
this.key[keyCode] = true;
};
var counter = 0;
function loop() {
board.resetCanvas();
if(counter > 1000){
update(); // ***only process buffered keys when moving
cat.move();
counter = 0;
}
cat.draw();
counter += 5*cat.multiplier;
};
setInterval(loop, 1);
body { margin: 0px }
<div>
<canvas id="board" width="300" height="200" style="display: block; margin: 0 auto; background-color: #553300; border-style: solid; border-color: green;"></canvas>
</div>
Limiting the buffer size
You can limit the buffer size by replacing this:
keyBuffer.push(keyCode);
with:
keyBuffer = keyBuffer.slice(-2).concat(keyCode);
This will limit the size to 3. Adjust the slice argument as desired.
You can keep track of whether the snake has 'moved'. If you receive keyboard input, don't react to another keypress until the snake has moved. This way you're only allowing 1 key for each movement, so you can't change direction and run into yourself.
Modified example: link
update = function() {
if (moved = true) {
if(cat.direction != 'right' && key[37] === true){
and so forth
I am trying to move a sprite using arrow keys. Below is the logic that SHOULD cause the sprite to move diagonally if two keys are pressed. However, that is not the case. It will only move in one direction a a time. Since I am saving each keyCode as a Boolean within an object property whenever I press a button, I would think that this should not be an issue, but I can't get it to work. I have also tried using an array to store these Booleans, and checking those values instead. No luck.
I am at my wit's end and would greatly appreciate some help. I've scoured stackoverflow and many different blogs on building 2d games, but but nothing that I try works.
var keyState = {};
window.onkeydown = window.onkeyup = function(e) {
keyState[e.keyCode] = e.type == 'keydown';
// checks for up and left
if (keyState[38] && keyState[37]) {
player.pos.x -= 2;
player.pos.y -= 2;
}
// checks for up and right
else if (keyState[38] && keyState[39]) {
player.pos.x += 2;
player.pos.y += 2;
}
// checks for down and left
else if (keyState[40] && keyState [37]) {
player.pos.x -= 2;
player.pos.y -= 2;
}
// checks for down and right
else if(keyState[40] && keyState [39]) {
player.pos.x += 2;
player.pos.y -= 2;
}
// checks for up
else if (keyState[38]) {
player.animation.y = 64;
player.pos.y -= 2;
}
};
You should explicitly set the object key values as booleans and you should toggle them in separate keydown/keyup handlers:
let keysdown = {};
window.addEventListener('keydown', function(evt) {
keysdown[evt.which] = true;
if (keysdown["38"] === true && keysdown["37"] === true) {
console.log('up & left');
} else if (keysdown["38"] === true && keysdown["39"] === true) {
console.log('up & right');
} else if (keysdown["40"] === true && keysdown["37"] === true) {
console.log('down and left');
} else if (keysdown["40"] === true && keysdown["39"] === true) {
console.log('down and right');
} else if (keysdown["38"] === true) {
console.log('up');
}
}, false);
window.addEventListener('keyup', function(evt) {
keysdown[evt.which] = false;
}, false);
This logs all the correct key combinations for me.
You need to move the movement logic out of the event listener. Have the key event just log the current state of each key (up or down) then in the main loop check the state and move the play as needed.
As I am unsure if you want the movement to be constant or only on the press I have included the option to change the behaviour with the constant flag MOVE_ONLY_ON_PRESS if true movement only when a press is first detected. So that no key strokes are missed I clear the key flag in the main loop.
The following is an example of how to interface with the keyboard for a game.
// global key log;
var keyState = [];
const KEY_UP = 38;
const KEY_DOWN = 40;
const KEY_LEFT = 37;
const KEY_RIGHT = 39;
// Player
var player = {};
player.x = 100;
player.y = 100;
const MOVE_SPEED = 2;
const MOVE_ONLY_ON_PRESS = false; // This will toggle constant movement or only on press
// from your code
window.onkeydown = window.onkeyup = function (e) {
if (MOVE_ONLY_ON_PRESS) {
if (e.type === 'keydown') {
keyState[e.keyCode] = true;
}
} else {
keyState[e.keyCode] = e.type == 'keydown';
}
}
// in the main loop;
function update(timer) {
// you dont need to test for the combinations (ie up left) when its just simple movement
if (keyState[KEY_UP]) {
player.y -= MOVE_SPEED;
if (MOVE_ONLY_ON_PRESS) { keyState[KEY_UP] = false; }
}
if (keyState[KEY_DOWN]) {
player.y += MOVE_SPEED;
if (MOVE_ONLY_ON_PRESS) { keyState[KEY_DOWN] = false; }
}
if (keyState[KEY_LEFT]) {
player.x -= MOVE_SPEED;
if (MOVE_ONLY_ON_PRESS) { keyState[KEY_LEFT] = false; }
}
if (keyState[KEY_RIGHT]) {
player.x += MOVE_SPEED;
if (MOVE_ONLY_ON_PRESS) { keyState[KEY_RIGHT] = false; }
}
requestAnimationFrame(update);
}
requestAnimationFrame(update);
Bit fields.
Another way to simplify the arrow keys is to set the first 4 bits of a number to correspond to a key, one bit per key, then it is easy to test for combinations of keys as each of the 16 possible combinations has a unique number. You Could map many more keys this way but it becomes a little impractical when you go to far.
This is how to set them
var arrowBits = 0; // the value to hold the bits
const KEY_BITS = [4,1,8,2]; // left up right down
const KEY_MASKS = [0b1011,0b1110,0b0111,0b1101]; // left up right down
window.onkeydown = window.onkeyup = function (e) {
if(e.keyCode >= 37 && e.keyCode <41){
if(e.type === "keydown"){
arrowKeys |= KEY_BITS[e.keyCode - 37];
}else{
arrowKeys &= KEY_MASKS[e.keyCode - 37];
}
}
}
These are 8 of the 16 possible combinations
// postfix U,D,L,R for Up down left right
const KEY_U = 1;
const KEY_D = 2;
const KEY_L = 4;
const KEY_R = 8;
const KEY_UL = KEY_U + KEY_L; // up left
const KEY_UR = KEY_U + KEY_R; // up Right
const KEY_DL = KEY_D + KEY_L; //
const KEY_DR = KEY_D + KEY_R; //
This is how you test for then
if ((arrowBits & KEY_UL) === KEY_UL) { // is UP and LEFT only down
if (arrowBits & KEY_UL) { // is Up left down (down and right may also be down)
if ((arrowBits & KEY_U) === KEY_U) { // is Up only down
if (arrowBits & KEY_U) { // is Up down (any other key may be down)
if (!(arrowBits & KEY_U)) { // is Up up (any other key may be down)
if (!arrowBits) { // no keys are down
if (arrowBits) { // any one or more keys are down
I cannot tell you why your code doesn't work. If I replace the player-movements with console-logs, it works for me.
Although it's pretty primitive.
First separate the keystate-manager from the movement-logic.
var isKeyDown = (function(alias){
for(var i=0, a=Object.create(null); i<256;) a[i++]=false;
for(var k in alias) i=0|alias[k], i>0 && i<256 && Object.defineProperty(a,k,{get:Function("return this["+i+"]")});
function keyStateHandler(e){
a[e.which||e.keyCode] = e.type==="keydown"
}
addEventListener("keydown", keyStateHandler, false);
addEventListener("keyup", keyStateHandler, false);
return a;
})({
//add some aliases for more readable code
up: 38,
left: 37,
right: 39,
down: 40
});
once, because the update-interval of the keydown-event is not reliable, varies between browsers, and is not sync with the update-interval of the brwoser wich will cause flickering.
Now create a main loop for your game, and take account for the timing. Although requestAnimationFrame aims for a constant framerate you may have lags, and you don't want to see your player move at different speeds during these lags.
var lastCall = Date.now();
function tick(){
requestAnimationFrame(tick);
var now = Date.now(),
time = (now - lastCall) / 1000; //time in seconds
//because it's easier to think in terms like 2px/s
//than 0.002px/ms or 0.0333 px/frame
lastCall = now;
move(time);
}
tick();
now the actual movement:
//the speed of your player: it would be better if this would be
//a property of the player than hardcoded like you did, or global like this
var speed = 2; // 2px/second
function move( time ){
//accounts for varying framerates
//opposite directions cancel each other out
var dx = (isKeyDown.right - isKeyDown.left) * time,
dy = (isKeyDown.down - isKeyDown.up) * time;
//taking account for diagonals
if(dx && dy) dx /= Math.SQRT2, dy /= Math.SQRT2;
player.pos.x += dx * speed;
player.pos.y += dy * speed;
}
Based on skyline3000 answer I tried to make it work also in down, left and right. Appart from adding the other options I had to change window to document to make it work:
let keysdown = {};
document.addEventListener('keydown', function (evt) {
keysdown[evt.which] = true;
if (keysdown["38"] === true && keysdown["37"] === true) {
prota.moveUp();
prota.moveLeft();
// console.log('up & left');
} else if (keysdown["38"] === true && keysdown["39"] === true) {
prota.moveUp();
prota.moveRight();
// console.log('up & right');
} else if (keysdown["40"] === true && keysdown["37"] === true) {
prota.moveDown();
prota.moveLeft();
// console.log('down and left');
} else if (keysdown["40"] === true && keysdown["39"] === true) {
prota.moveDown();
prota.moveRight();
// console.log('down and right');
} else if (keysdown["38"] === true) {
prota.moveUp();
// console.log('up');
} else if (keysdown["40"] === true) {
prota.moveDown();
// console.log('down');
} else if (keysdown["39"] === true) {
prota.moveRight();
// console.log('right');
} else if (keysdown["37"] === true) {
prota.moveLeft();
// console.log('left');
}
}, false);
document.addEventListener('keyup', function (evt) {
keysdown[evt.which] = false;
}, false);
I want to capture the event of onkeydown to move a box but when I press the key constantly,there existing a delay,that is,when I press a key constantly,the box will move after a while.Please help me eliminate the delay.
If I understand you correctly I believe the delay you are referring to might be the default functionality of your keyboard. For example if you open a text-editor and press and hold space you notice it will do:
(move over a little) (wait) (start moving)
Keyboard events also work in this way. If you defined moving of your box to something similar to this:
document.addEventListener("keydown", function(e) {
var key = e.which;
if( key == 37 ) X -= speed;
if( key == 38 ) Y -= speed;
if( key == 39 ) X += speed;
if( key == 40 ) Y += speed;
});
Here is an example of the code above
You will notice this "type-writer" like behavior. To fix this you can use Booleans to detect when a key is pressed (true), then to detect when the pressed key is let go (false). In your screen update function check these Booleans for movement.
var LEFT = false , RIGHT = false, UP = false, DOWN = false;
...
document.addEventListener("keydown", function(e) {
var key = e.which;
if( key == 37 ) LEFT = true;
if( key == 38 ) UP = true;
if( key == 39 ) RIGHT = true;
if( key == 40 ) DOWN = true;
});
document.addEventListener("keyup", function(e) {
var key = e.which;
if( key == 37 ) LEFT = false;
if( key == 38 ) UP = false;
if( key == 39 ) RIGHT = false;
if( key == 40 ) DOWN = false;
});
...
// In update function
if( LEFT ) X -= speed;
if( UP ) Y -= speed;
if( RIGHT ) X += speed;
if( DOWN ) Y += speed;
Here is an example
Notice in the second example the movement is much smoother.
<html>
<head>
<style>
.box {
width: 100px;
height: 100px;
background: #ccc;
position: absolute;
top: 0px;
left: 0px;
}
.clear {
clear: both;
}
p {
margin-top: 100px;
}
</style>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
</head>
<body>
<div class="box"></div>
<div class="clear"></div>
<p> Press Space Bar</p>
<script type="text/javascript">
$(document).ready(function(){
console.log("ready");
});
$(document).on('keydown', function(event){
var code = event.keyCode;
if (code == 32) {
var div = $('.box');
var left = div.position().left;
var new_left = left + 1;
console.log(new_left);
div.css({
'left': new_left + "px"
});
}
});
</script>
</body>
</html>
a JSfiddle Link
maybe this could give you a little help.
and try also to console.log on your event
For a phone validation I am using in prototype.js a ...
<body onload="Xaprb.InputMask.setupElementMasks()">
That is actually an older version (1.4) and an upgrade to (1.7) would not work. Here is the code placed into the page...
<script type="text/javascript" language="javascript">
//<![CDATA[
if ( typeof(Xaprb) == 'undefined' ) {
Xaprb = new Object();
}
Xaprb.InputMask = {
masks: {
phone: {
format: '( ) - ',
regex: /\d/
}
},
setupElementMasks: function() {
if ( document.getElementsByClassName ) { // Requires the Prototype library
document.getElementsByClassName('input_mask').each(function(item) {
Event.observe(item, 'keypress',
Xaprb.InputMask.applyMask.bindAsEventListener(item), true);
});
}
},
applyMask: function(event) {
var match = /mask_(\w+)/.exec(this.className);
if ( match.length == 2 && Xaprb.InputMask.masks[match[1]] ) {
var mask = Xaprb.InputMask.masks[match[1]];
var key = Xaprb.InputMask.getKey(event);
if ( Xaprb.InputMask.isPrintable(key) ) {
var ch = String.fromCharCode(key);
var str = this.value + ch;
var pos = str.length;
if ( mask.regex.test(ch) && pos <= mask.format.length ) {
if ( mask.format.charAt(pos - 1) != ' ' ) {
str = this.value + mask.format.charAt(pos - 1) + ch;
}
this.value = str;
}
Event.stop(event);
}
}
},
isPrintable: function(key) {
return ( key >= 32 && key < 127 );
},
getKey: function(e) {
return window.event ? window.event.keyCode
: e ? e.which
: 0;
}
};
//]]>
</script>
Forgive me but I am fairly new to this subject if missing something simple. It basically validates the phone number as the customer types it in. This is located a http://www.comparediabetictestingsupplies.com and actually forces the sliders through jreviews not function (this is a Joomla! CMS). I also want to add, I changed all 75 '$' to '_' in prototype.js.
I ended up finding prototype.maskedinput.js that works with the prototype.js 1.6.1. This ended up changing the code around, but solved not only a conflict with jquery, but also mootools. You can find the source at https://github.com/bjartekv/MaskedInput/blob/master/prototype.maskedinput.js