MatterJS ile diğer cisimler aracılığıyla kuvvet sürükleme cisimlerini önleyin


14

Fizik tabanlı bir oyun için MatterJs kullanıyorum ve vücut tarafından diğer bedenlerde zorla sürüklenmesini önlemek için bir çözüm bulamadım. Bir vücudu başka bir vücuda sürüklerseniz, sürüklenen vücut kendisini diğer vücuda girmeye zorlar. Kesişmelerini önlemek için güvenilir bir yol arıyorum. Bu etkiyi herhangi bir MatterJS demosunda, fareyle bir gövde seçip başka bir gövdeden zorlamaya çalışarak gözlemleyebilirsiniz. İşte tipik bir örnek:

resim açıklamasını buraya girin

https://brm.io/matter-js/demo/#staticFriction

Ne yazık ki bu sürükle ve bırak yöntemine bağlı olarak tüm oyunları veya simülasyonları bozuyor. Bir çarpışma meydana geldiğinde fare kısıtlamasını kırma veya kısıtlama sertliğini azaltma gibi çok sayıda çözüm denedim, ancak güvenilir bir şekilde çalışmadı.

Herhangi bir öneri hoş geldiniz!


Zorla sürüklenen ifadeleri anlamıyorum. Yani sürüklenen bedeninizin başka bedenlerden geçmesi mi gerekiyor?
grodzi

Hayır, bu sürüklenen cismin başka bir cisimden geçmesinin önlenmesi gerektiği anlamına gelir.
d13

1
@ d13 Sorunu gösteren bir animasyon ekleyebilir misiniz? İfadeye dayanan bir karışıklık olduğu için ...
Ghost

2
@Ghost eklendi ...
d13

@ d13 Bu işleri daha net yapar ..... bu zor bir şeydir
Ghost

Yanıtlar:


6

Buradaki en iyi cevabın, Matter.Resolverherhangi bir organ arasındaki fiziksel çatışmalardan kaçınmaktan kaçınmak için modüle önemli bir revizyon olacağını düşünüyorum . Bunun dışındaki herhangi bir şeyin belirli koşullar altında başarısız olması garanti edilir. Burada söylenen, gerçekte sadece kısmi çözümler olan iki "çözüm" dür. Aşağıda ana hatlarıyla verilmiştir.


Çözüm 1 (Güncelleme)

Bu çözümün çeşitli avantajları vardır:

  • Çözüm 2'den daha özlü
  • Çözüm 2'den daha küçük bir hesaplama alanı yaratır
  • Sürükleme davranışı Çözüm 2'deki gibi kesintiye uğramaz
  • Çözüm 2 ile tahribatsız olarak birleştirilebilir

Bu yaklaşımın arkasındaki fikir, " durdurulamaz bir güç taşınmaz bir nesneyle buluştuğunda " olan şeyin çelişkisini, gücü durdurulabilir hale getirmektir. Bu, her yönde Matter.Event beforeUpdatemutlak hız ve dürtü (veya daha doğrusu positionImpulse, gerçekten fiziksel dürtü olmayan) tarafından kullanıcı tanımlı sınırlar içinde kısıtlanmasına izin verir.

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>

Örnekte ben kısıtlayan ediyorum velocityve positionImpulseiçinde xve ymaksimum büyüklüğü ile 25.0. Sonuç aşağıda gösterilmiştir

resim açıklamasını buraya girin

Gördüğünüz gibi, bedenleri sürüklerken oldukça şiddetli olmak mümkündür ve bunlar birbirlerinden geçmeyeceklerdir. Bu yaklaşımı diğerlerinden ayıran şey budur: diğer potansiyel çözümlerin çoğu, kullanıcı sürüklemeye yeterince şiddet verdiğinde başarısız olur.

Bu yöntemle karşılaştığım tek eksiklik, Resolvermodülün çarpışmayı algılayamayacağı ve izin vereceği noktaya yeterli hız verecek kadar statik olmayan bir gövdeyi vurmak için statik olmayan bir gövdenin kullanılabilmesidir . diğer bedenlerden geçmek için ikinci beden. (Statik sürtünme örneğinde gerekli hız var, 50.0bunu sadece bir kez başarılı bir şekilde yapmayı başardım ve sonuç olarak bunu gösteren bir animasyonum yok).


Çözüm 2

Bu ek bir çözümdür, ancak adil bir uyarıdır: basit değildir.

Geniş anlamda bu, çalışma şeklinin sürüklenip sürüklenmediğini, dragBodystatik bir gövdeyle çarpışıp çarpışmadığını ve farenin o zamandan beri dragBodytakip etmeden çok ileri hareket edip etmediğini kontrol etmektir . Fare ile ayırmanın dragBodyçok büyük hale geldiğini algılarsa , olay dinleyicisini kaldırır ve farklı bir fareyi kaldırma işleviyle değiştirir . Bu işlev, farenin vücudun merkezine belirli bir yakınlığa dönüp dönmediğini kontrol eder. Ne yazık ki ben düzgün çalışmak için yerleşik yöntem alamadım bu yüzden doğrudan dahil etmek zorunda kaldı (Javascript benden daha bilgili biri bunu anlamak zorunda kalacak). Son olarak, bir olay algılanırsa normal dinleyiciye geri döner .Matter.js mouse.mousemovemouse.elementmousemove()Matter.Mouse._getRelativeMousePosition()mouseupmousemove

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>

Olay dinleyicisi anahtarlama şemasını uyguladıktan sonra, bedenler artık daha çok böyle davranıyor

resim açıklamasını buraya girin

Bunu oldukça kapsamlı bir şekilde test ettim , ancak her durumda çalışacağını garanti edemiyorum. Ayrıca mouseup, fare gerçekleştiğinde tuvalin içinde olmadığı sürece olayın tespit edilmediğine dikkat çeker - ancak bu herhangi bir Matter.js mouseupalgılaması için doğrudur, bu yüzden düzeltmeye çalışmadım.

Hız yeterince büyükse, Resolverherhangi bir çarpışmayı tespit edemez ve bu fiziksel çatışma lezzetinin öngörücü önlenmesinden yoksun olduğu için, burada gösterildiği gibi vücudun geçmesine izin verir.

resim açıklamasını buraya girin

Bu, Çözüm 1 ile birleştirilerek çözülebilir .

Burada son bir not, bunu sadece belirli etkileşimlere (örneğin, statik ve statik olmayan bir gövde arasındaki etkileşimlere) uygulamak mümkündür. Bunu yapmak,

if (bodies[i] != dragBody && Matter.SAT.collides(bodies[i], dragBody).collided == true) {
    //...
}

- (örneğin statik cisimler için)

if (bodies[i].isStatic == true && bodies[i] != dragBody && 
    Matter.SAT.collides(bodies[i], dragBody).collided == true) {
    //...
}

Başarısız çözümler

Durumda ileride kullanıcıların bu soruya rastlamak ve bunların kullanım örneği için yetersiz hem çözümler bulmak, burada yaptım teşebbüs çözümlerin bazılarıdır değil iş. Ne yapmamanız gerektiğine dair bir rehber.

  • mouse.mouseupDoğrudan arama : nesne hemen silindi.
  • mouse.mouseupAracılığıyla arama Event.trigger(mouseConstraint, 'mouseup', {mouse: mouse}): geçersiz kılınan Engine.update, davranış değişmedi.
  • Sürüklenen nesneyi geçici olarak statik yapma: statik olmayana ( Matter.Body.setStatic(body, false)veya aracılığıyla body.isStatic = false) dönüldüğünde nesne silindi .
  • İçin güç ayarlama (0,0)yoluyla setForceçatışma yaklaşırken: nesne hala, geçebilir uygulanacak gerekir Resolveraslında işe.
  • Değişen mouse.elementyoluyla farklı bir tuvaline setElement()ya mutasyona uğratarak mouse.elementdoğrudan: Nesne hemen silinir.
  • Nesneyi 'geçerli' konuma döndürmek: yine de geçişe izin verir,
  • İle davranışı değiştir collisionStart : tutarsız çarpışma tespiti yine de bu yöntemle geçişe izin verir


Katkılarınız için çok teşekkürler! Size ödül verdim çünkü çözümünüz mükemmel olmasa da, kesinlikle yolu işaret ediyor ve bu soruna büyük miktarda düşünce ve zaman koyuyorsunuz - Teşekkürler !! Şimdi bu sorunun MatterJS'de bir özellik boşluğu olduğundan eminim ve umarım bu tartışma gelecekte gerçek bir çözüme katkıda bulunacaktır.
d13

@ d13 Teşekkürler, sorunun sonuçta temel alınan kodda olduğunu kabul ediyorum, ancak bazı çözüm (ler) benzerliği elde edebileceğime sevindim
William Miller

0

Özelliği başka bir şekilde yönetirdim:

  • "Sürükleme" yok (bu nedenle sürükleme noktasının sürüklenen nesneyi kaydırılan nesneyle sürekli hizalaması yok)
  • Fare işaretçisinin konumu, nesnenin izlemesi için yönlendirilmiş bir hız vektörü verir
  • MouseUp'ta hız vektörünüzü sıfırlayın
  • Bırakın madde simülasyonu gerisini halletsin

1
matter.jsZaten sürükleme gövdelerini bu şekilde idare etmiyor mu? dan burada "... sanal bir bahar gibi bu fare yönünde fareye ataşeler sürüklerken ... bahar [vücuda] takılır ve çeker ...."
Hayalet

Sadece hızın ayarlanması sürüklenmeyi önler, sping vücudu başkalarına zorlar.
Mosè Raguzzini

Bu aslında bir çözüme işaret edebilir. Doğru anlarsam, MouseConstraint'teki MatterJS'in kullanılmaması ve farenin konumuna göre vücudun hızının manuel olarak ayarlanması anlamına gelir. Ancak bunun nasıl uygulanacağından tam olarak emin değilim, bu yüzden eğer herkes setPosition veya bir kısıtlama kullanmadan vücudu farenin konumuna nasıl hizalayacağına dair ayrıntılar gönderebilirse, lütfen yapın.
d13

@ d13 hala Resolverçarpışan cisimler hakkında ne yapacağına karar vermek için MatterJS's güveniyor olurdu - bu kod üzerinden adil bir bit baktığımda hala birçok durumda sürükleme izin vermek karar verirdi ..... eğer işe yarayabilir Ayrıca kendi sürümünü uyguladı solveVelocityve solvePositionancak bu noktada hala elle MatterJS doğrudan işlemek istediğiniz şeyi yapıyor ....
Ghost

0

Sürüklendiğinde çarpışmayı kontrol etmek için çarpışma filtresini kullanmanız ve olaylarını .

Varsayılan çarpışma filtresi maskeli gövdeler oluşturun 0x0001. Yakalama startdragve enddragolaylar ekleyin ve çarpışmalardan geçici olarak kaçınmak için farklı vücut çarpışma filtresi kategorisi ayarlayın .

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>


1
Mükemmel demo için çok teşekkürler! Aslında tam tersi bir etki yaratmaya çalışıyorum: Biri diğerine sürüklendiğinde bedenlerin kesişmesini engellemem gerekiyor.
d13

Maalesef sorunu yanlış anladıysam. Cesetlerin kesişmesini önleyerek ne demek istediğinizi açıklayabilir misiniz? Kuvvet uygulandığında diğer nesneler arasında sürüklenmeyi engellemeye mi çalışıyorsunuz?
Temur Tchanukvadze

1
Bu durumda açık bir sorundur ve CCD'yi uygulamak için kodlama yapmadan yapılamaz. Bir göz atın: github.com/liabru/matter-js/issues/5
Temur Tchanukvadze

0

Bu , GitHub sayfasındaki 672 sayısıyla ilişkili gibi görünüyor, bu da bunun Sürekli Çarpışma Algılama (CCD) eksikliğinden kaynaklandığını gösteriyor.

Bunu düzeltmek için bir girişim yapıldı ve bunun kodu burada bulunabilir, ancak sorun hala açıktır, bu nedenle CCD'yi kendiniz oluşturmak için motoru düzenlemeniz gerekebilir.


1
Cevabınız için teşekkürler! Bunu düşünmüştüm ama bunun bir CCD sorunu değil, "Durdurulamaz bir güç taşınmaz bir engelle karşılaştığında ne olur?" Sorunu olduğuna inanıyorum. Bir şekilde, vücutların kesişmesini önlemek için güçleri nasıl etkisiz hale getireceğimi bulmam gerekiyor.
d13
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.