Как использовать клавиши со стрелками для плавного перемещения объекта на холсте

Я работаю над простой космической игрой, в которой корабль движется влево и вправо, чтобы увернуться от астероидов.

Я научился перемещать свой корабль влево и вправо из этого видео.

Но движение довольно блочное. Как плавно перемещать корабль?

Вот весь мой код:

// 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>


person Dylan Madigan    schedule 01.10.2016    source источник
comment
Возможный дубликат анимация тела не плавная   -  person Kaiido    schedule 01.10.2016
comment
В целом, ваши движения будут более плавными, если вы будете чаще совершать небольшие движения (меньшая 'player.speed') (интервал времени 1000/60=16,67). Подумайте о том, чтобы узнать о requestAnimationFrame, который является лучшим циклом синхронизации для анимации.   -  person markE    schedule 01.10.2016


Ответы (2)


Это связано с тем, что keydown запускает события, похожие на то, как вы печатаете на клавиатуре в Блокноте, поскольку он срабатывает один раз с небольшой задержкой, и они запускают гораздо больше таймеров.

Как заявил @Azamantes, чтобы решить эту проблему, вам нужно сделать логическое значение, которое установлено в true для keydown и false для keyup. Затем вы будете использовать основной цикл событий либо с setTimeout/setInterval, либо с requestAnimationFrame. В приведенном ниже примере я только что использовал setInterval для основного цикла для простоты, поскольку он у вас уже есть, мы можем затем переместить move() в этот основной цикл:


Примечание. Для реализации requestAnimationFrame см. комментарий @MarkE к этому вопросу.

Также requestAnimationFrame будет обновлять столько, сколько может по умолчанию. Другими словами, вам нужно добавить больше логики, если вам нужно контролировать FPS, что может быть обычным явлением в играх HTML5. Для использования requestAnimationFrame с контролируемым FPS см. этот ответ.

// 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>

person Spencer Wieczorek    schedule 02.10.2016
comment
requestAnimationFrame will refresh as much as it can by default, нет, в этом и смысл rAF, он будет вызываться только при частоте обновления экрана. - person Kaiido; 02.10.2016
comment
@Kaiido Да, я имею в виду, что он будет работать так же быстро, как частота обновления экрана. В этом смысл requestAnimationFrame и почему не так просто контролировать FPS. - person Spencer Wieczorek; 02.10.2016

  1. Использовать запросаниматионфрейм
  2. Не помещайте update (ваш рендеринг) в обработчики событий keyUp keyDown
  3. Создайте логическую переменную (по одной для каждого направления) и установите для нее значение true для keuDown и false для keyUp, а внутри ваших функций рендеринга добавьте простую проверку if

TL;DR

Сделайте свою жизнь проще.

Вместо этого вы можете использовать event.key, который возвращает имя клавиши («a», «Esc», «Пробел» и т. д.), делает его более читаемым, если вы не ставите комментарии рядом с кодами клавиш.

person Azamantes    schedule 01.10.2016
comment
Хороший общий совет, но, поскольку OP является новым для javascript и разработки игр, вы можете расширить свой ответ, чтобы заполнить пробел между вашим ответом и их опытом. ;-) - person markE; 01.10.2016