💙

【Matter.js】SVGの凹面処理による複合ボディに単一のテクスチャを適応する

に公開

解決したい問題

https://brm.io/matter-js/demo/#svg
Matter.jsでは凹面(concave)を含むSVGをpoly-decomp.jsというライブラリで複数の凸面のみのパーツに分割し、それらのパーツをまとめて複合ボディとして生成する。

で、複合ボディにテクスチャを適応するとこうなる。

チューチュートレイン

// Matter.jsのワールドに追加
const body = Bodies.fromVertices(
  x,
  y,
  vertices,
  {
    mass,
    render: {
      sprite: {
        texture: texture(), // テクスチャ画像のパス
        xScale: scale, // x方向のスケール
        yScale: scale, // y方向のスケール
      },
    },
  },
  false
);
// パーツが複数生成されていること、それぞれにテクスチャが反映されていることがわかる
console.log("Generated vertices:", body.parts);

解決法

issueの一覧を見てみると、公式にバグとしてマークされているものの正式な修正は無い模様。
https://github.com/liabru/matter-js/issues/153
https://github.com/liabru/matter-js/issues/1209

以前からmatter.js関連でお世話になっていたサイトを見てみると、ドンピシャな回答が載っていた。
https://mmsrtech.com/entry/2022/09/17/225414

これを防ぐためにソースコードの修正が必要になる。(matter.jsのRender.bodiesを定義しているところ)

// handle compound parts
for (k = body.parts.length > 1 ? 1 : 0; k < body.parts.length; k++) {
    part = body.parts[k];// handle compound parts
const parts = body.render.sprite.single ? 1 : body.parts.length;
    for (k = !body.render.sprite.single && body.parts.length > 1 ? 1 : 0; k < parts; k++) {
        part = body.parts[k];
        さらに、index.jsのaddIconObject()のオブジェクト生成時のrender.spriteオプションにsingle: trueを追加する。
        render: {
        sprite: {
        texture: originCanvas.toDataURL(),
        single: true,
    }
},

さっそくライブラリを書き換えて、singleというオプションを生やそう。

npmパッケージ(ライブラリ)にパッチを当てる

patch-packageを使用することにした。
https://www.npmjs.com/package/patch-package

node_modulesフォルダ内のmatter-jsの該当部分を書き換えて、以下を実行するだけ。

npx patch-package matter-js

これでコードの差分を記録したpatch用のフォルダとファイルが生成されて、変更をgitで管理することができる。
以降新しくインストールしたときにも、以下でpatchが適用される。

npx patch-package

singleオプションの適応

// Matter.jsのワールドに追加
const body = Bodies.fromVertices(
  x,
  y,
  vertices,
  {
    mass,
    render: {
      sprite: {
        texture: texture(), // テクスチャ画像のパス
        xScale: scale, // x方向のスケール
        yScale: scale, // y方向のスケール
+        single: true, // テクスチャを1つのパーツにのみ適用
      },
    },
  },
  false
);
console.log("Generated vertices:", body.parts);

位置のズレなどもなく、いい感じ。

Discussion