如何在 Matter.js 中将身体移动到另一个位置(带动画)

问题描述 投票:0回答:1

如何用动画将圆形体移动到另一个位置?当我使用

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-game-engine中使用Matter.js。

javascript html game-engine matter.js
1个回答
0
投票

这是一个很好的问题——如何在 MJS 中实现这一点并不完全明显,因为没有

goto(body, x, y)
函数。

至少有几种不同的方法可用,每种方法都涉及许多潜在的繁琐细节和可用的调整。哪个最好似乎在很大程度上取决于您的用例。

一种选择是使用

Matter.Body.applyForce(body, position, force)
推动身体朝目的地前进。请参阅Matter.js 计算所需的力,了解设置这些调用的典型方法。给定一个目的地,您可以根据到目的地的距离来缩放力并应用一次。这会产生很大的力,随着身体接近目的地,该力会减弱。

另一种方法是逐帧逐渐用力。这就是下面的示例的工作原理。问题是,当物体接近目的地时,要减慢和停止它是有点棘手的。如果不小心,很容易超出目标,从而产生可能不理想的弹簧或橡皮筋效果。我检查了距离以确定当目标接近目的地时何时减慢力量。这里还有改进的空间。

无论哪种方式,如果你想旋转身体以指向它前进的方向,都需要做更多的工作。我根据我在旋转物体逐渐面对一个点?中的答案使用了手动旋转方法来找出转向的方向,然后

Matter.Body.setAngularVelocity(body, velocity)
告知MJS转向速度。与这里的其他所有内容一样,这些数字非常敏感,需要进行调整才能正确,因此请将它们视为概念证明。

您可以使用

applyForce
Body.setPosition
调用来手动重新定位移动主体,而不是
Body.setVelocity
。这对于常见情况可能不合适,因为它会导致身体闯入其他身体并忽略其大部分相关的物理属性,但对于某些用例,它可能是合适的。

与问题相关,如果需要,您可以跳过在 y 轴上施加力来限制水平轴的移动(具体来说,在下面的示例中在调用

y: 0
时设置
applyForce
)。

这是自上而下游戏的概念证明,该游戏具有转动和逐帧力,可将身体推向目的地点。

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.20.0/matter.min.js"></script>
<div id="instructions">click to move</div>

© www.soinside.com 2019 - 2024. All rights reserved.