【问题标题】:How to apply physics to complex shapes? (matter.js + p5.js)如何将物理学应用于复杂的形状? (matter.js + p5.js)
【发布时间】:2021-07-31 14:13:28
【问题描述】:

我一直在尝试使用 matter.js + p5.js 制作一个球坑模拟,用作交互式网站背景。

Mr Shiffman's video 的帮助下,我设法让它与circle shapes 很好地配合使用,但我想将它提升到一个新的水平并使用自定义的 blob 形状(取自客户的徽标)并应用相同的对他们来说是物理学。

我已经能够使用p5 的Beginshape() 和matter 的bodies.fromVertices 组合来渲染自定义形状。如您所见,它有点工作,但物理非常奇怪,即使我对两者使用相同的顶点,碰撞似乎也不匹配。

我认为这可能与 p5 文档中的这句话有关

transform()、rotate() 和 scale() 等转换在 beginShape() 中不起作用。

但我不知道我能做些什么来解决这个问题,因为我需要它们能够平移/旋转以使物理工作...

有什么想法吗?非常感谢您的帮助!

Codepen

    var fric = .6;
    var rest = .7
    //blob creation function
    function Blob(x, y) {
        var options = {
            friction: fric,
            restitution: rest
        }
        this.body = Bodies.fromVertices(x,y, blob, options);;
        World.add(world, this.body);
        
        this.show = function() {
            var pos = this.body.position;
            var angle = this.body.angle;
            
            push();
            translate(pos.x, pos.y);
            rotate(angle);
            rectMode(CENTER);
            strokeWeight(0);
            fill('#546B2E')
            beginShape();
              curveVertex(10.4235750010825,77.51573373392407);
              curveVertex(3.142478233002126,70.89274677890447);
              curveVertex(0.09197006398718799,61.45980047762196);
              curveVertex(1.1915720013184474,51.59196924554452);
              curveVertex(4.497757286928595,42.162760563619436);
              curveVertex(5.252622102311041,32.216346235505895);
              curveVertex(4.731619980811491,22.230638463608106);
              curveVertex(4.748780859149178,12.256964518539956);
              curveVertex(8.728313738681376,3.3252404103204602);
              curveVertex(17.998080279150148,0.07532797415084502);
              curveVertex(27.955564903146588,0.6294681264134124);
              curveVertex(37.68448491855515,2.8865688476481735);
              curveVertex(46.899804284802386,6.733477319787068);
              curveVertex(55.386932458422265,12.031766230704845);
              curveVertex(62.886098235421045,18.623827217916812);
              curveVertex(69.13243582467831,26.40824364010799);
              curveVertex(73.70136375533966,35.2754654128657);
              curveVertex(75.90839243871912,44.99927633563314);
              curveVertex(74.84120838749334,54.8784706257129);
              curveVertex(70.09272040861401,63.61579878615303);
              curveVertex(62.590342401896606,70.15080526550207);
              curveVertex(53.62552650480876,74.54988781923045);
              curveVertex(44.08788115809841,77.55817639102708);
              curveVertex(34.30859814694884,79.58860716640554);
              curveVertex(24.334764892578125,80.23994384765624);
              curveVertex(14.444775242328642,78.88621691226959);
            endShape(CLOSE);
            pop();
        }
    }

var clientHeight = document.getElementById('physBox').clientHeight;
var clientWidth = document.getElementById('physBox').clientWidth;

var Engine = Matter.Engine,
    World = Matter.World,
    Bodies = Matter.Bodies,
    Common = Matter.Common,
    Composite = Matter.Composite,
    Mouse = Matter.Mouse,
    MouseConstraint = Matter.MouseConstraint,
    Vertices = Matter.Vertices;

var blob = Vertices.fromPath('10.4235750010825 77.51573373392407 3.142478233002126 70.89274677890447 0.09197006398718799 61.45980047762196 1.1915720013184474 51.59196924554452 4.497757286928595 42.162760563619436 5.252622102311041 32.216346235505895 4.731619980811491 22.230638463608106 4.748780859149178 12.256964518539956 8.728313738681376 3.3252404103204602 17.998080279150148 0.07532797415084502 27.955564903146588 0.6294681264134124 37.68448491855515 2.8865688476481735 46.899804284802386 6.733477319787068 55.386932458422265 12.031766230704845 62.886098235421045 18.623827217916812 69.13243582467831 26.40824364010799 73.70136375533966 35.2754654128657 75.90839243871912 44.99927633563314 74.84120838749334 54.8784706257129 70.09272040861401 63.61579878615303 62.590342401896606 70.15080526550207 53.62552650480876 74.54988781923045 44.08788115809841 77.55817639102708 34.30859814694884 79.58860716640554 24.334764892578125 80.23994384765624 14.444775242328642 78.88621691226959');
    
var engine;
var world;
var blobs =[];

var ground;
var ceiling;
var wallLeft;
var wallRight;

var mConstraint;
//start sim after x time
setTimeout(function setup() {

    var cnv = createCanvas(clientWidth, clientHeight);
    cnv.parent("physBox");
    
    engine = Engine.create();
    world = engine.world;
    Engine.run(engine);
    //add ground
    ground = Bodies.rectangle(clientWidth/2, clientHeight+500, clientWidth, 1000, { isStatic: true });
    World.add(world, ground);
    //add ceiling
    ceiling = Bodies.rectangle(clientWidth/2, -clientHeight-500, clientWidth, 1000, { isStatic: true });
    World.add(world, ceiling);
    //add left wall
    wallLeft = Bodies.rectangle(-500, clientHeight/2, 1000, clientHeight*2, { isStatic: true });
    World.add(world, wallLeft);
    //add right wall
    wallRight = Bodies.rectangle(clientWidth+500, clientHeight/2, 1000, clientHeight*2, { isStatic: true });
    World.add(world, wallRight);
    //create x bodies
        for (var i = 0; i < 4; i++) {
        blobs.push(new Blob(clientWidth/2, 100));
        }
    //mouse controls
    var options = {
        mouse: canvasmouse
    }
    var canvasmouse = Mouse.create(cnv.elt);
    mConstraint = MouseConstraint.create(engine);
    World.add(world,mConstraint);
    
}, 2000);    

function draw() {

    background('#EEF2FD');
    //show all bodies
    for (var i = 0; i < blobs.length; i++) {
        blobs[i].show();
    }
    
}
    
body, html {
  overflow: hidden;
  padding:0;
  margin:0;
}
h1 {
  font-family: sans-serif;
  font-size: 4vw;
  background: none;
  position: absolute;
  margin-top:10%;
  margin-left: 10%;
  user-select: none;
}
#physBox {
      width: 100%;
    height: 100vh;
    padding: 0;
    left: 0;
    z-index: -1;
    box-sizing: border-box;
    
      -webkit-touch-callout: none; /* iOS Safari */
    -webkit-user-select: none; /* Safari */
     -khtml-user-select: none; /* Konqueror HTML */
       -moz-user-select: none; /* Old versions of Firefox */
        -ms-user-select: none; /* Internet Explorer/Edge */
            user-select: none; /* Non-prefixed version, currently
                                  supported by Chrome, Edge, Opera and Firefox */
}
<script src="https://cdn.jsdelivr.net/npm/poly-decomp@0.2.1/build/decomp.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.17.1/matter.min.js"></script>

<section id="physBox">
  <h1> Welcome to my website</h1>
</section>

注意:我最初尝试使用 SVG 而不是自定义形状来完成这一切,但很难理解如何使其工作,所以我放弃了,如果有人可以帮助我使用 SVG 来解决这个问题,我'我会很高兴的!另外,为我糟糕的代码格式道歉-我正在学习;)

【问题讨论】:

    标签: javascript p5.js matter.js


    【解决方案1】:

    这里的根本问题是,matter.js 根据对象的质心而不是顶点所在坐标系的原点来定位对象,很明显,blob 顶点的坐标系原点是不是它的质心,因为你所有的顶点都是正的。您可以计算 blob 的质心,然后在绘制之前使用该偏移量:

    const fric = 0.6;
    const rest = 0.7;
    
    const {
      Engine,
      Runner,
      World,
      Bodies,
      Body,
      Common,
      Composite,
      Mouse,
      MouseConstraint,
      Vertices
    } = Matter;
    
    const blob = Vertices.fromPath('10.4235750010825 77.51573373392407 3.142478233002126 70.89274677890447 0.09197006398718799 61.45980047762196 1.1915720013184474 51.59196924554452 4.497757286928595 42.162760563619436 5.252622102311041 32.216346235505895 4.731619980811491 22.230638463608106 4.748780859149178 12.256964518539956 8.728313738681376 3.3252404103204602 17.998080279150148 0.07532797415084502 27.955564903146588 0.6294681264134124 37.68448491855515 2.8865688476481735 46.899804284802386 6.733477319787068 55.386932458422265 12.031766230704845 62.886098235421045 18.623827217916812 69.13243582467831 26.40824364010799 73.70136375533966 35.2754654128657 75.90839243871912 44.99927633563314 74.84120838749334 54.8784706257129 70.09272040861401 63.61579878615303 62.590342401896606 70.15080526550207 53.62552650480876 74.54988781923045 44.08788115809841 77.55817639102708 34.30859814694884 79.58860716640554 24.334764892578125 80.23994384765624 14.444775242328642 78.88621691226959');
    
    // from http://paulbourke.net/geometry/polygonmesh/
    function computeArea(vertices) {
      let area = 0;
      for (let i = 0; i < vertices.length - 1; i++) {
        let v = vertices[i];
        let vn = vertices[i + 1];
        area += (v.x * vn.y - vn.x * v.y) / 2;
      }
    
      return area;
    }
    
    function computeCenter(vertices) {
      let area = computeArea(vertices);
      let cx = 0,
        cy = 0;
      for (let i = 0; i < vertices.length - 1; i++) {
        let v = vertices[i];
        let vn = vertices[i + 1];
        cx += (v.x + vn.x) * (v.x * vn.y - vn.x * v.y) / (6 * area);
        cy += (v.y + vn.y) * (v.x * vn.y - vn.x * v.y) / (6 * area);
      }
    
      return {
        x: cx,
        y: cy
      };
    }
    
    const center = computeCenter(blob);
    
    let engine;
    let world;
    let blobs = [];
    
    let ground;
    let ceiling;
    let wallLeft;
    let wallRight;
    
    let mConstraint;
    
    //blob creation function
    function Blob(x, y) {
      let options = {
        friction: fric,
        restitution: rest
      }
    
      this.body = Bodies.fromVertices(x, y, blob, options);
      World.add(world, this.body);
      
      // Scales the body around the center
      Body.scale(this.body, 0.5, 0.5);
    
      this.show = function() {
        var pos = this.body.position;
        var angle = this.body.angle;
    
        push();
        translate(pos.x, pos.y);
        rotate(angle);
        scale(0.5, 0.5);
        translate(-center.x, -center.y);
        strokeWeight(0);
        fill('#546B2E')
        beginShape();
        for (const {
            x,
            y
          } of blob) {
          curveVertex(x, y);
        }
        endShape(CLOSE);
        pop();
    
        // Alternately, when drawing your blobs you could use 
        // the bodies vertices, but it looks like these are
        // converted into a convex polygon.
        push();
        stroke('red');
        strokeWeight(1);
        noFill();
        beginShape();
        for (const {
            x,
            y
          } of this.body.vertices) {
          curveVertex(x, y);
        }
        endShape(CLOSE);
        pop();
      }
    }
    
    //start sim after x time
    function setup() {
      const cnv = createCanvas(windowWidth, Math.max(windowHeight, 300));
    
      engine = Engine.create();
      world = engine.world;
    
      const runner = Runner.create();
      Runner.run(runner, engine);
      //add ground
      ground = Bodies.rectangle(width / 2, height, width, 50, {
        isStatic: true
      });
      World.add(world, ground);
      //add ceiling
      ceiling = Bodies.rectangle(width / 2, 0, width, 50, {
        isStatic: true
      });
      World.add(world, ceiling);
      //add left wall
      wallLeft = Bodies.rectangle(0, height / 2, 50, height, {
        isStatic: true
      });
      World.add(world, wallLeft);
      //add right wall
      wallRight = Bodies.rectangle(width, height / 2, 50, height, {
        isStatic: true
      });
      World.add(world, wallRight);
      //create x bodies
      for (let i = 0; i < 4; i++) {
        blobs.push(new Blob(random(50, width - 100), random(50, height - 100)));
      }
    }
    
    function draw() {
      background('#EEF2FD');
      //show all bodies
      for (var i = 0; i < blobs.length; i++) {
        blobs[i].show();
      }
    }
    
    function mouseClicked() {
      blobs.push(new Blob(mouseX, mouseY));
    }
    html,
    body {
      margin: 0;
      overflow-x: hidden;
    }
    <script src="https://cdn.jsdelivr.net/npm/poly-decomp@0.2.1/build/decomp.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.17.1/matter.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

    【讨论】:

    • 哇,太棒了,谢谢!关于中心缩放是有道理的,我认为 rectMode(CENTER) 会解释这一点,但我想这不支持自定义形状。这个解决方案很棒,虽然我不太懂数学(哈哈),但它工作得很好,非常感谢!我可以再问一个问题吗? -> 有没有办法动态缩放这些形状?我的意思是 - 我想将 blob 的宽度缩放为视口宽度的 10%,以便它响应不同的设备。
    • ^我认为这需要 Matter.Body.scale(body, scaleX, scaleY, [point]) ...但我无法弄清楚如何在没有错误或没有结果的情况下实现它.
    • 啊,是的,rectMode 仅适用于 rect()square() 函数。我已经更新了我的示例来扩展你的 blob。不是说需要用matter.js body调用Body.scale,绘制形状时需要调用p5.js scale函数。
    • 哦,我明白了,我想我快到了,但我在 scale 函数中放了“this”而不是“this.body”,似乎总是忘记这一点。再次感谢保罗,这一直困扰着我好几天 :)
    • 你不能改用Matter.Vertices.centre(body.vertices)吗?
    猜你喜欢
    • 2021-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多