Matter.jsをプラグインで拡張する

lark-matter screenshot

ちょっと前にlark-matterっていう物理エンジンMatter.jsのレンダラをドット絵っぽくするプラグインを作ったんだけど、その時に覚えたはずのMatter.jsのプラグインによる拡張方法を忘れないうちにメモっておく。

基本的には以下のドキュメントを読めば良い。

プラグインの基本情報を書く所登録する所はだいたいサンプルの通り書けばいいとして、肝心なのはエンジンへのパッチの当て方だ。

基本的にはMatter.beforeMatter.afterという関数を使えばMatter.js内のあらゆる関数の実行前と実行後にプラグインとして実行したい処理を挟み込むことができるので、そこでゴニョゴニョすれば良い。

lark-matterの例で言えば、まずRenderを差し替えたいのでRender.createの後で独自レンダラの初期化などを行って、Render.runの後で既存レンダラを止めて新しいレンダラに差し替えている。

  matter.after('Render.create', initRender);
  matter.after('Render.run', runRender);
function runRender(render: Matter.Render) {
  matter.Render.stop(render);
  renderLm(render);
}

Render.runの引数とかはそのままフックする関数の引数にくるので、それを使ってrunしたレンダラを止めるとかいう処理もできる。

後はEngine.createの後でMatter.js内のイベントのリスナーを登録しておけば、特定のイベントが発生した時の処理を差し込むこともできる。例えば衝突発生時(collisionStart)にパーティクルを飛ばすとか。

  matter.after('Engine.create', initEngine);
function initEngine() {
  const engine: Matter.Engine = this;
  matter.Events.on(engine, 'collisionStart', e => {
    e.pairs.forEach(p => {
      p.activeContacts.forEach(ac => {
        const b = ac.vertex.body;
        const v = b.velocity;
        let ratio = (<any>p).collision.depth * matter.Vector.magnitude(v) * 0.1;
        if (ratio > 2) {
          ratio = 2;
        }
        if (ratio > 0.3) {
          ppe.emit(b.ppeTypeId,
            ac.vertex.x / LarkMatter.options.dotSize,
            ac.vertex.y / LarkMatter.options.dotSize,
            Math.atan2(-v.y, -v.x),
            { countScale: ratio, speed: 0.7 * ratio });
          if (LarkMatter.options.enableSes) {
            sss.play(b.sssTypeId, 2, null, ratio > 1 ? 1 : ratio);
          }
        }
      });
    });
  });
}

どのようなイベントが存在するかはMatter.jsのリファレンスを見れば分かる。例えばEngineで発生するイベントの一覧とか。

あとはプラグインのリストもあるので必要に応じて他の人の使ったプラグインを参考にすれば良い。