Matter.js - 如何将移动和鼠标交互限制在单个轴上?

Chr*_*rey 5 javascript matter.js

使用 Matter.js,如何将身体的运动限制在单个轴上(即仅 X 轴)?我希望能够做到这样一个身体只能在设定的边界内水平拖动。

我正在尝试创建一个算盘,并允许用户沿固定条轻弹珠子。把这一切都摆出来很好,但我一辈子都无法弄清楚如何限制这样的运动。

我试过在珠子的上方和下方设置一个固定的主体,这在一定程度上起作用,但是当我添加 MouseConstraint 时,拖动行为完全忽略了这一点,您可以直接拖动它。

我还尝试使用 beforeUpdate 事件在每个珠子被绘制之前“重置”它的 Y 值(使用 Body.setPosition)。这种工作在某种程度上是有效的,但是 Y 值会回到对齐方式,如果有意义的话,我宁愿它根本不偏离。

有没有更好的方法来完成这项任务?也许可以告诉一个身体“粘”到另一个身体上吗?这样我就可以创建我的酒吧并将珠子粘在上面。

ggo*_*len 0

setPosition一种选择是在事件处理程序中使用来afterUpdate强制主体到达一个轴上的某个位置。

\n

这是概念证明(请原谅硬编码值):

\n

\r\n
\r\n
const canvas = document.querySelector("canvas");\nconst ctx = canvas.getContext("2d");\nctx.lineWidth = 2;\nctx.fillStyle = "white";\nconst engine = Matter.Engine.create();\nengine.gravity.y = 0;\nconst verticalBox = {\n  w: 40,\n  h: 20,\n  pinX: 150,\n  body: Matter.Bodies.rectangle(100, 100, 40, 20, {\n    frictionAir: 0.6,\n    density: 1,\n    restitution: 1,\n  }),\n  elem: document.querySelector(".vertical-box"),\n};\nconst horizontalBox = {\n  w: 40,\n  h: 20,\n  pinY: 150,\n  body: Matter.Bodies.rectangle(100, 100, 40, 20, {\n    frictionAir: 0.6,\n    density: 1,\n    restitution: 1,\n  }),\n  elem: document.querySelector(".horizontal-box"),\n};\nconst walls = [\n  Matter.Bodies.rectangle(200, -200, 400, 400, {isStatic: true}),\n  Matter.Bodies.rectangle(-200, 200, 400, 400, {isStatic: true}),\n  Matter.Bodies.rectangle(200, 500, 400, 400, {isStatic: true}),\n  Matter.Bodies.rectangle(500, 200, 400, 400, {isStatic: true}),\n];\nconst boxes = [...Array(100)].map(() => Matter.Bodies.rectangle(\n  Math.random() * 400,\n  Math.random() * 400,\n  Math.random() * 20 + 10,\n  Math.random() * 20 + 10,\n  {\n    angle: Math.random() * Math.PI,\n    frictionAir: 0.05,\n    restitution: 0.6,\n    density: 0.2,\n  }\n));\nconst repositionDOMBox = box => {\n  const {x, y} = box.body.position;\n  box.elem.style.top = `${y - box.h / 2}px`;\n  box.elem.style.left = `${x - box.w / 2}px`;\n  box.elem.style.transform = `rotate(${box.body.angle}rad)`;\n};\nconst renderBoxOnCanvas = body => {\n  ctx.beginPath();\n  body.vertices.forEach(e => ctx.lineTo(e.x, e.y));\n  ctx.closePath();\n  ctx.fill();\n  ctx.stroke();\n};\nconst mouseConstraint = Matter.MouseConstraint.create(engine, {\n  element: document.body,\n});\nMatter.Composite.add(engine.world, [\n  mouseConstraint,\n  horizontalBox.body,\n  verticalBox.body,\n  ...walls,\n  ...boxes,\n]);\nMatter.Events.on(engine, "afterUpdate", () => {\n  const {y} = verticalBox.body.position;\n  Matter.Body.setPosition(\n    verticalBox.body,\n    {\n      x: verticalBox.pinX,\n\n      // optionally clamp to walls\n      y: y > 290 ? 290 : y < 0 ? 0 : y,\n      //y,\n    }\n  );\n  const {x} = horizontalBox.body.position;\n  Matter.Body.setPosition(\n    horizontalBox.body,\n    {\n      // optionally clamp to walls\n      x: x > 280 ? 280 : x < 0 ? 0 : x,\n      //x,\n\n      y: horizontalBox.pinY,\n    }\n  );\n\n  // optionally prevent rotation\n  Matter.Body.setAngle(verticalBox.body, 0);\n  Matter.Body.setAngle(horizontalBox.body, 0);\n});\n(function rerender() {\n  requestAnimationFrame(rerender);\n  Matter.Engine.update(engine);\n  ctx.clearRect(0, 0, canvas.width, canvas.height);\n  ctx.moveTo(0, 150);\n  ctx.lineTo(canvas.width, 150);\n  ctx.stroke();\n  ctx.moveTo(150, 0);\n  ctx.lineTo(150, canvas.height);\n  ctx.stroke();\n  repositionDOMBox(verticalBox);\n  repositionDOMBox(horizontalBox);\n  boxes.forEach(renderBoxOnCanvas);\n})();
Run Code Online (Sandbox Code Playgroud)\r\n
html,\nbody {\n  position: relative;\n  height: 100%;\n  margin: 0;\n}\n.box {\n  border: 2px solid black;\n  position: absolute;\n  height: 20px;\n  width: 40px;\n  user-select: none;\n  color: white;\n  text-align: center;\n}\n.vertical-box {\n  background: #191;\n  cursor: row-resize;\n}\n.horizontal-box {\n  background: #e71;\n  cursor: col-resize;\n}\ncanvas {\n  border: 3px solid black;\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: -1;\n}
Run Code Online (Sandbox Code Playgroud)\r\n
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js"></script>\n<div class="box vertical-box">\xe2\x86\x95</div>\n<div class="box horizontal-box">\xe2\x86\x94</div>\n<canvas width="300" height="300"></canvas>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

不过,根据经验,使用setPosition覆盖物理引擎会产生一些恼人的边缘情况,通常需要处理这些情况以保持真实感,因此这在 MJS 中是危险的领域。如何处理这些情况是特定于用例的。

\n

在此示例中,我采取了额外的步骤来防止轴约束主体的角度发生变化,这使得两个盒子之间的碰撞有点不现实。我还将轴约束的盒子夹在墙上,以防止它们被拖离画布。

\n

约束主体碰撞的一种解决方法是使用静态核心,在每个刻度上跟踪轴约束主体以防止重叠,如此处所述

\n

也就是说,在许多情况下,轴约束主体可能不需要处理固定到另一个轴的主体的碰撞,如算盘应用程序中那样。虽然,对于算盘应用程序,您可能需要处理同一轴上的珠子在用鼠标拖动时会相互跳跃;这可以通过在不允许物体移动且仅与拖动的物体碰撞的区域上临时创建大型不可见静态物体来解决。

\n

使用圆形物体并避免鼠标交互(众所周知,鼠标交互是出了名的小问题)也可以帮助缓解问题。

\n

也可以看看:

\n\n