Ich denke, dass die beste Antwort hier eine umfassende Überarbeitung des Matter.Resolver
Moduls wäre, um eine vorausschauende Vermeidung physischer Konflikte zwischen Körpern zu implementieren. Alles andere scheitert unter bestimmten Umständen garantiert . Davon abgesehen gibt es zwei "Lösungen", die in Wirklichkeit nur Teillösungen sind. Sie sind unten aufgeführt.
Lösung 1 (Update)
Diese Lösung hat mehrere Vorteile:
- Es ist prägnanter als Lösung 2
- Es erzeugt einen geringeren Rechenaufwand als Lösung 2
- Das Ziehverhalten wird nicht wie in Lösung 2 unterbrochen
- Es kann zerstörungsfrei mit Lösung 2 kombiniert werden
Die Idee hinter diesem Ansatz ist es, das Paradoxon dessen zu lösen, was passiert, " wenn eine unaufhaltsame Kraft auf ein unbewegliches Objekt trifft ", indem die Kraft stoppbar gemacht wird. Dies wird durch das ermöglicht Matter.Event
beforeUpdate
, wodurch die absolute Geschwindigkeit und der absolute Impuls (oder besser gesagt positionImpulse
, der nicht wirklich physikalische Impuls) in jeder Richtung auf benutzerdefinierte Grenzen beschränkt werden können.
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>
Im Beispiel beschränke ich das velocity
und positionImpulse
in x
und y
auf eine maximale Größe von 25.0
. Das Ergebnis ist unten dargestellt
Wie Sie sehen können, ist es möglich, die Körper ziemlich heftig zu ziehen, und sie werden sich nicht gegenseitig durchdringen. Dies unterscheidet diesen Ansatz von anderen: Die meisten anderen potenziellen Lösungen schlagen fehl, wenn der Benutzer beim Ziehen ausreichend gewalttätig ist.
Das einzige Manko, auf das ich bei dieser Methode gestoßen bin, ist, dass es möglich ist, einen nicht statischen Körper zu verwenden, um einen anderen nicht statischen Körper so hart zu treffen, dass er eine ausreichende Geschwindigkeit erreicht, bis das Resolver
Modul die Kollision nicht erkennt und die Kollision zulässt zweiter Körper, der durch andere Körper geht. (Im Beispiel für statische Reibung ist die erforderliche Geschwindigkeit ungefähr vorhanden 50.0
. Ich habe dies nur einmal erfolgreich durchgeführt, und folglich habe ich keine Animation, die dies darstellt.)
Lösung 2
Dies ist eine zusätzliche Lösung, eine faire Warnung: Es ist nicht einfach.
Im Großen und Ganzen funktioniert dies, um zu überprüfen, ob der gezogene Körper dragBody
mit einem statischen Körper kollidiert ist und ob sich die Maus seitdem zu weit bewegt hat, ohne zu dragBody
folgen. Wenn festgestellt wird, dass der Abstand zwischen der Maus und der Maus dragBody
zu groß geworden ist, wird der Ereignis-Listener entfernt und durch eine andere Mausbewegungsfunktion ersetzt . Diese Funktion prüft, ob die Maus in eine bestimmte Nähe des Körperzentrums zurückgekehrt ist. Leider konnte ich die eingebaute Methode nicht richtig zum Laufen bringen, so dass ich sie direkt einbinden musste (jemand, der mehr über Kenntnisse in Javascript verfügt als ich, muss dies herausfinden). Wenn ein Ereignis erkannt wird, wechselt es zurück zum normalen Listener.Matter.js
mouse.mousemove
mouse.element
mousemove()
Matter.Mouse._getRelativeMousePosition()
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>
Nach dem Anwenden des Ereignis-Listener-Schaltschemas verhalten sich die Körper nun ähnlicher
Ich habe dies ziemlich gründlich getestet , kann aber nicht garantieren, dass es in jedem Fall funktioniert. Es ist auch zu beachten, dass das mouseup
Ereignis erst erkannt wird, wenn sich die Maus zum Zeitpunkt des Auftretens innerhalb der Zeichenfläche befindet. Dies gilt jedoch für jede Matter.js- mouseup
Erkennung, sodass ich nicht versucht habe, dies zu beheben.
Wenn die Geschwindigkeit ausreichend groß ist, Resolver
keine Kollision erkannt werden kann und der Körper keine vorbeugende Verhinderung dieses Geschmacks von physischen Konflikten aufweist, kann er wie hier gezeigt passieren.
Dies kann durch Kombination mit Lösung 1 behoben werden .
Ein letzter Hinweis hier: Es ist möglich, dies nur auf bestimmte Wechselwirkungen anzuwenden (z. B. zwischen einem statischen und einem nicht statischen Körper). Dies wird durch Ändern erreicht
if (bodies[i] != dragBody && Matter.SAT.collides(bodies[i], dragBody).collided == true) {
//...
}
zu (für zB statische Körper)
if (bodies[i].isStatic == true && bodies[i] != dragBody &&
Matter.SAT.collides(bodies[i], dragBody).collided == true) {
//...
}
Fehlgeschlagene Lösungen
Falls zukünftige Benutzer auf diese Frage stoßen und beide Lösungen für ihren Anwendungsfall als unzureichend empfinden, sind hier einige der Lösungen aufgeführt, die ich versucht habe und die dies nicht getan haben funktioniert haben. Eine Art Leitfaden für das, was man nicht tun sollte.
- Berufung
mouse.mouseup
Direkt : Objekt sofort gelöscht.
- Anrufen
mouse.mouseup
überEvent.trigger(mouseConstraint, 'mouseup', {mouse: mouse})
: überschrieben von Engine.update
, Verhalten unverändert.
- Das gezogene Objekt vorübergehend statisch machen: Objekt wird gelöscht, wenn zu nicht statisch zurückgekehrt wird (ob über
Matter.Body.setStatic(body, false)
oder body.isStatic = false
).
- Einstellen der Kraft auf
(0,0)
viasetForce
bei Annäherung an einen Konflikt: Objekt kann immer noch passieren, müsste implementiert werden, Resolver
um tatsächlich zu funktionieren.
- Wechseln
mouse.element
zu einer anderen Zeichenfläche über setElement()
oder durch mouse.element
direktes Mutieren : Objekt sofort gelöscht.
- Zurücksetzen des Objekts auf die letzte 'gültige' Position: Ermöglicht weiterhin den Durchgang,
- Verhaltensänderung über
collisionStart
: Inkonsistente Kollisionserkennung ermöglicht weiterhin den Durchgang mit dieser Methode