軽量な2次元物理演算エンジン「Matter.js」を使って、マルチデバイスなブラウザゲームを作ってみた。

今回は、軽量な2次元物理演算エンジン「Matter.js」を紹介します。

20151015_img
Matter.js

これまで、物理演算エンジンとして「Box2DJS」や「Physijs」や「Cannon.js」などを紹介してきました。どれも高機能なものですが、ファイルが重く、負荷が高いものと言わざるえません。「three.js」といった別のライブラリと併用となると、物理演算の負荷軽減が重要になります。また、スマートフォンで使用しようと考えると、ファイルサイズが大きいものや、負荷の高いものを使用するのは避けたいところです。

今回紹介する「Matter.js」は、物理演算エンジンとして、必要な機能を揃えておきながら、圧縮後のファイルが76KBと軽く、演算負荷に関してもモバイルDemoを見る限り、iPhone6で60FPSが出ているので、問題なく使用できる感じがあります。さらにはスマートフォンの傾きに合わせて、重力が適応されるといったサンプルもあり、PCのみならずスマートフォンやタブレットを意識した作りになっています。

サンプルも豊富で、ドキュメントもちゃんと揃っています(英語)ので、非常にとっつきやすいと思います。本体のソースコードも、綺麗に整理されているので、抵抗なくソースを読むことが出来るかと思います。


基本的な使い方

20151015_img2

基本的な使い方は、

  1. Matter.Engine.createメソッドで、物理演算エンジンを生成します。第1引数にコンテナとなる要素、第2引数にオプションを渡します。
  2. Matter.Bodies.rectangleメソッドやMatter.Bodies.circleメソッドで演算対象となるMatter.Body要素を生成します。
  3. 生成したMatter.Body要素を、Matter.World.addメソッドで、物理演算対象に追加します。第1引数に、(1)で生成した物理演算エンジンのプロパティのworldを、第2引数に(2)で生成したMatter.Body要素の配列を渡します。
  4. 最後に、Matter.Engine.runメソッドで物理演算エンジンを起動させます。引数に(1)で生成した物理演算エンジンを渡します。
<!DOCTYPE html>
<html lang="ja">
    <head>
        <title>Sample</title>
    </head>
    <body>
        <div id="container"></div>
        <script src="./js/matter.min.js"></script>
        <script>

            // Engine生成
            var engine = Matter.Engine.create(document.getElementById("container"), {
                render: {
                    options: {
                        wireframes: true, // ワイヤー表示
                        width: 640, // canvasの横幅
                        height: 480, // canvasの高さ
                        background: "rgba(0, 0, 0, 1)"
                    }
                }
            });

            // 床生成
            floor = Matter.Bodies.rectangle(320, 240, 480, 10, {
                isStatic: true // 固定
            });

            // 床追加
            Matter.World.add(engine.world, [floor]);

            for (var i = 0; i < 20; i++) {

                // ボール生成
                var x = Math.random() * 640;
                var y = Math.random() * 480 - 480;
                ball = Matter.Bodies.circle(x, y, 20, {
                    density: 0.001, // 質量
                    frictionAir: 0.01, // 空気抵抗
                    restitution: 1, // 弾力性
                    friction: 0.01 // 摩擦
                });

                // ボール追加
                Matter.World.add(engine.world, [ball]);

            }

            // 物理演算実行
            Matter.Engine.run(engine);

        </script>
    </body>
</html>

基本的な使い方は以上です。DEMOにあるSlingshot Gameのような物理演算を利用したゲームなら、すぐに作れそうですね。


マルチデバイス(PC、スマフォ対応)ブラウザゲーム

もう少し突っ込んで、マルチデバイスを意識したブラウザゲームを作ってみました。

20151015_img3
ゲームサンプル:http://knockknock.jp/sample/MatterJS/index.html

「フ○ッピーバード」のようなゲームなら簡単に作れるのでは、と思ってサンプルを作りました。操作は簡単で、イカがどこにも接触しないようにいいタイミングで、画面をタップする(PCの場合は何かしらのキーを押す)だけです。

しかし、物理演算エンジンで、「フ○ッピーバード」と同様のものが実装できると思っていたのですが、予想と違う結果でした。重力と反対方向に一定の力を加えることで、イカをジャンプさせていますが、物体の重力加速度が強ければ強いほど、強い力でジャンプしなければ、同じ高さでジャンプする事ができません。結果としては、イカに上方向に一定の力を加えた場合、上昇と落下の状態によって、ジャンプの高さが変わってしまうのです。物理法則でいうとなるべくしてなった結果なのですが。そのため「フ○ッピーバード」よりも、物理法則に沿った難易度の高いものとなっています。

何が正解かはわかりませんが、リアルが正解とは限らないのゲームなんですね。奥が深い。


ソースコード解説

ソースコードからポイントをピックアップして、解説します。

CoffeeScriptSassJadeのコンパイルは、IDEで自動化(WebStromのFileWatcher機能)していますので、GulpやGruntといったツールは内包していません。サンプルとして参照していただければと思います。

演算領域の再設定

物理エンジンを作成するときに、気をつけないといけない事があります。オプション設定でcanvasの幅と高さを設定するのですが、同じ値を演算領域としてworld.bounds.maxに設定する必要があります。演算領域 == canvasサイズというわけではないので、canvasサイズ設定のさいには、演算領域も設定する必要があります。ちなみに演算領域のデフォルト値はworld.bounds.min == {x:0、y:0}world.bounds.max == {x:800、y:600}になります。

※ ドキュメントには説明のない事ですので、間違っていたらご指摘ください。

追記 : 訂正です、ver0.8.0の話でした。ver1.0?(2015/09/17)では、解消済みで演算領域 == canvasサイズとなっています。

./js/index.coffee Line48〜

# 物理エンジンを作成
@_engine = Matter.Engine.create document.getElementById(element), {
  render: { # レンダリングの設定
    options: {
      wireframes: false # ワイヤーフレームモードをoff
      width: STAGE_WIDTH # canvasのwidth
      height: STAGE_HEIGHT # canvasのheight
      background: "./images/bg.png"
    }
  }
}
# 演算領域を設定
@_engine.world.bounds.max = {x: STAGE_WIDTH, y: STAGE_HEIGHT}
# 物理シュミレーションを実行
Matter.Engine.run @_engine

エンジンの停止と始動

イントロとアウトロでは、物理演算を停止状態にしておきたかったので、シーンが切り替わったタイミングで、エンジンのenabledプロパティにbooleanを挿入しています。

./js/index.coffee Line74〜

# イントロシーン開始
setIntroScene: ->
  # エンジン停止
  @_engine.enabled = false

# ゲームシーン開始
setGameScene: ->
  # エンジン始動
  @_engine.enabled = true

衝突判定

動的なBody要素(isStatic == true)が、動的なBody要素及び、静的なBody要素(isStatic == false)に衝突したタイミングcollisionStartイベントが発生します。Matter.Events.onメソッドで、このイベントをキャッチする事ができます。第1引数に、物理演算エンジン、第2引数にイベント名、第3引数にコールバック関数を渡します。

./js/index.coffee Line116〜

# 衝突判定
Matter.Events.on @_engine, "collisionStart", @_onHit

./js/index.coffee Line140〜

# 衝突判定
Matter.Events.off @_engine, "collisionStart", @_onHit

この他にも、

  • collisionStart : 衝突開始
  • collisionActive : 衝突中
  • collisionEnd : 衝突終了
  • mousedown : マウスダウン(タッチスタート)
  • mousemove : マウス移動(タッチムーブ)
  • mouseup : マウスアップ(タッチエンド)
  • startdrag : ドラッグ開始で発生
  • enddrag : ドラッグ終了で発生
  • sleepStart : Bodyがスリープ開始
  • sleepEnd : Bodyがスリープ終了
  • beforeAdd : 追加前
  • afterAdd : 追加後
  • beforeRemove : 削除前
  • afterRemove : 削除後
  • beforeTick
  • tick
  • afterTick
  • beforeUpdate : アップデート前
  • afterUpdate : アップデート後
  • beforeRender : レンダリング前
  • afterRender : レンダリング後

といったイベントが用意されているようです。collisionStart以外は試していないので、詳しくはわかりません。

ジャンプ

Matter.Body.applyForceメソッドでBodyに力を加える事ができます。これを利用してジャンプを行っています。また、ジャンプの際にイカの画像を0.1秒だけ差し替えています。欲をいえば、スプレットシートのようなものが使えるといいのですが。

./js/index.coffee Line178〜

# ジャンプ
jump: ->
  # Body上方向に力を加える
  Matter.Body.applyForce @_body, {x: 0, y: 1}, {x: 0, y: -PLAYER_JUMP_FORCE}
  # スプライト変更
  @_body.render.sprite.texture = "./images/gesso2.png"
  if @_intervalId
    clearTimeout @_intervalId
  @_intervalId = setTimeout =>
    @_body.render.sprite.texture = "./images/gesso.png"
  , 100

以上が、ポイントごとのソースコード解説です。


まとめ

軽量、軽負荷な2次元物理演算エンジン「Matter.js」は、いかがでしたでしょうか?次は「Matter.js」と「three.js」と組み合わせて、スマートフォンでどこまで出来るか試してみたいです。

物理演算エンジンとは関係ない話ですが、今回作ったサンプルのような、PCでもスマートフォンでもワンソースで実装可能なマルチデバイスなブラウザゲームは、アプリとは違い、審査もなく、低予算、短納期で実装可能です。おまけに、HTMLやCSSやJavaScriptといったWeb技術で実装されているので、ソーシャル連携はお手の物です。考えると、ちょっとワクワクしますね。ただし、あくまでもクライアントサイドの技術ですので、JavaScriptや通信データの改ざん等のハックの可能性を視野に入れてください。

コメント

  1. […] ← 軽量な2次元物理演算エンジン「Matter.js」を使って、マルチデバイスなブラウザゲームを作ってみた。 検索: […]