2

I'm working on making a simple space game where a ship moves left and right to dodge asteroids.

I learned to move my ship left and right from this video.

But the movement is pretty blocky. How do I move the ship smoothly?

Here is all my code:

// JavaScript Document

////// Variables //////
var canvas = {width:300, height:300 };
var score = 0;

var player = {
 x:canvas.width/2,
 y:canvas.height-100,
 speed: 20
};




////// Arrow keys //////

function move(e) {
 
 if(e.keyCode == 37) { 
  player.x -= player.speed;
 }
 if(e.keyCode == 39) {
  player.x += player.speed; 
 }
 
 update();
 
}

document.onkeydown = move;



////// other functions //////


//function to clear canvas
function clearCanvas() {
 ctx.clearRect(0,0,canvas.width,canvas.height);
}

// Draw Player ship.
function ship(x,y) {
 var x = player.x;
 var y = player.y;
 ctx.fillStyle = "#FFFFFF";

 ctx.beginPath();
    ctx.moveTo(x,y);
    ctx.lineTo(x+15,y+50);
    ctx.lineTo(x-15,y+50);
    ctx.fill();
}

// update

setInterval (update, 50);

function update() {
 clearCanvas();
 ship();
}
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>My Game</title>   

<script src="game-functions.js"></script>
        
</head>

<body>

<canvas id="ctx" width="300" height="300" style="border: thin solid black; background-color: black;"></canvas>
<br>


<script>
////// Canvas setup //////
var ctx = document.getElementById("ctx").getContext("2d");



</script>

</body>
</html>
jwpfox
  • 5,124
  • 11
  • 45
  • 42
Dylan Madigan
  • 31
  • 1
  • 1
  • 2
  • 1
    Possible duplicate of [Body animation isn't smooth](http://stackoverflow.com/questions/32365542/body-animation-isnt-smooth) – Kaiido Oct 01 '16 at 13:24
  • In general, your movements will be smoother if you make smaller movements (smaller 'player.speed') more often (time interval 1000/60=16.67). Consider learning about [requestAnimationFrame](https://www.paulirish.com/2011/requestanimationframe-for-smart-animating/) which is a better timing loop for animation. – markE Oct 01 '16 at 14:50

2 Answers2

7

This is because keydown triggers events similarity to how you keyboard will type on Notepad, as it it will trigger once have a slight delay and they trigger many more timers.

As @Azamantes stated to solve this you will want to make a boolean that is set to true on keydown and false on keyup. Then you will be using a main event loop with either a setTimeout/setInterval and/or requestAnimationFrame. In the example below I've just used setInterval for the main loop for simplicity since you already have it, we can then move move() into that main loop:


Note: For implementing requestAnimationFrame see @MarkE's comment on this question.

Also requestAnimationFrame will refresh as much as it can by default. In other words you need to add more logic if you need to control the FPS, which can be common in HTML5 games. For using requestAnimationFrame with controlled FPS see this answer.

// JavaScript Document

////// Variables //////
var canvas = {width:300, height:300 };
var score = 0;

var player = {
 x:canvas.width/2,
 y:canvas.height-100,
 speed: 3
};

var LEFT = false; 
var RIGHT = false;


////// Arrow keys //////

function move() {
 
 if(LEFT) { 
  player.x -= player.speed;
 }
 if(RIGHT) {
  player.x += player.speed; 
 }
 
}

document.onkeydown = function(e) {
 if(e.keyCode == 37) LEFT = true;
 if(e.keyCode == 39) RIGHT = true;
}

document.onkeyup = function(e) {
 if(e.keyCode == 37) LEFT = false;
 if(e.keyCode == 39) RIGHT = false;
}


////// other functions //////


//function to clear canvas
function clearCanvas() {
 ctx.clearRect(0,0,canvas.width,canvas.height);
}

// Draw Player ship.
function ship(x,y) {
 var x = player.x;
 var y = player.y;
 ctx.fillStyle = "#FFFFFF";

 ctx.beginPath();
    ctx.moveTo(x,y);
    ctx.lineTo(x+15,y+50);
    ctx.lineTo(x-15,y+50);
    ctx.fill();
}

// update

setInterval (update, 10);

function update() {
 clearCanvas();
 ship();
    move();
}
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>My Game</title>   

<script src="game-functions.js"></script>
        
</head>

<body>

<canvas id="ctx" width="300" height="300" style="border: thin solid black; background-color: black;"></canvas>
<br>


<script>
////// Canvas setup //////
var ctx = document.getElementById("ctx").getContext("2d");



</script>

</body>
</html>
Community
  • 1
  • 1
Spencer Wieczorek
  • 21,229
  • 7
  • 44
  • 54
  • `requestAnimationFrame will refresh as much as it can by default`, nope, that's the point of rAF, it will be called **only** at screen refresh rate. – Kaiido Oct 02 '16 at 07:54
  • @Kaiido Yes, what I mean is that it will go as fast as the screen refresh rate. That's the point of `requestAnimationFrame` and why it's not as easy to control the FPS of it – Spencer Wieczorek Oct 02 '16 at 17:53
2
  1. Use requestAnimationFrame
  2. Don't put update (your rendering) inside keyUp keyDown event handlers
  3. Make a boolean variable (one for each directio) and set it to true on keuDown and to false on keyUp and inside your rendering functions add a simple if check

TL;DR

Make your life simpler.

You might want to use event.key instead, which returns key name ('a', 'Esc', 'Space' etc), makes it more readable if you don't put comments next to the keyCodes.

Azamantes
  • 1,435
  • 10
  • 15
  • 2
    Good general advice but since the OP is new to javascript & game-dev you might want to expand your answer to fill the gap between your answer and their experience. ;-) – markE Oct 01 '16 at 14:52