我正在试验ES6&用于游戏开发的画布。在这个例子中,我有一个宇宙飞船的图像,我用左右箭头键旋转它,并用上键向前移动它。 如果你观察旋转和前进,你会发现它是滞后的。有没有办法让它更顺畅?
“动画”和动作的代码:(也在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);
}
感谢您的时间。
答案 0 :(得分:3)
主要原因是您为每个输入使用专用功能:
spaceship.rotateLeft()
,spaceship.moveForward()
,spaceship.rotateRight()
虽然这看起来非常好OOP,但结果是每次调用window.onkeydown
处理程序时,它都会中断/重置当前执行,因为密钥处理程序与...不同步帧的功能。
帧功能以固定间隔运行(理论上)。但是键处理程序不遵循相同的模式,因为当你按一个键时会被触发。所以这两种方式相互作用。
(如果我错了,请任何人纠正我,不要百分百肯定。)
在任何情况下,您都可以通过将密钥处理程序移至spaceship.js
来解决此问题,并在密钥处理程序中为每个密钥设置boolean
,当您按下它时true
,并添加一个onkeyup
处理程序,将它们重新设置为假
在您的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代码段中我不得不降低它们,因为由于可用空间,船只必须更小。 angle === -360
和angle === 360
分别更改为angle <= -360
和angle >= 360
,因为您的角度可以跳过360
然后该值不会被重置。这种方式更安全。width:100%;
和height:100%
添加到<html>
,因此<canvas>
正确覆盖了整个页面。结果是一台航行平稳的太空机:
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>