私は物理ベースのゲームにMatterJを使用していて、マウスが他のボディを介してボディを強制的にドラッグすることを防ぐ問題の解決策を見つけていません。ボディを別のボディにドラッグすると、ドラッグされているボディは、他のボディに押し込まれ、他のボディを通過します。彼らが交差するのを防ぐ信頼できる方法を探しています。マウスでボディを選択し、別のボディを介してそれを強制しようとすることにより、MatterJSデモでこの効果を観察できます。これが典型的な例です:
https://brm.io/matter-js/demo/#staticFriction
残念ながら、これはドラッグアンドドロップに応じてゲームやシミュレーションを中断します。衝突が発生したときにマウスの制約を解除する、制約の剛性を下げるなど、さまざまな解決策を試しましたが、確実に機能するものはありませんでした。
どんな提案も歓迎します!
ここでの最良の答えは、_Matter.Resolver
_モジュールを大幅に見直して、ボディ間の物理的な競合を予測的に回避することです。それ以外のものは、特定の状況下で失敗することが保証されます。ここで言われているのは、実際には単なる部分的な解決策である2つの「解決策」です。それらの概要を以下に示します。
このソリューションにはいくつかの利点があります。
このアプローチの背後にあるアイデアは、「止められない力が動かないオブジェクトに出会うとき」力を止めることができるようにすることによって、起こることのパラドックスを解決することです。これは_Matter.Event
_ beforeUpdate
によって有効になり、各方向の絶対速度と衝撃(または実際には物理的な衝撃ではないpositionImpulse
)を内に制限できますユーザー定義の境界。
_window.addEventListener('load', function() {
var canvas = document.getElementById('world')
var mouseNull = document.getElementById('mouseNull')
var engine = Matter.Engine.create();
var world = engine.world;
var render = Matter.Render.create({ element: document.body, canvas: canvas,
engine: engine, options: { width: 800, height: 800,
background: 'transparent',showVelocity: true }});
var body = Matter.Bodies.rectangle(400, 500, 200, 60, { isStatic: true}),
size = 50, counter = -1;
var stack = Matter.Composites.stack(350, 470 - 6 * size, 1, 6,
0, 0, function(x, y) {
return Matter.Bodies.rectangle(x, y, size * 2, size, {
slop: 0, friction: 1, frictionStatic: Infinity });
});
Matter.World.add(world, [ body, stack,
Matter.Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Matter.Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
Matter.Bodies.rectangle(800, 300, 50, 600, { isStatic: true }),
Matter.Bodies.rectangle(0, 300, 50, 600, { isStatic: true })
]);
Matter.Events.on(engine, 'beforeUpdate', function(event) {
counter += 0.014;
if (counter < 0) { return; }
var px = 400 + 100 * Math.sin(counter);
Matter.Body.setVelocity(body, { x: px - body.position.x, y: 0 });
Matter.Body.setPosition(body, { x: px, y: body.position.y });
if (dragBody != null) {
if (dragBody.velocity.x > 25.0) {
Matter.Body.setVelocity(dragBody, {x: 25, y: dragBody.velocity.y });
}
if (dragBody.velocity.y > 25.0) {
Matter.Body.setVelocity(dragBody, {x: dragBody.velocity.x, y: 25 });
}
if (dragBody.positionImpulse.x > 25.0) {
dragBody.positionImpulse.x = 25.0;
}
if (dragBody.positionImpulse.y > 25.0) {
dragBody.positionImpulse.y = 25.0;
}
}
});
var mouse = Matter.Mouse.create(render.canvas),
mouseConstraint = Matter.MouseConstraint.create(engine, { mouse: mouse,
constraint: { stiffness: 0.1, render: { visible: false }}});
var dragBody = null
Matter.Events.on(mouseConstraint, 'startdrag', function(event) {
dragBody = event.body;
});
Matter.World.add(world, mouseConstraint);
render.mouse = mouse;
Matter.Engine.run(engine);
Matter.Render.run(render);
});
_
_<canvas id="world"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.10.0/matter.js"></script>
_
この例では、velocity
とpositionImpulse
のx
とy
を_25.0
_の最大値に制限しています。結果を以下に示します
ご覧のように、ボディをドラッグするのは非常に暴力的で、ボディを互いに通過することはできません。これが、このアプローチを他のアプローチと区別するものです。他のほとんどの潜在的なソリューションは、ユーザーがドラッグするのに十分に暴力的である場合に失敗します。
この方法で遭遇した唯一の欠点は、非静的ボディを使用して、Resolver
モジュールが失敗するポイントに十分な速度を与えるのに十分なほど強く、別の非静的ボディに当たることが可能であることです。衝突を検出し、2番目のボディが他のボディを通過できるようにします。 (静止摩擦の例では、必要な速度は_50.0
_あたりです。これを成功させることができたのは1回だけであり、そのため、それを表すアニメーションはありません)。
これは追加のソリューションですが、公正な警告です:簡単ではありません。
大まかに言えば、これが機能する方法は、ドラッグされているボディdragBody
が静的なボディと衝突しているかどうか、およびマウスがdragBody
を追従せずに移動しすぎていないかどうかを確認することです。マウスとdragBody
の間隔が大きくなりすぎたことを検出すると、 _Matter.js
_ _mouse.mousemove
_イベントリスナーを_mouse.element
_から削除して置き換えます。別のマウスムーブ関数mousemove()
を使用してください。この関数は、マウスが体の中心の特定の近接範囲内に戻ったかどうかをチェックします。残念ながら、組み込みのMatter.Mouse._getRelativeMousePosition()
メソッドを適切に機能させることができなかったため、直接含める必要がありました(Javascriptで私より知識のある人がそれを理解する必要があります)。最後に、mouseup
イベントが検出されると、通常のmousemove
リスナーに切り替わります。
_window.addEventListener('load', function() {
var canvas = document.getElementById('world')
var mouseNull = document.getElementById('mouseNull')
var engine = Matter.Engine.create();
var world = engine.world;
var render = Matter.Render.create({ element: document.body, canvas: canvas,
engine: engine, options: { width: 800, height: 800,
background: 'transparent',showVelocity: true }});
var body = Matter.Bodies.rectangle(400, 500, 200, 60, { isStatic: true}),
size = 50, counter = -1;
var stack = Matter.Composites.stack(350, 470 - 6 * size, 1, 6,
0, 0, function(x, y) {
return Matter.Bodies.rectangle(x, y, size * 2, size, {
slop: 0.5, friction: 1, frictionStatic: Infinity });
});
Matter.World.add(world, [ body, stack,
Matter.Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Matter.Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
Matter.Bodies.rectangle(800, 300, 50, 600, { isStatic: true }),
Matter.Bodies.rectangle(0, 300, 50, 600, { isStatic: true })
]);
Matter.Events.on(engine, 'beforeUpdate', function(event) {
counter += 0.014;
if (counter < 0) { return; }
var px = 400 + 100 * Math.sin(counter);
Matter.Body.setVelocity(body, { x: px - body.position.x, y: 0 });
Matter.Body.setPosition(body, { x: px, y: body.position.y });
});
var mouse = Matter.Mouse.create(render.canvas),
mouseConstraint = Matter.MouseConstraint.create(engine, { mouse: mouse,
constraint: { stiffness: 0.2, render: { visible: false }}});
var dragBody, overshoot = 0.0, threshold = 50.0, loc, dloc, offset,
bodies = Matter.Composite.allBodies(world), moveOn = true;
getMousePosition = function(event) {
var element = mouse.element, pixelRatio = mouse.pixelRatio,
elementBounds = element.getBoundingClientRect(),
rootNode = (document.documentElement || document.body.parentNode ||
document.body),
scrollX = (window.pageXOffset !== undefined) ? window.pageXOffset :
rootNode.scrollLeft,
scrollY = (window.pageYOffset !== undefined) ? window.pageYOffset :
rootNode.scrollTop,
touches = event.changedTouches, x, y;
if (touches) {
x = touches[0].pageX - elementBounds.left - scrollX;
y = touches[0].pageY - elementBounds.top - scrollY;
} else {
x = event.pageX - elementBounds.left - scrollX;
y = event.pageY - elementBounds.top - scrollY;
}
return {
x: x / (element.clientWidth / (element.width || element.clientWidth) *
pixelRatio) * mouse.scale.x + mouse.offset.x,
y: y / (element.clientHeight / (element.height || element.clientHeight) *
pixelRatio) * mouse.scale.y + mouse.offset.y
};
};
mousemove = function() {
loc = getMousePosition(event);
dloc = dragBody.position;
overshoot = ((loc.x - dloc.x)**2 + (loc.y - dloc.y)**2)**0.5 - offset;
if (overshoot < threshold) {
mouse.element.removeEventListener("mousemove", mousemove);
mouse.element.addEventListener("mousemove", mouse.mousemove);
moveOn = true;
}
}
Matter.Events.on(mouseConstraint, 'startdrag', function(event) {
dragBody = event.body;
loc = mouse.position;
dloc = dragBody.position;
offset = ((loc.x - dloc.x)**2 + (loc.y - dloc.y)**2)**0.5;
Matter.Events.on(mouseConstraint, 'mousemove', function(event) {
loc = mouse.position;
dloc = dragBody.position;
for (var i = 0; i < bodies.length; i++) {
overshoot = ((loc.x - dloc.x)**2 + (loc.y - dloc.y)**2)**0.5 - offset;
if (bodies[i] != dragBody &&
Matter.SAT.collides(bodies[i], dragBody).collided == true) {
if (overshoot > threshold) {
if (moveOn == true) {
mouse.element.removeEventListener("mousemove", mouse.mousemove);
mouse.element.addEventListener("mousemove", mousemove);
moveOn = false;
}
}
}
}
});
});
Matter.Events.on(mouseConstraint, 'mouseup', function(event) {
if (moveOn == false){
mouse.element.removeEventListener("mousemove", mousemove);
mouse.element.addEventListener("mousemove", mouse.mousemove);
moveOn = true;
}
});
Matter.Events.on(mouseConstraint, 'enddrag', function(event) {
overshoot = 0.0;
Matter.Events.off(mouseConstraint, 'mousemove');
});
Matter.World.add(world, mouseConstraint);
render.mouse = mouse;
Matter.Engine.run(engine);
Matter.Render.run(render);
});
_
_<canvas id="world"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.10.0/matter.js"></script>
_
イベントリスナーの切り替えスキームを適用した後、ボディはこのように動作します
私はこれをかなり完全にテストしましたが、すべての場合で動作することを保証できません。 mouseup
イベントは、発生時にマウスがキャンバス内にない限り検出されないことにも注意してください。ただし、これはMatter.jsのmouseup
検出に当てはまるため、実行しませんでした。修正してください。
速度が十分に大きい場合、Resolver
は衝突の検出に失敗します。また、この物理的な衝突のフレーバーを予測的に防止できないため、ここに示すようにボディが通過できます。
これは、Solution 1と組み合わせることで解決できます。
ここで最後の注意点として、これを特定の相互作用のみに適用することが可能です(例:静的ボディと非静的ボディの間の相互作用)。これを行うには、
_if (bodies[i] != dragBody && Matter.SAT.collides(bodies[i], dragBody).collided == true) {
//...
}
_
へ(例:静的ボディ)
_if (bodies[i].isStatic == true && bodies[i] != dragBody &&
Matter.SAT.collides(bodies[i], dragBody).collided == true) {
//...
}
_
将来のユーザーがこの質問に遭遇し、両方の解決策がユースケースには不十分であるとわかった場合、私が試した解決策の一部を以下に示します機能しません。してはいけないことのための種類のガイド。
mouse.mouseup
_を直接呼び出す:オブジェクトはすぐに削除されます。Event.trigger(mouseConstraint, 'mouseup', {mouse: mouse})
を介した_mouse.mouseup
_の呼び出し:_Engine.update
_によってオーバーライドされ、動作は変更されません。Matter.Body.setStatic(body, false)
または_body.isStatic = false
_を介して)。setForce
を介して_(0,0)
_に力を設定すると、オブジェクトはまだ通過できますが、実際に機能するにはResolver
に実装する必要があります。mouse.element
_をsetElement()
を介して、または_mouse.element
_を直接変更して別のキャンバスに変更する:オブジェクトはすぐに削除されます。collisionStart
を介して動作を変更:一貫性のない衝突検出は、このメソッドでのパススルーを許可しますドラッグ時の衝突を制御するには、 衝突フィルター と イベント を使用する必要があります。
デフォルトでボディを作成 衝突フィルタマスク0x0001
。 catch startdrag
およびenddrag
イベントを追加し、一時的に衝突を回避するために別の本体 衝突フィルターカテゴリ を設定します。
Matter.Events.on(mouseConstraint, 'startdrag', function(event) {
event.body.collisionFilter.category = 0x0008; // move body to new category to avoid collision
});
Matter.Events.on(mouseConstraint, 'enddrag', function(event) {
event.body.collisionFilter.category = 0x0001; // return body to default category to activate collision
});
window.addEventListener('load', function () {
//Fetch our canvas
var canvas = document.getElementById('world');
//Setup Matter JS
var engine = Matter.Engine.create();
var world = engine.world;
var render = Matter.Render.create({
canvas: canvas,
engine: engine,
options: {
width: 800,
height: 800,
background: 'transparent',
wireframes: false,
showAngleIndicator: false
}
});
//Add a ball
const size = 50;
const stack = Matter.Composites.stack(350, 470 - 6 * size, 1, 6, 0, 0, (x, y) => {
return Matter.Bodies.rectangle(x, y, size * 2, size, {
collisionFilter: {
mask: 0x0001,
},
slop: 0.5,
friction: 1,
frictionStatic: Infinity,
});
});
Matter.World.add(engine.world, stack);
//Add a floor
var floor = Matter.Bodies.rectangle(250, 520, 500, 40, {
isStatic: true, //An immovable object
render: {
visible: false
}
});
Matter.World.add(world, floor);
//Make interactive
var mouseConstraint = Matter.MouseConstraint.create(engine, { //Create Constraint
element: canvas,
constraint: {
render: {
visible: false
},
stiffness: 0.8
}
});
Matter.World.add(world, mouseConstraint);
// add events to listen drag
Matter.Events.on(mouseConstraint, 'startdrag', function (event) {
event.body.collisionFilter.category = 0x0008; // move body to new category to avoid collision
});
Matter.Events.on(mouseConstraint, 'enddrag', function (event) {
event.body.collisionFilter.category = 0x0001; // return body to default category to activate collision
});
//Start the engine
Matter.Engine.run(engine);
Matter.Render.run(render);
});
<canvas id="world"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.10.0/matter.min.js"></script>
私は別の方法で機能を管理したでしょう: