I am learning JavaScript and I am trying to program a small game.
I want that something in my game moves consistently while I hold down a key.
This is what I came up with so far:
document.onkeydown = onKeyDownListener;
function onKeyDownListener(evt) {
var keyCode = evt.which || evt.keyCode;
if(keyCode == 37) {
move();
}
}
My problem is that when I press down the key move gets called once, then there is a pause and after that pause move gets called repeatedly as I intend. It goes
like this: m......mmmmmmmmm when 'm' stands for move and'.' is the pause.
Is there a way to get rid of the pause?
Here is a GIF of what happens:
I'm pretty sure you have this problem because you're not depending on a game loop to run your game. If you were, you wouldn't be running into this problem because the game loop at each interval would check if the key is pressed. Your web app is only reacting to each key press as they happen.
I highly recommend you read this tutorial:
W3 HTML Game - Game Controllers
https://www.w3schools.com/graphics/game_movement.asp
Read the "Keyboard as Controller" section. The "setInterval" method induces the game loop in this example.
I used it as a reference to write game code for myself.
This is code from the W3 tutorial. It also goes on to teach collision detection as well.
var myGameArea = {
canvas : document.createElement("canvas"),
start : function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function (e) {
myGameArea.keys = (myGameArea.keys || []);
myGameArea.keys[e.keyCode] = true;
})
window.addEventListener('keyup', function (e) {
myGameArea.keys[e.keyCode] = false;
})
},
clear : function(){
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function updateGameArea() {
myGameArea.clear();
myGamePiece.speedX = 0;
myGamePiece.speedY = 0;
if (myGameArea.keys && myGameArea.keys[37]) {myGamePiece.speedX = -1; }
if (myGameArea.keys && myGameArea.keys[39]) {myGamePiece.speedX = 1; }
if (myGameArea.keys && myGameArea.keys[38]) {myGamePiece.speedY = -1; }
if (myGameArea.keys && myGameArea.keys[40]) {myGamePiece.speedY = 1; }
myGamePiece.newPos();
myGamePiece.update();
}
You could try this:
document.onkeydown = onKeyDownListener;
var keyDown = false;
function onKeyDownListener(evt) {
if(evt.keyCode == 37) {
keyDown = true;
}
}
document.onkeyup = function(evt){
if(evt.keyCode == 37) {
keyDown = false;
}
}
setInterval(function(){
if(keyDown){
player.x--;
}
},20)
It's not ideal, I know, but my best guess is it's an issue with keydown detection. This way, for as long as you're holding down that key, it will move until you let go of the key.
Related
I am fairly new to JavaScript and have searched everywhere for an answer to my question and cant seem to find anything related at all. This tells me that I'm missing something with my understanding of how my program works.
I have written a small game where the player navigates through a randomly generated maze using a gameloop that checks keydown events every x milliseconds. The game has a difficulty dropdown menu and then the game is started my clicking a button that calls a function to create a canvas where the game is drawn.
My problem is that when the button is clicked again to create a new maze without reloading the page, the gameloop for the original maze is still running and so key events are registered twice. This is causing some unexpected behavior. It's as though every time the button is clicked, a new instance of the function is running. Is there some way that each time the button is clicked I can set it to stop the previous game function?
var canvas;
var div;
var mazeGenButton;
$(document).ready(function () {
canvas = null;
div = document.getElementById('canvascontainer');;
mazeGenButton = document.getElementById("mazeGenButton");
mazeGenButton.onclick = createInstance;
});
function createInstance() {
if (canvas != null) {
div.removeChild(document.getElementById("myCanvas"));
}
canvas = document.createElement('canvas');
canvas.id = "myCanvas";
canvas.width = 1000;
canvas.height = 1000;
div.appendChild(canvas);
drawMaze();
};
var drawMaze = function () {
//code here to create the game(not posted)
//here is the Key listener - not sure if it's related
var keyState = {};
window.addEventListener('keydown', function (e) {
keyState[e.keyCode || e.which] = true;
}, true);
window.addEventListener('keyup', function (e) {
keyState[e.keyCode || e.which] = false;
}, true);
function gameLoop() {
//left
if (keyState[37] || keyState[65]) {
if (isLegalMove(playerXPos - 1, playerYPos)) {
grid[playerXPos][playerYPos].removePlayerCell();
playerXPos -= 1;
grid[playerXPos][playerYPos].setPlayerCell();
}
}
//right
if (keyState[39] || keyState[68]) {
if (isLegalMove(playerXPos + 1, playerYPos)) {
grid[playerXPos][playerYPos].removePlayerCell();
playerXPos += 1;
grid[playerXPos][playerYPos].setPlayerCell();
}
}
//up
if (keyState[38] || keyState[87]) {
if (isLegalMove(playerXPos, playerYPos - 1)) {
grid[playerXPos][playerYPos].removePlayerCell();
playerYPos -= 1;
grid[playerXPos][playerYPos].setPlayerCell();
}
}
//down
if (keyState[40] || keyState[83]) {
if (isLegalMove(playerXPos, playerYPos + 1)) {
grid[playerXPos][playerYPos].removePlayerCell();
playerYPos += 1;
grid[playerXPos][playerYPos].setPlayerCell();
}
}
drawSurroundingCells();
setTimeout(gameLoop, 50);
}
}
I'm trying to implement key input in my ping-pong game. The main point is that the up and down arrow keys don't work at all. My browser console doesn't display any errors messages.
Here is my code, this is WIP some Objects are not implemented yet
var playerBat = {
x: null,
y: null,
width: 20,
height: 80,
UP_DOWN: false,
DOWN_DOWN: false,
update: function() {
// Keyboard inputs
window.addEventListener('keydown', onKeyDown, false);
window.addEventListener('keyup', onKeyUp, false);
var key = {
UP: 38,
DOWN: 40
};
function onKeyDown(e) {
if (e.keyCode == 38)
this.UP_DOWN = true;
else if (e.keyCode == 40)
this.DOWN_DOWN = true;
}
function onKeyUp(e) {
if (e.keyCode == 38)
this.UP_DOWN = false;
else if (e.keyCode == 40)
this.DOWN_DOWN = false;
}
this.y = Math.max(Math.min(this.y, Canvas_H - this.height), 0); // Collide world bounds
},
render: function() {
ctx.fillStyle = '#000';
ctx.fillRect(this.x, this.y, this.width, this.height);
if (this.UP_DOWN)
this.playerBat.y -= 5;
else if (this.DOWN_DOWN)
this.playerBat.y += 5;
}
};
The events are firing, the problem is that you're adding them on each update. What you'll want to do it take the callbacks and the addEventListeners outside in a method such as addEvents, which should be called ONCE during initialization. Currently the huge amount of event handlers being triggered kills the page.
function addEvents() {
window.addEventListener('keydown', onKeyDown, false);
window.addEventListener('keyup', onKeyUp, false);
var key = {
UP: 38,
DOWN: 40
};
function onKeyDown(e) {
if (e.keyCode == key.UP) {
playerPaddle.UP_DOWN = true;
}
if (e.keyCode == key.DOWN) {
playerPaddle.DOWN_DOWN = true;
}
}
function onKeyUp(e) {
if (e.keyCode == key.UP) {
playerPaddle.UP_DOWN = false;
}
if (e.keyCode == 40) {
playerPaddle.DOWN_DOWN = key.DOWN;
}
}
}
After reviewing it further there are some other problems. First of all, the logic for actually changing the X and Y of the paddle should be within the update method (since that's what's usually used to change object properties), while the render method should simply draw the shapes and images using the object's updated properties.
Second, you're trying to access this.playerBat.y within the render method, however 'this' actually IS the playerBat. So in order to properly target the 'y' property you'd need to write this.y instead.
I also noticed that you've got a key map, with UP and DOWN keycodes defined, but don't actually use it, instead you use numbers. Maybe something you were planning on doing?
I reimplemented the code you have provided and added a init function to playerBat that attaches the event listeners for keydown and keyup events. I just kept the relevant bits and implemented objects as functions, but the concept should still be the applicable.
The callback function passed into addEventListener needs to bind this, otherwise the this value inside the callback (this.UP_DOWN and this.DOWN_DOWN) won't be the same as the this value in the enclosing scope; the one value you intended.
<canvas id='canvas' style="background:#839496">Your browser doesn't support The HTML5 Canvas</canvas>
<script>
var canvas = document.getElementById('canvas');
canvas.width = window.innerWidth-20;
canvas.height = window.innerHeight-20;
var ctx = canvas.getContext('2d');
var Canvas_W = Math.floor(canvas.width);
var Canvas_H = Math.floor(canvas.height);
/*
* Define a Player object.
*/
function PlayerBat(){
this.x = null;
this.y = null;
this.width = 20;
this.height = Canvas_H/3;
this.UP_DOWN = false;
this.DOWN_DOWN = false;
this.init = function() {
console.log('init');
// MUST bind `this`!
window.addEventListener('keydown', function(e){
console.log('keydown');
if (e.keyCode == 38) this.UP_DOWN = true;
else if (e.keyCode == 40) this.DOWN_DOWN = true;
}.bind(this), false);
// MUST bind `this`!
window.addEventListener('keyup', function(e){
console.log('keyUp')
if (e.keyCode == 38) this.UP_DOWN = false;
else if (e.keyCode == 40) this.DOWN_DOWN = false;
}.bind(this), false);
};
this.update = function() {
var key = {UP: 38, DOWN: 40};
this.y = Math.max(Math.min(this.y, Canvas_H - this.height), 0);
};
this.render = function() {
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Redraw paddle
ctx.fillStyle = '#00F';
ctx.fillRect(this.x, this.y, this.width, this.height);
this.y = (this.UP_DOWN) ? this.y - 5 : ((this.DOWN_DOWN) ? this.y + 5 : this.y );
};
}
function GameRunner(){
// Create instance of player
var playerBat = new PlayerBat();
playerBat.init();
// Execute upon instantiation of GameRunner
(function () {
playerBat.x = playerBat.width;
playerBat.y = (Canvas_H - playerBat.height) / 2;
})();
function step() {
playerBat.update();
playerBat.render();
requestAnimationFrame(step);
}
// Public method. Start animation loop
this.start = function(){
requestAnimationFrame(step);
}
}
// Create GameRunner instance
var game = new GameRunner();
// Start game
game.start();
</script>
So, I've created myself a little demo with javascript/html canvas in the context of a gameloop. You can move a small square by pressing the w,a,s,d keys. However, when held down for more than 3 or 4 seconds, the canvas becomes stuttery and the square almost stops moving.
Here's the javascript;
// --------------------------------------------------------------------
// -- MAIN GAME LOOP
// --------------------------------------------------------------------
function gameLoop(){
update();
render();
requestAnimationFrame(gameLoop);
}
function update(){
processInput();
};
function render(){
var canvas = document.getElementById('viewport');
var ctx = canvas.getContext('2d');
if(upDown){
rect.top -= rect.speed;
}else if(downDown){
rect.top += rect.speed;
}else if(leftDown){
rect.left -= rect.speed;
}else if(rightDown) {
rect.left += rect.speed;
}
ctx.clearRect(0, 0, 1024, 768);
ctx.beginPath();
ctx.rect(rect.left, rect.top, 50, 50, true);
ctx.closePath();
ctx.fill();
};
var rect = {
top: 0,
left: 0,
speed: 5
};
// --------------------------------------------------------------------
// -- OTHER FUNCTIONS
// --------------------------------------------------------------------
var rightDown = false;
var leftDown = false;
var upDown = false;
var downDown = false;
function processInput(){
$(document).keydown(function(e){
console.log(e.keyCode);
if(e.keyCode == 87){upDown = true;}
if(e.keyCode == 83){downDown = true;}
if(e.keyCode == 68){rightDown = true;}
if(e.keyCode == 65){leftDown = true;}
}).keyup(function(){
upDown = false;
downDown = false;
rightDown = false;
leftDown = false;
})
}
$(document).ready(function(){
requestAnimationFrame(gameLoop);
});
Anyone got any ideas?
Here's my codepen;
http://codepen.io/anon/pen/wKGJOr
The issue is because you're calling processInput (via update) within your gameloop. This function is attaching new keydown and keyup event handlers every time it is called. It's only necessary to call it once. Remove the call from update, and (for example) call it within the ready function instead:
$(document).ready(function(){
processInput();
requestAnimationFrame(gameLoop);
});
By registering more and more event handlers, you're causing a lot more code to run than is necessary, hence the stuttering.
Updated codepen.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I'm trying to put together a simple "game" in HTML5. However I can't get diagonal movement working.
The "diagonal movement" only works when the two buttons are pressed at exactly the same time. Even then it moves once. Here's the code:
// Getting canvas, and canvas context
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
//Keymap, later passed as method parameter
var map = [false,false,false,false];
// Top, Right, Bottom, Left
// Function for resetting keymap
var resetMap = function() {
for(i=0;i<map.length;i++) {
map[i] = false;
};
};
//Function for clearing the screen before drawing
var clrScrn = function() {
ctx.clearRect(0,0,500,500);
};
// The player character
var character = function() {
this.x = 50;
this.y = 50;
this.h = 50;
this.w = 50;
};
// Draw method of the character class
character.prototype.draw = function() {
ctx.fillRect(this.x,this.y,this.h,this.w);
};
// The move method of the character class
character.prototype.move = function(map) {
if(map[0] === true) {
if(map[1] == true) {
this.x+=5;
this.y-=5;
console.log("Pressed at the same time");
}
else {
this.y-=5;
}
}
else if(map[1] === true) {
this.x+=5;
}
else if(map[2] === true) {
this.y+=5;
}
else if(map[3] === true) {
this.x-=5;
}
};
//Making a new character
var player = new character();
player.draw();
// Drawing everything on screen
var render = function() {
clrScrn();
player.move(map);
player.draw();
resetMap();
requestAnimFrame(render);
};
//Calling the render function
render();
//Binding event listener to window,checking key down, likely the source of the problem
window.addEventListener("keydown",function(e){
if(e.keyCode == 38 && e.keyCode == 39) {
map[0] = true;
map[1] = true;
}
else if(e.keyCode == 38) {
map[0] = true;
}
if(e.keyCode == 39) {
map[1] = true;
}
if(e.keyCode == 40) {
map[2] = true;
}
if(e.keyCode == 37) {
map[3] = true;
}
console.log(e.keyCode);
},false);
//Binding event listener to key up
window.addEventListener("keyup",function(e){
resetMap();
},false);
In your render function:
player.move(map); // you move the player
player.draw(); // you draw the player
resetMap(); // you forget all pressed keys
The resetMap() is the reason you need to press the keys again in order to move just one more step.
Note that horizontal and vertical motions may accidentally work due to keyboard repeat when a key is pressed for a long time. But you shouldn't depend on key repeats for a game. You should detect key down and key up individually for individual keys in order to figure out if a key is pressed or not.
try looking at:
JavaScript multiple keys pressed at once
and then re-work your if/else logic a little bit.
I am currently developing a Javascript game (almost everything is based on a tutorial yet, so I am not worried of sharing the code).
The problem is, I can't get the character to jump after pressing the Space button. Please, can someone look at the code and help me?
// EDIT: Sorry for lack of information I provided. The thing is - code is written, the game is in the state, that the character is animated (=is running) and the backgrounds are moving. Yesterday, I tried to implement some basic controls, such as jump by pressing spacebar. The thing is, the player won't jump at all, and browser console is not giving me any error statements.
Character is defined as Player on line 5. and 321. in the code provided below.
The jumping is defined in the following examples:
Pressing the Space button
var KEY_CODES = {
32: 'space'
};
var KEY_STATUS = {};
for (var code in KEY_CODES) {
if (KEY_CODES.hasOwnProperty(code)) {
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;
}
};
Other jump information (please, read the comments in the code)
this.update = function() {
// jump, if the characted is NOT currently jumping or falling
if (KEY_STATUS.space && this.dy === 0 && !this.isJumping) {
this.isJumping = true;
this.dy = this.jumpDy;
jumpCounter = 12;
assetLoader.sounds.jump.play();
}
// longer jump if the space bar is pressed down longer
if (KEY_STATUS.space && jumpCounter) {
this.dy = this.jumpDy;
}
jumpCounter = Math.max(jumpCounter-1, 0);
this.advance();
// gravity
if (this.isFalling || this.isJumping) {
this.dy += this.gravity;
}
// change animation is-falling
if (this.dy > 0) {
this.anim = this.fallAnim;
}
// change animation is-jumping
else if (this.dy < 0) {
this.anim = this.jumpAnim;
}
else {
this.anim = this.walkAnim;
}
this.anim.update();
};
/**
* Update the Sprite's position by the player's speed
*/
this.update = function() {
this.dx = -player.speed;
this.advance();
};
/**
* Draw the current player's frame
*/
this.draw = function() {
this.anim.draw(this.x, this.y);
};
}
Player.prototype = Object.create(Vector.prototype);
Everything seems just fine to me, but the player just won't move. :(
Any help?
If you are curious about the full code, go here: http://pastebin.com/DHZKhBMT
EDIT2:
Thank you very much for your replies so far.
I have moved the RequestAnimFrame to the end of the function - will keep that in mind, thanks.
I have also implemented the simple jumping script Ashish provided above, but the character is still not jumping.
This is what it looks like now:
/** JUMP KEYS DEFINITION **/
$(document).keypress(function(e){
if(e.which==32){
$('Player.prototype').css({'top':"0px"});
}
setTimeout(function(){
$('Player.prototype').css({'top':"200px"});
},350);
});
/** DEFINING CHARACTER **/
function Player(x, y) {
this.dy = 0;
this.gravity = 1;
this.speed = 6;
this.jumpDy = -10;
this.isJumping = false;
this.width = 60;
this.height = 96;
this.sheet = new SpriteSheet('imgs/normal_walk.png', this.width, this.height);
this.walkAnim = new Animation(this.sheet, 4, 0, 11);
this.jumpAnim = new Animation(this.sheet, 4, 3, 3);
this.fallAnim = new Animation(this.sheet, 4, 3, 3);
this.anim = this.walkAnim;
Vector.call(this, x, y, 0, this.dy);
var jumpCounter = 0; // Maximalna dlzka drzania tlacidla skakania
}
Player.prototype = Object.create(Vector.prototype);
Where am I wrong?
I've tried in http://jsfiddle.net/Ykge9/1/
and you have an infinite loop in animate, the requestAnimFrame should be at the end of the function:
/**
* Loop cykly hry
*/
function animate() {
background.draw();
for (i = 0; i < ground.length; i++) {
ground[i].x -= player.speed;
ctx.drawImage(assetLoader.imgs.grass, ground[i].x, ground[i].y+250);
}
if (ground[0].x <= -platformWidth) {
ground.shift();
ground.push({'x': ground[ground.length-1].x + platformWidth, 'y': platformHeight});
}
player.anim.update();
player.anim.draw(64, 260);
requestAnimFrame( animate );
}