LoginSignup
3
3

More than 1 year has passed since last update.

p5-matter を使って p5.js での物理演算エンジン(Matter.js)の利用を簡単化する【活用編1】

Last updated at Posted at 2021-09-11

以下の記事で扱ってきた、 p5.js で物理演算エンジン「Matter.js」を利用する話の続きです。

1つ目の記事では、p5.js と Matter.js をそのまま組み合わせて、2つ目の記事ではそれら 2つの併用を簡単化してくれる p5-matter について軽く触れました。
今回の記事では p5-matter の情報をさらに見ていきつつ、1つ目の記事で掲載していたプログラムを p5-matter を使ったものに書きかえていきます。

ちなみに、できあがりはこのようなものになります。

元になるプログラムと書きかえ後の内容

元のプログラム

冒頭に掲載した 1つ目の記事の中で、最終的に動かしたプログラムの JavaScript の部分は以下のとおりです。

規模感としては90行ほどです。

sketch.js
let Engine = Matter.Engine,
  World = Matter.World,
  Bodies = Matter.Bodies;

let engine;
let world;
let circles = [];
let boundaries = [];

let ground;

function setup() {
  createCanvas(400, 400);
  engine = Engine.create();
  world = engine.world;

  boundaries.push(new Boundary(150, 100, width * 0.6, 20, 0.3));
  boundaries.push(new Boundary(250, 300, width * 0.6, 20, -0.3));
}

function mouseDragged() {
  circles.push(new Circle(mouseX, mouseY, random(5, 10)));
}

function draw() {
  background(180);
  Engine.update(engine);
  for (let i = 0; i < circles.length; i++) {
    circles[i].show();
  }
  for (let i = 0; i < boundaries.length; i++) {
    boundaries[i].show();
  }
}

function Boundary(x, y, w, h, a) {
  let options = {
    friction: 0,
    restitution: 0.95,
    angle: a,
    isStatic: true,
  };
  this.body = Bodies.rectangle(x, y, w, h, options);
  this.w = w;
  this.h = h;
  World.add(world, this.body);
  console.log(this.body);

  this.show = function () {
    let pos = this.body.position;
    let angle = this.body.angle;
    push();
    translate(pos.x, pos.y);
    rotate(angle);
    rectMode(CENTER);
    strokeWeight(1);
    noStroke();
    fill(0, 100, 200);
    rect(0, 0, this.w, this.h);
    pop();
  };
}

function Circle(x, y, r) {
  let options = {
    friction: 0,
    restitution: 0.95,
  };
  this.body = Bodies.circle(x, y, r, options);
  this.r = r;
  World.add(world, this.body);

  this.show = function () {
    let pos = this.body.position;
    let angle = this.body.angle;
    push();
    translate(pos.x, pos.y);
    rotate(angle);
    rectMode(CENTER);
    strokeWeight(1);
    stroke(255);
    fill(0, 0, 80);
    ellipse(0, 0, this.r * 2);
    pop();
  };
}

書きかえ後のプログラム

今回、書きかえを行った後のプログラムは以下のとおりです。
規模感的にはおおよそ 60行ほどで、先ほどのものが三分の一くらいになりました。

sketch.js
let boundaries = [];
let circles = [];

function setup() {
  createCanvas(400, 400);

  matter.init();
  boundaries.push(
    matter.makeBarrier(150, 100, width * 0.6, 20, {
      angle: radians(20),
      friction: 0.02,
      restitution: 0.95,
    })
  );
  boundaries.push(
    matter.makeBarrier(250, 300, width * 0.6, 20, {
      angle: radians(-20),
      friction: 0.8,
      restitution: 0.95,
    })
  );
}

function draw() {
  background(180);

  push();
  strokeWeight(1);
  noStroke();
  fill(0, 100, 200);
  for (let i = 0; i < boundaries.length; i++) {
    boundaries[i].show();
  }
  pop();
  
  push();
  strokeWeight(1);
  stroke(255);
  fill(0, 0, 80);
  for (let i = circles.length - 1; i >= 0; i--) {
    let b = circles[i];
    b.show();
    if (b.isOffCanvas()) {
      matter.forget(b);
      circles.splice(i, 1);
    }
  }
  pop();
}

function mouseDragged() {
  circles.push(
    matter.makeBall(mouseX, mouseY, random(10, 20), {
      friction: 0.01,
      restitution: 0.95,
    })
  );
}

boundaries.push の部分をもう少しすっきりさせれば、もう少し短くなるかも。

書きかえの際に参考にしたプログラム

今回の書きかえをする際に参考にしたのは、以下の公式ページのサンプル「Tilted Platform」です。

Tilted Platform - Roll balls down a series of tilted platforms with realistic collisions by clicking your mouse.
See it live!View source code.

この後、プログラムの書きかえ前後についての補足などを書いていきます。

プログラムの書きかえに関する補足

書きかえ前のおおまかな仕組み

書きかえ前のプログラムでは、Matter.js用の以下の変数を用意したり、 engine = Engine.create(); といった処理を書く必要がありました。

let Engine = Matter.Engine,
  World = Matter.World,
  Bodies = Matter.Bodies;

また、以下のように円や傾いた床のような部分は、それを生成するための関数を別に用意したほうが良さそうなくらいの記述量があるようでした。

function Boundary(x, y, w, h, a) {
  let options = {
    friction: 0,
    restitution: 0.95,
    angle: a,
    isStatic: true,
  };
  this.body = Bodies.rectangle(x, y, w, h, options);
  ...
function Circle(x, y, r) {
  let options = {
    friction: 0,
    restitution: 0.95,
  };
  this.body = Bodies.circle(x, y, r, options);
  ...

それを使う部分は、以下が該当します。

function setup() {
  ...

  boundaries.push(new Boundary(150, 100, width * 0.6, 20, 0.3));
  boundaries.push(new Boundary(250, 300, width * 0.6, 20, -0.3));
}

function mouseDragged() {
  circles.push(new Circle(mouseX, mouseY, random(5, 10)));
}

そして、 draw() の中は、書きかえ前後で処理の流れ的には大幅な変更はない感じになりました。
(書きかえ後で、描画する際の色や枠の線の指定がこの中に入ってきたのはありますが)

上記の仕組みの中の各処理を変更する

初期化処理的な部分

書きかえ後のほうでは、初期化処理等は基本的に以下のみです。
これは参考にした公式サンプルに書いてあったので追加してみました。

matter.init();

こちらは、念のためドキュメントの以下の説明も見てみたのですが「書くのを忘れても大きな問題にならないもの」らしいです。
どうやら、説明を読むと「呼び出すのをオススメするけど、忘れてしまっていた場合は、何かのメソッドが呼ばれた際に呼び出しがされる」という感じのようです。

●p5-matter 1.0.0 | Documentation
 http://palmerpaul.com/p5-matter/docs/#matterinit
matter.init()

円や傾いた床の呼び出し

書きかえ前の円や傾いた床の呼び出しは上で書いていたとおりでした。

これが書きかえ後では、以下の matter.makeBallmatter.makeBarrier を呼び出すだけです。
書きかえ前の function Boundary(x, y, w, h, a)function Circle(x, y, r) で書いていた処理は、ライブラリ内でうまく処理をしてくれているようです。

  circles.push(
    matter.makeBall(mouseX, mouseY, random(10, 20), {
      friction: 0.01,
      restitution: 0.95,
    })
  );
  boundaries.push(
    matter.makeBarrier(250, 300, width * 0.6, 20, {
      angle: radians(-20),
      friction: 0.8,
      restitution: 0.95,
    })
  );

この時、各パラメータは 4番目の引数として連想配列で渡してやる必要があるようです(Matter.js と同じ形です)。
それと、パラメータとして指定できるものは以下の Matter.js の Body のプロパティに関する APIドキュメントを見れば良さそうでした。
 Body - Matter.js Physics Engine API Docs - matter-js 0.17.0

それと、これらを描画される際の処理で show() を使っています。
書きかえ前はこの部分の中身を実装していた形でしたが、書きかえ後はライブラリ内で中身をうまく隠蔽してくれている形になるようです。

円の削除

上で draw() の中の処理の流れは大幅な変更はない、と書いていたものの、書きかえ前になかった処理が加わっています。
それは、以下の forget()splice() の部分です。

  for (let i = circles.length - 1; i >= 0; i--) {
    let b = circles[i];
    b.show();
    if (b.isOffCanvas()) {
      matter.forget(b);
      circles.splice(i, 1);
    }
  }

これを入れておかないと、画面外に出て行った円が配列に残ったままになっていました。
これを書いていて思ったのですが、「実は書きかえ前のものもこれに該当する処理が必要では?」という感じがします。

ここで出てくる p5-matter絡みの処理について、公式リファレンスの記載を掲載しておきます。

isOffCanvas

forget

まとめ

今回、 p5.js と Matter.js を直接組み合わせる形のプログラムを、p5-matter を使って書きかえてみました。
その結果、描画まわりの処理など、ある程度は自前で実装する必要があった部分が、ライブラリ内での処理になるのでソースコードの内容がすっきりしました。

この後も、引き続き以下の公式ドキュメントなどを見つつ、活用法を探っていければと思います。

●p5-matter 1.0.0 | Documentation
 http://palmerpaul.com/p5-matter/docs/

【追記】 MQTT との組み合わせ

以前、p5.js とリアルタイム通信の MQTT を組み合わせて、異なる 2つの異なるデバイスの画面が仮想的につながる、というものを試作してみていました。
その試作に、今回の p5-matter と Matter.js を使った物理演算を組み合わせてみました。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3