如何在Matter.js中用动画将物体移动到另一个位置

3

我该如何使用动画将圆形物体移动到另一个位置?当我使用translate()时,对象会立即消失并在新位置重新出现。

我正在开发一个简单的游戏。玩家必须避免从上方来的障碍物。他们只能在三个区域内向左或向右移动。我希望在这些区域内实现动画效果。目前,我的代码类似于以下内容:

public move = (entities, {touches, time}) => {
  touches.filter(t => t.type === 'press').forEach(t => {
    const player = entities['player']
    const direction = this.getPlayerMoveDirection(t.event.pageX)
    const nextFieldId = this.getNextFieldIdByDirection(direction)
    if (nextFieldId !== this.fieldId) {
      this.setFieldId(nextFieldId)
      const nextField = this.game.fields[nextFieldId]
      const nextFieldXPosition = nextField.getCenter()
      const newXPosition = direction === 'left' ? (player.body.position.x - nextFieldXPosition) *-1 : nextFieldXPosition - player.body.position.x
      Matter.Body.translate( player.body, {x: newXPosition, y:  0});
      this.playMoveSound()
    }
  })
  return entities
}

这很有效,但精灵没有动画效果。它只是在新位置出现。我想要动画效果来展示移动过程。
我在react-native中使用Matter.js和react-native-game-engine。

2
不仅仅是将力应用于物体,而是将其转化为新的位置。这段代码片段没有上下文,很难提供可运行的代码作为回应,所以我建议分享一个可运行的最小化完整示例(MCVE)。另外,对于简单的物理和碰撞问题,Matter.js可能有些过于复杂了。你可以参考这个项目,它只使用了纯粹的JavaScript:链接 - undefined
1
但是如何使用力量将玩家停在固定位置上呢? - undefined
1
哎呀,我错过了你的评论。如果你想要完全停止,你可以使用摩擦力,在相反方向施加力量,或者可能像创建一个看不见的障碍物这样有点巧妙的方法,我猜 -- 这似乎非常依赖于你想要的效果。再次强调,如果你只是进行左右移动和圆形/矩形碰撞的缓动效果,我不确定Matter.js是否适合在这里使用。当你调用translate并开始覆盖MJS的物理引擎时,就好像买一辆自行车只是为了搬运它一样。 - undefined
1个回答

0

这是一个好问题——在MJS中如何实现goto(body, x, y)函数并不完全明显。

至少有几种不同的方法可用,每种方法都涉及许多潜在的琐碎细节和可用的调整。哪种方法最好似乎严重依赖于您的用例。

一种选择是使用Matter.Body.applyForce(body, position, force)将物体推向目标。请参见Matter.js计算所需力量以了解设置这些调用的典型方式。给定一个目标,您可以通过距离目标来缩放力并应用它一次。这将产生一个随着物体接近其目标而减小的大力。

另一种方法是逐帧施加力。下面的示例就是这样工作的。问题在于,当身体接近目标时,减速和停止身体有点棘手。如果不小心,很容易超过目标,产生可能不想要的弹簧或橡皮筋效果。我检查了距离,确定何时在目标接近目的地时减缓力量。这里还有改进的空间。

无论哪种方式,如果您想旋转身体以指向它前进的方向,则需要更多的工作。我使用了一个基于我的回答 如何逐渐旋转对象以面向一个点? 的手动旋转方法来确定旋转方向,然后使用 Matter.Body.setAngularVelocity(body, velocity) 来通知 MJS 旋转速度。与此处的其他所有内容一样,数字非常敏感,并且需要微调才能正确地使用,因此请将它们视为概念验证。

与其使用applyForce,您可以使用Body.setPositionBody.setVelocity调用手动重新定位移动的物体。这可能不适用于常见情况,因为它会使物体穿过其他物体并忽略大部分相关的物理属性,但对于某些用例,它可能是合适的。

与问题相关的是,如果需要将移动限制在水平轴上,则可以跳过在y轴上施加力(具体来说,在对applyForce的下面的示例中设置y: 0)。

这是一个概念验证,其中包括转向和逐帧推动物体朝向目标点的力。

const {PI} = Math;
const TAU = PI * 2;
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
const ctx = canvas.getContext("2d");
canvas.height = 180;
canvas.width = 400;

const engine = Matter.Engine.create();
engine.gravity.y = 0; // enable top-down
const ship = {
  body: Matter.Bodies.rectangle(
    canvas.width / 2,
    canvas.height / 2,
    20,
    20,
    {
      frictionAir: 0.03,
      density: 0.3,
      friction: 0.8,
    }
  ),
  size: 20,
  destX: canvas.width / 2,
  destY: canvas.height / 2,
  color: "#eaf",
  setDestination(x, y) {
    this.destX = x;
    this.destY = y;
  },
  draw(ctx) {
    ctx.save();
    ctx.translate(this.body.position.x, this.body.position.y);
    ctx.rotate(this.body.angle);
    ctx.lineWidth = 3;
    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.lineTo(this.size / 1.2, 0);
    ctx.stroke();
    ctx.fillStyle = this.color;
    ctx.fillRect(
      this.size / -2,
      this.size / -2,
      this.size,
      this.size
    );
    ctx.strokeRect(
      this.size / -2,
      this.size / -2,
      this.size,
      this.size
    );
    ctx.restore();
  },
};

Matter.Composite.add(engine.world, [ship.body]);

canvas.addEventListener("click", (e) => {
  ship.setDestination(e.offsetX, e.offsetY);
});

(function update() {
  requestAnimationFrame(update);
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  const {x, y} = ship.body.position;
  const dist = Math.sqrt(
    (ship.destX - x) ** 2 + (ship.destY - y) ** 2
  );

  if (dist > 10) {
    const dx = ship.destX - ship.body.position.x;
    const dy = ship.destY - ship.body.position.y;

    let theta = Math.atan2(dy, dx);
    const a =
      ship.body.angle > 0
        ? ((ship.body.angle + TAU) % TAU) - TAU
        : -(((-ship.body.angle + TAU) % TAU) - TAU);
    let diff = a - theta;
    diff = diff > PI ? diff - TAU : diff;
    diff = diff < -PI ? diff + TAU : diff;
    const f = dist < 70 ? Math.min(0.02, dist / 10000) : 0.03;
    Matter.Body.applyForce(
      ship.body,
      {x, y},
      {
        x: Math.cos(theta) * f,
        y: Math.sin(theta) * f,
      }
    );

    if (dist > 15) {
      Matter.Body.setAngularVelocity(ship.body, diff / -8);
    } else {
      Matter.Body.setAngularVelocity(ship.body, 0);
    }
  }

  ship.draw(ctx);
  Matter.Engine.update(engine);
})();
body { 
  margin: 0;
  font-family: monospace;
  display: flex; 
  align-items: center; 
}

html, body { 
  height: 100%; 
}

canvas { 
  background: #eee;
  margin: 1em; 
  border: 4px solid #222; 
}

#instructions {
  transform: rotate(-90deg);
  background: #222;
  color: #fff;
  padding: 2px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js"></script>
<div id="instructions">click to move</div>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接