LoginSignup
10
13

More than 1 year has passed since last update.

p5.js と物理演算エンジン「Matter.js」の組み合わせをお試し

Last updated at Posted at 2021-09-10

以下のツイートの動画で、「円同士がぶつかったり、地面みたいなところの上を転がったり」という部分の仕組みの話です。

物理演算エンジン/物理エンジンとか、英語では Physics engine と呼ばれたりするものになります。

●物理演算エンジン - Wikipedia
 https://ja.wikipedia.org/wiki/%E7%89%A9%E7%90%86%E6%BC%94%E7%AE%97%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%B3

今回、JavaScript で使えるライブラリの話になるのですが、いくつかあるライブラリの中の「Matter.js」を扱います。それを、p5.js の描画処理と組み合わせて使います。

今回の内容

今回の記事で扱うのは、冒頭で掲載していた動画の中でも使われている仕組みで、具体的には以下のようなものです。

冒頭の動画は、「画像認識で手が開いている状態を認識できたら、その手の部分から円が出現する」というものでしたが、こちらは「マウスのドラッグ操作をすると、ドラッグした軌跡の上に円が出現する」というものになります。

p5.js と Matter.js の組み合わせたプログラムの参照元

最初は、何か参考にできそうなものがあればとググってみるところから始めたのですが、その結果、以下の動画にたどり着きました。

●5.17: Introduction to Matter.js - The Nature of Code - YouTube
 https://www.youtube.com/watch?v=urR596FsU68

スキップしつつ動画の内容をざっくり見ていくと、Matter.js を p5.js と組み合わせて使っているのが確認できました。

そして、動画の説明欄を見ていると「Source Code for the all Video Lessons という記載とリンク」を見つけることができました。
ソースコード.jpg

そのリンク先はこちらです。
CodingTrain_website.jpg

ソースコードを探す

そのリポジトリの中にソースコードがありそうだったので、動画のタイトル「5.17: Introduction to Matter.js - The Nature of Code」の一部である「Introduction to Matter.js」をキーワードにして検索をかけてみました。

その結果が以下です。
検索.jpg

その検索結果の一番上に README.md のファイルが表示されていますが、そのファイルが置かれている階層へ移動してみました。

そして、ファイルの一覧を見ると sketch.js も置いてあります。
スケッチ.jpg
 
中はこのような内容になっていました。

sketch.js
// Daniel Shiffman
// http://codingtra.in
// http://patreon.com/codingtrain
// Code for: https://youtu.be/uITcoKpbQq4

// module aliases
var Engine = Matter.Engine,
  // Render = Matter.Render,
  World = Matter.World,
  Bodies = Matter.Bodies;

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

var ground;

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

  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 keyPressed() {
//   if (key == ' ') {
//   }
// }

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

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

他に box.js、boundary.js といったファイルもありました。

box.js
// Daniel Shiffman
// http://codingtra.in
// http://patreon.com/codingtrain
// Code for: https://youtu.be/uITcoKpbQq4

function Circle(x, y, r) {
  var 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() {
    var pos = this.body.position;
    var angle = this.body.angle;
    push();
    translate(pos.x, pos.y);
    rotate(angle);
    rectMode(CENTER);
    strokeWeight(1);
    stroke(255);
    fill(127);
    ellipse(0, 0, this.r * 2);
    pop();
  };
}
boundary.js
// Daniel Shiffman
// http://codingtra.in
// http://patreon.com/codingtrain
// Code for: https://youtu.be/uITcoKpbQq4

function Boundary(x, y, w, h, a) {
  var 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() {
    var pos = this.body.position;
    var angle = this.body.angle;
    push();
    translate(pos.x, pos.y);
    rotate(angle);
    rectMode(CENTER);
    strokeWeight(1);
    noStroke();
    fill(0);
    rect(0, 0, this.w, this.h);
    pop();
  };
}

あとは index.html の中身を確認して、ライブラリの依存関係を確認しておきます。
上記の JavaScript のファイルを読み込む部分などもありましたが、依存しているライブラリは「p5.js と Matter.js のみ」となりそうです。

  <script language="javascript" type="text/javascript" src="https://cdn.jsdelivr.net/npm/p5@1.3.1/lib/p5.min.js"></script>
  <script language="javascript" type="text/javascript" src="libraries/matter.js"></script>

オンライン環境(p5.js Web Editor)で動かしてみる

必要そうなもの一式がそろったので、これをオンラインの開発・実行環境である「p5.js Web Editor」で動かしてみようと思います。

index.html に関する内容

p5.js Web Editor のデフォルトの index.html に関しては、p5.sound.min.js を削除し、matter.min.js を追加しました。
matter.min.js は、現時点の最新版を CDN から読み込んでくるようにしました。

index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.17.1/matter.min.js"></script><!-- 追加 -->
    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />
  </head>
  <body>
    <script src="sketch.js"></script>
  </body>
</html>

sketch.js に関する内容

sketch.js については、元々 3つに分かれていた処理を 1つにまとめてしまいました。
元の 3つに分割されたままで使っても良かったのですが、後で全体を眺めて読んでいきたかったので 1つにしてしまいました(全体の行数がそれほど多くないのもあり)。

そのまま動かしてみた後、色の設定の部分などは少しだけ変更を加えてみています。

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();
  };
}

style.css に関する内容

style.css は特に変更は加えていません。

実行する

あとは、p5.js Web Editor の実行ボタンを押して動かせば OK です。

実行ボタン.jpg

実行後は、画面上でマウスのドラッグ操作をしてみましょう。
そうすると、ドラッグをした軌跡にあたる部分から、円がたくさん出現して画面内を落ちていきます。
動作中の様子.jpg

そして、円の落下・円同士の衝突など、画面内の物体の動きは、物理演算エンジンがうまく処理をしてくれているのも確認できました。

まとめ

今回、サンプルとなるものをほぼそのまま利用して、p5.js と物理演算エンジン「Matter.js」を組み合わせた処理に入門してみました。

その後、p5-matterという、「p5.js と Matter.js を組み合わせるのを、より簡単にしてくれるライブラリ」があるのを知ったので、Matter.js をさらに使っていきつつ、こちらも試していければと思っています。

●p5-matter by pzp1997
 http://palmerpaul.com/p5-matter/
●pzp1997/p5-matter: Seamlessly integrate matter.js with p5.js
 https://github.com/pzp1997/p5-matter
p5-matter_by_pzp1997.jpg

【追記】 p5-matter に関する記事の公開

p5-matter に関する記事を書きました。

●p5-matter を使って p5.js での物理演算エンジン(Matter.js)の利用を簡単化する【概要編】 - Qiita
 https://qiita.com/youtoy/items/a0e0da2da4c3acf66a7b
●p5-matter を使って p5.js での物理演算エンジン(Matter.js)の利用を簡単化する【活用編1】 - Qiita
 https://qiita.com/youtoy/items/7fa6f6e6df2cf60133e6

10
13
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
10
13