我正在尝试使用 2D 物理引擎来模拟边缘带有销钉的旋转圆盘。销钉可能会与其他物体碰撞,从而阻止圆盘进一步旋转,或者将其他物体推开。光盘本身不会与任何东西碰撞(因为它在另一个平面上)。
我一直在努力在 Matter.js 中实现这一目标,但我以前从未使用过它。
首先,我将圆盘的
collisionFilter = { .mask: 0 }
以及销钉和驱动对象设置为 collisionFilter: { .category: 1, .mask: 1 }
。圆盘不应与任何物体碰撞,并且销轴和从动物体应碰撞。
然后我尝试了两种形成圆盘和销钉的方法:
创建一个由两部分组成的复合体。然而,根据本期2017,“零件不能有自己的
collisionFilter
,只有父母受到尊重。”
创建两个独立的实体,它们之间有刚性约束,以固定圆盘和销钉的相对位置。这成功地达到了预期的效果,当圆盘旋转时,销钉随之移动(反之亦然)。
但是,这里的问题是销钉仍然可以绕其中心旋转。如果引脚是方形的,则最明显。
(另外我不确定这个方法是否可以让我控制圆盘的转动惯量?)
这可以实现吗?法学硕士试图让我设置一个
beforeUpdate
事件来抵消销钉的角速度。但我的预感是我正在做的事情不应该那么复杂?
几个小时后,我无法确定如何使用 Matter.js 来做到这一点。我切换到 Planck.js 它解决了我使用 Matter.js 的两个问题:
我可以在构成单个主体的固定装置(物理部件)上定义不同的碰撞属性。
我可以使用不同类型的关节,例如
WeldJoint
和 RevoluteJoint
,这使得对身体之间的复杂关系进行建模变得更容易。
在以下可以在Playground上运行的示例中,我创建了三个圆盘和销钉主体,如我的问题中所述,以模拟密码锁的轮组。尸体同心堆叠。每个光盘都有一个销钉,与下一张光盘上的苍蝇(基本上是另一个销钉)连接以驱动它。
planck.testbed('WheelPack', function(testbed) {
let pl = planck, Vec2 = pl.Vec2, Transform = pl.Transform;
let world = new pl.World(Vec2(0, 0)); // no gravity
// unsure how to disable the mouse in testbed mode
testbed.mouseForce = 0.01;
let discRadius = 2;
let pinSize = 0.2;
let flySize = 0.2;
function createWheel(position, angle, index) {
let discFixture = pl.Circle(discRadius);
let pinFixture = pl.Box(pinSize, pinSize, Vec2(discRadius - pinSize, 0));
let flyFixture = pl.Box(flySize, flySize, Vec2(-(discRadius - flySize), 0));
let wheel = world.createBody({
type: 'dynamic',
position: position,
angle: angle,
// prevent wheels from spinning when force is removed
angularDamping: 20,
});
wheel.createFixture(discFixture, {
density: 1,
filterMaskBits: 0,
});
wheel.createFixture(pinFixture, {
density: 1,
filterCategoryBits: 1 << index,
filterMaskBits: 1 << index,
});
wheel.createFixture(flyFixture, {
density: 1,
filterCategoryBits: 1 << (index + 1),
filterMaskBits: 1 << (index + 1),
});
// Add a revolute joint to fix wheel in place
world.createJoint(pl.RevoluteJoint({
enableMotor: false
}, world.createBody(), wheel, wheel.getPosition()));
return wheel;
}
let wheel1 = createWheel(Vec2(0, 0), 0, 1);
let wheel2 = createWheel(Vec2(0, 0), Math.PI / 3, 2); // offset by 60 degrees
let wheel3 = createWheel(Vec2(0, 0), -Math.PI / 3, 3); // offset by -60 degrees
testbed.step = function() {
let format = (angle) => ((100 + 50 * angle / Math.PI) % 100).toFixed(1)
testbed.status('wheel 1', format(wheel1.getAngle()));
testbed.status('wheel 2', format(wheel2.getAngle()));
testbed.status('wheel 3', format(wheel3.getAngle()));
if (testbed.activeKeys.right) {
wheel3.setAngularVelocity(-2);
} else if (testbed.activeKeys.left) {
wheel3.setAngularVelocity(2);
} else {
wheel3.setAngularVelocity(0);
}
};
testbed.info('Use arrow keys to rotate wheel 3.');
return world;
})
一些补充意见:
调整主体的
angularDamping
来控制销钉不再被驱动后圆盘减速的速度。
我使用
filterMaskBits: 0
来防止与给定夹具(光盘)发生任何碰撞。
我使用
filterCategoryBits
和 filterMaskBits
来控制哪些对象由给定引脚驱动,就像这些对象位于不同的平面上一样。
轮体上的
RevoluteJoint
使其能够像风车一样绕其中心点旋转,而不会旋转到太空中。