【问题标题】:Making movement smoother - JS Canvas Game development让动作更流畅 - JS Canvas 游戏开发
【发布时间】:2017-07-04 12:56:48
【问题描述】:

我正在尝试使用 ES6 和 Canvas 进行游戏开发。在这个例子中,我有一个宇宙飞船的图像,我用左右箭头键旋转它,然后用上键向前移动它。 如果您查看旋转和向前移动,您会发现它是滞后的。有没有办法让它更流畅?

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);
  }

感谢您的宝贵时间。

【问题讨论】:

  • 您要做的不是直接操纵船的位置和方向,而是操纵它的速度和角动量,并让这些属性在主循环中操纵船。

标签: javascript ecmascript-6 game-physics


【解决方案1】:

主要原因是您为每个输入使用了专用函数:
spaceship.rotateLeft()spaceship.moveForward()spaceship.rotateRight()

虽然这看起来非常好OOP 明智,但结果是每次调用window.onkeydown 处理程序时,它都会中断/重置当前执行,因为键处理程序与帧函数不同步。
框架函数(理论上)以固定间隔运行。但是 key-handler 不遵循相同的模式,因为当你按下一个键时它会被触发。所以这两者有点互相矛盾。
(如果我错了,请任何人纠正我,这不是 100% 确定的。)

无论如何,您可以通过将键处理程序移动到spaceship.js 来解决它,并在键处理程序中为每个键设置一个booleantrue,然后添加一个@ 987654330@ 处理程序再次将它们设置回 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 代码片段中,我不得不稍微降低它们,因为船必须由于可用空间较小。
  • 在左右键计算中,我将angle === -360angle === 360 分别更改为angle &lt;= -360angle &gt;= 360,因为您的角度可能会跳过360,然后该值不会被重置。这种方式更安全。
  • 最后我更改的主要内容是将width:100%;height:100% 添加到&lt;html&gt;,因此&lt;canvas&gt; 可以正确覆盖整个页面。
  • 我做了一些其他更改,主要是为了让我自己更好地了解您的代码。如果您喜欢这种风格,请使用它,否则请忽略它。

结果是一台平稳航行的太空机:

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

【讨论】:

  • 确实是时髦的回答:D 非常感谢,让我给你喝啤酒
猜你喜欢
  • 1970-01-01
  • 2020-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多