让运动更流畅 - JS Canvas Game开发

时间:2017-07-04 12:56:48

标签: javascript ecmascript-6 game-physics

我正在试验ES6&用于游戏开发的画布。在这个例子中,我有一个宇宙飞船的图像,我用左右箭头键旋转它,并用上键向前移动它。 如果你观察旋转和前进,你会发现它是滞后的。有没有办法让它更顺畅?

Plunker

“动画”和动作的代码:(也在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);
  }

感谢您的时间。

1 个答案:

答案 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)和不必要的括号等)。如果你确实需要它们,你当然可以使用它们,但我想尽量保持计算,以消除口吃的任何原因。 rotateDeltaforwardDelta现在有很好的圆值 在Plunker中,它们是710,在SO代码段中我不得不降低它们,因为由于可用空间,船只必须更小。
  • 在左右键计算中,我将angle === -360angle === 360分别更改为angle <= -360angle >= 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>
plunker:https://plnkr.co/edit/nKAyweLV4d0hmmIRzxG4?p=preview