让动作更流畅 - JS Canvas 游戏开发
Making movement smoother - JS Canvas Game development
我正在尝试使用 ES6 和 Canvas 进行游戏开发。在这个例子中,我有一个宇宙飞船的图像,我用左右箭头键旋转它,用上键向前移动它。
如果您查看旋转和向前移动,您会发现它很滞后。有没有办法让它更顺畅?
Plunker
'animation' 和移动的代码:(也在 plunker 中找到)
来自 engine.js
:
let mainLoop = function() {
clrscr();
draw();
requestAnimationFrame(mainLoop);
}
let draw = function() {
spaceship.draw(ctx);
}
let keyDownListener = function(e) {
if(e.keyCode == 37)
spaceship.rotateLeft();
if(e.keyCode == 38)
spaceship.moveForward(ctx);
if(e.keyCode == 39)
spaceship.rotateRight();
if(e.keyCode == 32)
createExplosion();
};
let clrscr = function() {
ctx.fillStyle="#415575";
ctx.fillRect(0,0,w,h);
}
来自 spaceship.js
:
let width = image.width * resizeMultiplier;
let height = image.height * resizeMultiplier;
const rotateDelta = 0.37;
const forwardDelta = 0.77;
let draw = function(context) {
//Save context
context.save();
//Translate before rotate
context.translate(x,y);
//Rotate on translated 0,0
context.rotate((angle) * Math.PI/180);
//Draw rotated image
context.drawImage(image, -(width/2), -(height/2), width, height);
//Restore the translated & rotated coords to where we began
context.restore();
}
let rotateRight = function() {
console.log(angle);
angle = (angle === 360) ? 0 : angle + (rotateDelta *(1000/60));
}
let rotateLeft = function() {
console.log(angle);
angle = (angle === -360) ? 0 : angle - (rotateDelta *(1000/60));
}
let moveForward = function() {
let dx = Math.sin((angle) * Math.PI/180);
let dy = - Math.cos((angle) * Math.PI/180);
x += dx * forwardDelta * (1000/60);
y += dy * forwardDelta * (1000/60);
console.log('dx: ',dx,' dy: ',dy);
//x += forwardDelta * (1000/20);
}
感谢您的宝贵时间。
主要原因是您为每个输入使用专用函数:
spaceship.rotateLeft()
, spaceship.moveForward()
, spaceship.rotateRight()
虽然这看起来很好 OOP 明智,但结果是每次调用 window.onkeydown
处理程序时,它将 interrupt/reset 当前执行,因为 key-handler与 frame-function.
不同步
frame-function 以固定间隔运行(理论上)。但是 key-handler 不遵循相同的模式,因为当您按下某个键时它会被触发。所以这两者有点相互矛盾。
(如有错误请指正,不能100%确定。)
无论如何,您可以通过将key-handler移动到spaceship.js
来解决它,并在key-handler中为每个键设置一个boolean
到[=20] =] 当您按下它时,还添加一个 onkeyup
处理程序以再次将它们设置回 false。
在你的 draw()
函数中,你调用了计算所有运动的函数,就在你绘制新值之前:
//move
const rotateDelta = 7; //degrees
const forwardDelta = 10;
let key = {up:false, left:false, right:false, fire:false};
//DRAW--------------------
let draw = function(ctx) {
move();
ctx.save();
...
};
//MOVE--------------------
let move = function() {
if (key.left) {angle = (angle <= -360)?0: angle-rotateDelta;}
if (key.right) {angle = (angle >= 360)?0: angle+rotateDelta;}
if (key.up) {
x += Math.sin(angle*Math.PI/180)*forwardDelta;
y += -Math.cos(angle*Math.PI/180)*forwardDelta;
}
if (key.fire) {}
};
//KEY-HANDLER--------------------
window.onkeydown = function(e) {
if (e.keyCode == 37) {key.left=true;} //LEFT
if (e.keyCode == 38) {key.up=true;} //UP
if (e.keyCode == 39) {key.right=true;} //RIGHT
if (e.keyCode == 32) {key.fire=true;} //SPACEBAR
};
window.onkeyup = function(e) {
if (e.keyCode == 37) {key.left=false;} //LEFT
if (e.keyCode == 38) {key.up=false;} //UP
if (e.keyCode == 39) {key.right=false;} //RIGHT
if (e.keyCode == 32) {key.fire=false;} //SPACEBAR
};
- 如您所见,我还删除了我认为不必要的距离和旋转并发症(
*(1000/60)
和不必要的括号等)。如果你确实需要它们,你当然可以使用它们,但我想让计算尽可能简单,以消除任何导致口吃的原因。 rotateDelta
和 forwardDelta
现在有很好的整数值。
在 Plunker 中,它们是 7
和 10
,在 SO 代码片段中,我不得不将它们降低一点,因为由于可用 space,船必须更小。
- 在左侧和right-key的计算中,我将
angle === -360
和angle === 360
分别更改为angle <= -360
和angle >= 360
,因为你的角度可以跳过360
然后该值不会被重置。这种方式更安全。
- 我更改的最后一件事是将
width:100%;
和 height:100%
添加到 <html>
,因此 <canvas>
正确覆盖了整个页面。
- 我做了一些其他更改,主要是为了让我自己对您的代码有一个很好的了解。喜欢风格就用,不喜欢就忽略
结果是smooth-sailingspace机器:
Engine = window.Engine || {};
Engine = function() {
let canvas,ctx, w,h;
//player
let ssImgPath = "http://i68.tinypic.com/2q87s0i.png";
let ssSizeRatio = 0.1; //multiplier for original image dimensions
let spaceship; //player object
let ssImage = new Image();
ssImage.src = ssImgPath;
//INIT--------------------
let initModule = function() {
canvas = document.getElementById("canvas");
canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
ctx = canvas.getContext("2d");
w=canvas.width, h=canvas.height;
spaceship = new Spaceship({x:w/2, y:h*0.7, angle:0, canvasW:w, canvasH:h, resizeMultiplier:ssSizeRatio, image:ssImage});
mainLoop();
};
//FRAME-LOOP--------------------
let mainLoop = function() {
clrscr();
draw();
requestAnimationFrame(mainLoop);
};
let clrscr = function() {
ctx.fillStyle = "rgb(65,85,117)";
ctx.fillRect(0,0,w,h);
};
let draw = function() {
spaceship.draw(ctx);
};
//RETURN--------------------
return {initModule};
}(); window.onload=Engine.initModule;
/*==============================================================*/
/****************************************************************/
/*==============================================================*/
Spaceship = window.Spaceship || {};
Spaceship = function(options) {
let {x,y,angle, canvasW,canvasH, resizeMultiplier, image} = options;
let width = image.width*resizeMultiplier;
let height = image.height*resizeMultiplier;
//move
const rotateDelta = 7; //degrees
const forwardDelta = 5;
let key = {up:false, left:false, right:false, fire:false};
//DRAW--------------------
let draw = function(ctx) {
move();
ctx.save();
ctx.translate(x,y);
ctx.rotate((angle) * Math.PI / 180); //player rotation
ctx.drawImage(image, -width/2, -height/2, width,height);
ctx.restore();
};
//MOVE--------------------
let move = function() {
if (key.left) {angle = (angle <= -360)?0: angle-rotateDelta;}
if (key.right) {angle = (angle >= 360)?0: angle+rotateDelta;}
if (key.up) {
x += Math.sin(angle*Math.PI/180)*forwardDelta;
y += -Math.cos(angle*Math.PI/180)*forwardDelta;
}
if (key.fire) {console.log("pew");}
};
//KEY-HANDLER--------------------
window.onkeydown = function(e) {
if (e.keyCode == 37) {key.left=true;} //LEFT
if (e.keyCode == 38) {key.up=true;} //UP
if (e.keyCode == 39) {key.right=true;} //RIGHT
if (e.keyCode == 32) {key.fire=true;} //SPACEBAR
};
window.onkeyup = function(e) {
if (e.keyCode == 37) {key.left=false;} //LEFT
if (e.keyCode == 38) {key.up=false;} //UP
if (e.keyCode == 39) {key.right=false;} //RIGHT
if (e.keyCode == 32) {key.fire=false;} //SPACEBAR
};
//RETURN--------------------
return {draw};
};
html, body {width:100%; height:99%; margin:0; padding:0;}
<!DOCTYPE html>
<html>
<head>
<title>Asteroids</title>
<link rel=stylesheet href="asteroids.css">
<script src="engine.js"></script>
<script src="spaceship.js"></script>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
</html>
笨蛋:https://plnkr.co/edit/nKAyweLV4d0hmmIRzxG4?p=preview
我正在尝试使用 ES6 和 Canvas 进行游戏开发。在这个例子中,我有一个宇宙飞船的图像,我用左右箭头键旋转它,用上键向前移动它。 如果您查看旋转和向前移动,您会发现它很滞后。有没有办法让它更顺畅?
Plunker
'animation' 和移动的代码:(也在 plunker 中找到)
来自 engine.js
:
let mainLoop = function() {
clrscr();
draw();
requestAnimationFrame(mainLoop);
}
let draw = function() {
spaceship.draw(ctx);
}
let keyDownListener = function(e) {
if(e.keyCode == 37)
spaceship.rotateLeft();
if(e.keyCode == 38)
spaceship.moveForward(ctx);
if(e.keyCode == 39)
spaceship.rotateRight();
if(e.keyCode == 32)
createExplosion();
};
let clrscr = function() {
ctx.fillStyle="#415575";
ctx.fillRect(0,0,w,h);
}
来自 spaceship.js
:
let width = image.width * resizeMultiplier;
let height = image.height * resizeMultiplier;
const rotateDelta = 0.37;
const forwardDelta = 0.77;
let draw = function(context) {
//Save context
context.save();
//Translate before rotate
context.translate(x,y);
//Rotate on translated 0,0
context.rotate((angle) * Math.PI/180);
//Draw rotated image
context.drawImage(image, -(width/2), -(height/2), width, height);
//Restore the translated & rotated coords to where we began
context.restore();
}
let rotateRight = function() {
console.log(angle);
angle = (angle === 360) ? 0 : angle + (rotateDelta *(1000/60));
}
let rotateLeft = function() {
console.log(angle);
angle = (angle === -360) ? 0 : angle - (rotateDelta *(1000/60));
}
let moveForward = function() {
let dx = Math.sin((angle) * Math.PI/180);
let dy = - Math.cos((angle) * Math.PI/180);
x += dx * forwardDelta * (1000/60);
y += dy * forwardDelta * (1000/60);
console.log('dx: ',dx,' dy: ',dy);
//x += forwardDelta * (1000/20);
}
感谢您的宝贵时间。
主要原因是您为每个输入使用专用函数:
spaceship.rotateLeft()
, spaceship.moveForward()
, spaceship.rotateRight()
虽然这看起来很好 OOP 明智,但结果是每次调用 window.onkeydown
处理程序时,它将 interrupt/reset 当前执行,因为 key-handler与 frame-function.
不同步
frame-function 以固定间隔运行(理论上)。但是 key-handler 不遵循相同的模式,因为当您按下某个键时它会被触发。所以这两者有点相互矛盾。
(如有错误请指正,不能100%确定。)
无论如何,您可以通过将key-handler移动到spaceship.js
来解决它,并在key-handler中为每个键设置一个boolean
到[=20] =] 当您按下它时,还添加一个 onkeyup
处理程序以再次将它们设置回 false。
在你的 draw()
函数中,你调用了计算所有运动的函数,就在你绘制新值之前:
//move
const rotateDelta = 7; //degrees
const forwardDelta = 10;
let key = {up:false, left:false, right:false, fire:false};
//DRAW--------------------
let draw = function(ctx) {
move();
ctx.save();
...
};
//MOVE--------------------
let move = function() {
if (key.left) {angle = (angle <= -360)?0: angle-rotateDelta;}
if (key.right) {angle = (angle >= 360)?0: angle+rotateDelta;}
if (key.up) {
x += Math.sin(angle*Math.PI/180)*forwardDelta;
y += -Math.cos(angle*Math.PI/180)*forwardDelta;
}
if (key.fire) {}
};
//KEY-HANDLER--------------------
window.onkeydown = function(e) {
if (e.keyCode == 37) {key.left=true;} //LEFT
if (e.keyCode == 38) {key.up=true;} //UP
if (e.keyCode == 39) {key.right=true;} //RIGHT
if (e.keyCode == 32) {key.fire=true;} //SPACEBAR
};
window.onkeyup = function(e) {
if (e.keyCode == 37) {key.left=false;} //LEFT
if (e.keyCode == 38) {key.up=false;} //UP
if (e.keyCode == 39) {key.right=false;} //RIGHT
if (e.keyCode == 32) {key.fire=false;} //SPACEBAR
};
- 如您所见,我还删除了我认为不必要的距离和旋转并发症(
*(1000/60)
和不必要的括号等)。如果你确实需要它们,你当然可以使用它们,但我想让计算尽可能简单,以消除任何导致口吃的原因。rotateDelta
和forwardDelta
现在有很好的整数值。
在 Plunker 中,它们是7
和10
,在 SO 代码片段中,我不得不将它们降低一点,因为由于可用 space,船必须更小。 - 在左侧和right-key的计算中,我将
angle === -360
和angle === 360
分别更改为angle <= -360
和angle >= 360
,因为你的角度可以跳过360
然后该值不会被重置。这种方式更安全。 - 我更改的最后一件事是将
width:100%;
和height:100%
添加到<html>
,因此<canvas>
正确覆盖了整个页面。 - 我做了一些其他更改,主要是为了让我自己对您的代码有一个很好的了解。喜欢风格就用,不喜欢就忽略
结果是smooth-sailingspace机器:
Engine = window.Engine || {};
Engine = function() {
let canvas,ctx, w,h;
//player
let ssImgPath = "http://i68.tinypic.com/2q87s0i.png";
let ssSizeRatio = 0.1; //multiplier for original image dimensions
let spaceship; //player object
let ssImage = new Image();
ssImage.src = ssImgPath;
//INIT--------------------
let initModule = function() {
canvas = document.getElementById("canvas");
canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
ctx = canvas.getContext("2d");
w=canvas.width, h=canvas.height;
spaceship = new Spaceship({x:w/2, y:h*0.7, angle:0, canvasW:w, canvasH:h, resizeMultiplier:ssSizeRatio, image:ssImage});
mainLoop();
};
//FRAME-LOOP--------------------
let mainLoop = function() {
clrscr();
draw();
requestAnimationFrame(mainLoop);
};
let clrscr = function() {
ctx.fillStyle = "rgb(65,85,117)";
ctx.fillRect(0,0,w,h);
};
let draw = function() {
spaceship.draw(ctx);
};
//RETURN--------------------
return {initModule};
}(); window.onload=Engine.initModule;
/*==============================================================*/
/****************************************************************/
/*==============================================================*/
Spaceship = window.Spaceship || {};
Spaceship = function(options) {
let {x,y,angle, canvasW,canvasH, resizeMultiplier, image} = options;
let width = image.width*resizeMultiplier;
let height = image.height*resizeMultiplier;
//move
const rotateDelta = 7; //degrees
const forwardDelta = 5;
let key = {up:false, left:false, right:false, fire:false};
//DRAW--------------------
let draw = function(ctx) {
move();
ctx.save();
ctx.translate(x,y);
ctx.rotate((angle) * Math.PI / 180); //player rotation
ctx.drawImage(image, -width/2, -height/2, width,height);
ctx.restore();
};
//MOVE--------------------
let move = function() {
if (key.left) {angle = (angle <= -360)?0: angle-rotateDelta;}
if (key.right) {angle = (angle >= 360)?0: angle+rotateDelta;}
if (key.up) {
x += Math.sin(angle*Math.PI/180)*forwardDelta;
y += -Math.cos(angle*Math.PI/180)*forwardDelta;
}
if (key.fire) {console.log("pew");}
};
//KEY-HANDLER--------------------
window.onkeydown = function(e) {
if (e.keyCode == 37) {key.left=true;} //LEFT
if (e.keyCode == 38) {key.up=true;} //UP
if (e.keyCode == 39) {key.right=true;} //RIGHT
if (e.keyCode == 32) {key.fire=true;} //SPACEBAR
};
window.onkeyup = function(e) {
if (e.keyCode == 37) {key.left=false;} //LEFT
if (e.keyCode == 38) {key.up=false;} //UP
if (e.keyCode == 39) {key.right=false;} //RIGHT
if (e.keyCode == 32) {key.fire=false;} //SPACEBAR
};
//RETURN--------------------
return {draw};
};
html, body {width:100%; height:99%; margin:0; padding:0;}
<!DOCTYPE html>
<html>
<head>
<title>Asteroids</title>
<link rel=stylesheet href="asteroids.css">
<script src="engine.js"></script>
<script src="spaceship.js"></script>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
</html>