让动作更流畅 - 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) 和不必要的括号等)。如果你确实需要它们,你当然可以使用它们,但我想让计算尽可能简单,以消除任何导致口吃的原因。 rotateDeltaforwardDelta 现在有很好的整数值。
    在 Plunker 中,它们是 710,在 SO 代码片段中,我不得不将它们降低一点,因为由于可用 space,船必须更小。
  • 在左侧和right-key的计算中,我将angle === -360angle === 360分别更改为angle <= -360angle >= 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