matter.jsでゲーム作り

思うより簡単にゲームがつくれる事をわかってもらえるよう、プラウザゲームを作ってみます。
簡単に遊べるようにしたいので、物理演算によるシュミレーションゲームを作る事にします。それで、物理演算エンジンmatter.js と相性がよさそうなゲームを2つ作ってみます。

特徴としては、パソコンでもスマートフォンでも遊べるものにします。 パソコンでもスマートフォンでも、画面一杯でゲームできるようにする。ゲーム性を損なう画面スクロールなどのデフォルトアクションをさせない。ゲーム画面上に文字列を表示する。などとします。

①エアホッケーゲーム
このホームページのエアホッケーに出来上がったゲームを載せました。ただ、ここに載せたmatter.jsの骨組みだけでは、画面サイズによってゲームバランスが変わるので、物理演算はmatter.jsで、画面表示はthree.jsでおこなう事にしました。

まず、matter.js をダウンロードします。 https://brm.io/matter-js/Latest Buildを選ぶとGitHubに行くので、GitHubに登録してサインインします。そうすると右の方のボタン「Clone or download」が選択可能になるので、「Download ZIP」を選択してZIPファイルをダウンロードします。解凍した「matter-js-master」の中のフォルダ「build」 のなかの「matter.min.js」だけを使います。自分のゲームフォルダにセットすれば、ゲームフォルダだけでゲームが完結出来ます。

さて、新規作成で新しいフォルダを作り、「testgame」など適当に名前を付けて下さい。メモ帳を開き、下記の2つをコピーして、「index.html」、「main.js」の名前で、文字コードUTF8で作成したフォルダ「testgame に保存して下さい。もう一つ 「matter.min.js」もコピーして保存して下さい。
出来上がったものは下記から実行できます。スマートフォンでも動作確認してみてください。
testgame

フォルダごと自分のホームページにFTPでアップロードすれば、パソコンでもスマホでも動きます。ローカル環境でフォルダ内の「index.html」を実行することも出来ます。
試しに、上記リンクからゲーム画面を開き、ゲーム画面 の隅の余白を右クリックして表示されたメニューの「ページのソースを表示」を左クリックしてみてください。下記 「index.html」 と 「main.js」 が確認できると思います。
この エアホッケーゲーム は、分かりやすくするため骨組みだけです。出来上がったゲームはエアホッケーを見てください。

index.html

<!doctype html>
<html>
<head>
<meta name="apple-mobile-web-app-capable" content="yes">
<script src="matter.min.js"></script>
</head>
<body ontouchmove="event.preventDefault()">
<div id="js-engine"></div>
<script src="main.js"></script>
</body>
</html>

※スマートフォンでのデフォルトアクションを行わない
<body ontouchmove=”event.preventDefault()”>
ゲームは、このIDの中で実行
<div id=”js-engine”>

main.js

(function() {
    var Engine = Matter.Engine,
        World = Matter.World,
        Body = Matter.Body,
        Bodies = Matter.Bodies,
        Constraint = Matter.Constraint,//使用される構成の複合体を作成
        Vertices = Matter.Vertices;

    var container = document.getElementById('js-engine');//ID'js-engine'にマッチするドキュメント要素をcontainerとする
    var S_WIDTH = window.innerWidth;//フラウザのコンテンツ領域の幅
    var S_HEIGHT = window.innerHeight;//フラウザのコンテンツ領域の高さ
    if (S_WIDTH > S_HEIGHT) {//コンテンツの幅がコンテンツの高さより大きければPC、小さければスマホ
        Scale = 32;//円の直径
        goal = 100//ゴールの幅
        speed = 4;//速度の倍率
        MAXvelocity = 40;//最高速度
        weight = 0.0000001;//密度
    } else {
        Scale = 56;
        goal = 60;
        speed = 4.5;
        MAXvelocity = 55;
        weight = 0.0000001;
    }
    touchx = 0;//画面タッチx
    touchy = 0;//画面タッチy
    isTouch = false;
    disptitle = true;

    var engine = Engine.create(container, {//containerをMatter.jsのゲーム画面にする
        render: {
            options: {
                wireframes: false,
                width: S_WIDTH,
                height: S_HEIGHT,
                background: 'cornsilk'
            }
        }
    });
    //重力値
    engine.world.gravity.y = 0;//重力ナシ

    World.add(engine.world, [
              Bodies.rectangle(S_WIDTH/2,S_HEIGHT/2,S_WIDTH,3,{collisionFilter:true,isStatic:true,render:{fillStyle:'gold'}}),//画面中央の線。衝突判定ナシ
              Bodies.rectangle(S_WIDTH/2,Scale/2,S_WIDTH,Scale,{friction:1,isStatic:true,label:'wall1',render:{fillStyle:'maroon'}}),//画面上の壁
              Bodies.rectangle(Scale/2,S_HEIGHT/2,Scale,S_HEIGHT,{friction:1,isStatic:true,label:'wall2',render:{fillStyle:'maroon'}}),//画面左側の壁
              Bodies.rectangle(S_WIDTH-Scale/2,S_HEIGHT/2,Scale,S_HEIGHT,{friction:1,isStatic:true,label:'wall3',render:{fillStyle:'maroon'}}),//画面右側の壁
              Bodies.rectangle(S_WIDTH/4-goal,S_HEIGHT-Scale/2,S_WIDTH/2-goal*2,Scale,{friction:1,isStatic:true,label:'wall4',render:{fillStyle:'maroon'}}),//画面下左の壁
              Bodies.rectangle(S_WIDTH/4*3+goal,S_HEIGHT-Scale/2,S_WIDTH/2-goal*2,Scale,{friction:1,isStatic:true,label:'wall5',render:{fillStyle:'maroon'}}),//画面下右の壁
    ]);
    ballA = Bodies.circle(S_WIDTH/2,S_HEIGHT*5/6, Scale*1.2, {//自機マレット
            friction: 1,//摩擦係数
            restitution: 1,//反発係数
            collisionFilter: { group: -1 },
            label: 'ballA',//名前
            render:{fillStyle:'red'}
            });

    //回転マルチボディ拘束追加
    var body = Bodies.circle(S_WIDTH/2,S_HEIGHT*5/6, Scale*1.2, { collisionFilter: { group: -1 } });
    var constraint = Constraint.create({
        bodyA: body,
        bodyB: ballA
    });

    ballB = Bodies.circle(S_WIDTH/2, S_HEIGHT/2, Scale, {//パック
            density: weight,
            frictionAir: 0,
            friction: 1,
            restitution: 0.9,
            label: 'ballB',
            render:{fillStyle:'green'}
            });

    World.add(engine.world, [body, ballA, constraint, ballB]);

    //フレーム毎に実行
    Matter.Events.on(engine, 'beforeUpdate', function() {
        if (touchx > Scale && touchx < S_WIDTH - Scale && touchy > Scale && touchy < S_HEIGHT - Scale) {
           Matter.Body.setPosition(ballA,{x:touchx,y:touchy});//自機マレットを移動
        }
	// 速度チェック
	Matter.Body.setVelocity(ballB, {//フレーム毎にパックのx速度とy速度を調べて、最高速度以上なら最高速度にする
	         x: Math.max(Math.min(ballB.velocity.x, MAXvelocity), -MAXvelocity),
                 y: Math.max(Math.min(ballB.velocity.y, MAXvelocity), -MAXvelocity)
	});
        if (ballB.position.y > S_HEIGHT) {//パックが画面下に出たら終了
           info.innerHTML = 'START';
           disptitle = true;
           Matter.Body.setPosition(ballB,{x:S_WIDTH / 2,y:S_HEIGHT / 2});
           Matter.Body.setVelocity(ballB, {x:0, y:0});
        } 
    });

    //物理シュミレーションを実行
    Engine.run(engine);

    //ページ全体が読み込まれたときに一度発生
    window.addEventListener("load", function(){
             var target = document.getElementById("js-engine");//ID'js-engine'にマッチするドキュメント要素をtargetとして、cssをフラウザの幅と高さにする
             target.css({
             position: "absolute",
             height: window.innerHeight,
             width: window.innerWidth
             });
    }, false)

    //スマホでタッチイベント発生
    window.addEventListener('touchstart',function(e) {
        e.preventDefault();//デフォルトアクションを行わない
        touchx = e.changedTouches[0].pageX;//画面タッチx
        touchy = e.changedTouches[0].pageY - Scale * 1.2;//画面タッチyのマレット分上
        isTouch = true;//タッチしている
        if (disptitle) {info.innerHTML = ' ';disptitle = false;}
    },false)
    window.addEventListener('touchmove',function(e) {
        e.preventDefault();
        touchx = e.changedTouches[0].pageX;
        touchy = e.changedTouches[0].pageY - Scale * 2;
    },false)
    window.addEventListener('touchend',function(e) {
        e.preventDefault();
        isTouch = false;//タッチ終了
    },false)

    //パソコンでマウスイベント発生
    window.addEventListener('mousedown',function(e) {
        e.preventDefault();//デフォルトアクションを行わない
        touchx = e.clientX;//画面タッチx
        touchy = e.clientY;//画面タッチy
        isTouch = true;//タッチしている
        if (disptitle) {info.innerHTML = ' ';disptitle = false;}
    },false)
    window.addEventListener('mousemove',function(e) {
        e.preventDefault();
        if (isTouch) {
           touchx = e.clientX;touchy = e.clientY;
        }
    },false)
    window.addEventListener('mouseup',function(e) {
        e.preventDefault();
        isTouch = false;//タッチ終了
    },false)

    //文字列を表示する
    container = document.createElement( 'div' );
    document.body.appendChild( container );
    var info = document.createElement( 'div' );
    info.style.position = 'absolute';
    info.style.left = '50%';
    info.style.bottom = '50%';
    info.style.width = '50%';
    info.style.color = "blue";
    info.style.fontWeight = "bold";
    info.style.fontSize = "300%";
    info.innerHTML = 'START';
    container.appendChild( info );
})();

※Bodyに動きを設定すれば、かってに動きます。そこに、衝突した時のアクション、毎フレームごとに実行する処理、タッチイベントやマウスイベントの処理を付ければ出来上がりです。
matter.jsでは、物体が早すぎると壁をすり抜けてしまいます。ネット検索で見つけた、それを防ぐ方法です。
Matter.Body.setVelocity(ball, {//フレーム毎にx速度とy速度を調べて、最高速度以上なら最高速度にする
x: Math.max(Math.min(ball.velocity.x, MAXvelocity), -MAXvelocity),
y: Math.max(Math.min(ball.velocity.y, MAXvelocity), -MAXvelocity)
}) ;
※ゲーム画面をフラウザのコンテンツ領域いっぱいにする方法。
var target = document.getElementById(“js-engine”);//ID’js-engine’にマッチするドキュメント要素をtargetとして、cssをフラウザの幅と高さにする
target.css({
position: “absolute”,
height: window.innerHeight,
width: window.innerWidth
});
※ゲーム画面に、位置を指定して、文字列を表示する 方法。
container = document.createElement( ‘div’ );
document.body.appendChild( container );
var info = document.createElement( ‘div’ );
info.style.position = ‘absolute’;
info.style.left = ‘50%’;
info.style.bottom = ‘50%’;
info.style.width = ‘50%’;
info.style.color = “blue”;
info.style.fontWeight = “bold”;
info.style.fontSize = “300%”;
info.innerHTML = ‘START’;
container.appendChild( info );

②RCカーゲーム
このホームページのRCカーグランプリに出来上がったゲームを載せました。物理演算はmatter.jsで、画面表示はthree.jsでおこなっています。ここにはそのゲームの骨組み部分だけを抜き出して載せます。これだけで動きます。

まず「matter.min.js」に続いて、「three.min.js」を用意します。
https://threejs.org/のメニュー「download]リンクからライブラリをダウンロードします。ダウンロードしたthree.js-master.zipを展開して、その中のフォルダ「build」の「three.min.js」をコピーします。

新規作成で新しいフォルダを作り、「testgame」など適当に名前を付けて下さい。メモ帳を開き、下記の3つをコピーして、「index.html」、「index.css」、「main.js」の名前で、文字コードUTF8で作成したフォルダ「testgame」 に保存して下さい。 「matter.min.js」、「three.min.js」もコピーして保存して下さい。このフォルダだけでゲームが完結出来ます。
出来上がったものは下記から実行できます。スマートフォンでも動作確認してください。
testgame3D

index.html

<!doctype html>
<html>
<head>
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link rel="stylesheet" href="index.css">
</head>
<body ontouchmove="event.preventDefault()">
<script src="matter.min.js"></script>
<script src="three.min.js"></script>
<div id="js-engine" class="engine"></div>
<script src="main.js"></script>
</body>
</html>

index.css

.engine {
  position: absolute;
  left: 0;
  right: 0;
  //visibility:hidden;
  width: 180px;
  height: 180px; }
  .engine canvas {
    width: 100%;
    height: 100%; }

main.js

(function() {
var MAP = [//コースの2次元配列
    [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [ 1, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 1],
    [ 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 1],
    [ 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 1],
    [ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1],
    [ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 0, 0, 0, 0, 0, 1],
    [ 1, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 6, 0, 0, 0, 0, 1],
    [ 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 3, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 6, 0, 0, 0, 1],
    [ 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 3, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 2, 0, 0, 0, 1],
    [ 1, 0, 0, 0, 0, 4, 2, 6, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 2, 0, 0, 2, 0, 0, 0, 1],
    [ 1, 0, 0, 0, 4, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 1],
    [ 1, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 4, 2, 2, 6, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 1],
    [ 1, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 1],
    [ 1, 0, 0, 0, 2, 0, 3, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 1],
    [ 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 1],
    [ 1, 0, 0, 0, 2, 0, 0, 0, 4, 2, 2, 2, 3, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 1],
    [ 1, 0, 0, 0, 2, 0, 0, 0, 2, 2, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 1],
    [ 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 2, 0, 0, 2, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 1],
    [ 1, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 1],
    [ 1, 0, 0, 0, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 2, 0, 0, 0, 5, 2, 2, 3, 0, 0, 0, 1],
    [ 1, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 4, 2, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [ 1, 0, 0, 0, 2, 2, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1],
    [ 1, 0, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 4, 0, 1],
    [ 1, 0, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 4, 0, 0, 1],
    [ 1, 0, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 1],
    [ 1, 0, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 1],
    [ 1, 0, 0, 0, 2, 2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 1],
    [ 1, 0, 0, 0, 5, 3, 0, 0, 0, 0, 0, 5, 2, 2, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 1],
    [ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1],
    [ 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 2, 2, 2, 2, 2, 6, 0, 0, 0, 0, 1],
    [ 1, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 1],
    [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
    ];

    var center_x = window.innerWidth / 2;//画面の縦方向の真ん中

    //three.js用3D外壁
    var geobrock1 = new THREE.CubeGeometry(26, 5, 26);
    //loader = new THREE.TextureLoader();texture = loader.load("wall01.jpg");画像を用意すれば、それを表示する
    var matebrock1 = new THREE.MeshBasicMaterial({color: "sienna"});//MeshBasicMaterial({map: texture});
    //three.js用3D植え込み
    var matebrock2 = new THREE.MeshBasicMaterial({color: "green"});
    //three.js用3Dコーナー
    var geobrock3 = new THREE.CubeGeometry(37, 4,24);
    var matebrock3 = new THREE.MeshBasicMaterial({color: "white"});//MeshBasicMaterial({map: texture});
    //three.js自機
    geometry = new THREE.CylinderGeometry(7,10, 8, 4);
    var side_material = new THREE.MeshBasicMaterial({color: "red"});
    var end_material = new THREE.MeshBasicMaterial({color: "crimson"});
    material = new THREE.MeshFaceMaterial([side_material, end_material]);
    var machine = new THREE.Mesh(geometry, material);
    machine.position.y = 4;
    //three.js用3D地面
    geometry = new THREE.PlaneGeometry(60 * 26,60 * 26);
    //loader = new THREE.TextureLoader();texture = loader.load("floor.jpg");画像を用意すれば、それを表示する
    material = new THREE.MeshBasicMaterial({color: "gray",side: THREE.BackSide});//裏返しに張り付けなければ、透明になってしまう
    var plane = new THREE.Mesh(geometry, material);

    //three.jsシーン
    var scene = new THREE.Scene();

    //matter.js物理演算エンジン
    var Engine = Matter.Engine,
        Render = Matter.Render,
        World = Matter.World,
        Bodies = Matter.Bodies;
    //matter.js物理演算ワールドスペース
    var container = document.getElementById('js-engine');//cssでvisibility:hiddenにすれば非表示
    var engine = Engine.create(container, {
        render: {options: {wireframes: true,width: 832,height: 832}
        }
    });

    //重力ゼロにして、落下防止する
    engine.world.gravity.y = 0;

    //衝突チェック(自機が壁にぶつかればアクセルをゼロ)
    Matter.Events.on(engine, 'collisionStart', function(event) {
        pairs = event.pairs;
        for (i = 0; i < pairs.length; i++) {
            var pair = pairs[i];
            if (pair.bodyA.label == 'player' && pair.bodyB.label == 'block') {
                accel = 0;
            }
            if (pair.bodyA.label == 'block' && pair.bodyB.label == 'player') {
                accel = 0;
            }
        }
    });

    //キー入力
    document.onkeydown = function(e) {
        e.preventDefault();//規定の動作をキャンセル
        if (e.keyCode == 16) {down = true;}
        if (e.keyCode == 39) {right = true;}
        if (e.keyCode == 37) {left = true;}
    };
    document.onkeyup = function(e) {
        e.preventDefault();//規定の動作をキャンセル
        if (e.keyCode == 16) {down = false;}
        if (!game) {game = true;info.innerHTML = ' ';}//ゲームスタートと文字を消す
        if (e.keyCode == 39) {right = false;}
        if (e.keyCode == 37) {left = false;}
    };

    //フレーム毎に繰り返し実行
    Matter.Events.on(engine, 'beforeUpdate', function() {
        if (game) {//ゲームの開始フラグ
            if (right) {Matter.Body.setAngularVelocity(player, Math.PI / 125);//右に曲がる
            } else if  (left) {Matter.Body.setAngularVelocity(player, -Math.PI / 125);}//左に曲がる
            if (down) {accel = accel * 0.96;}//時間と共に掛け算でブレーキ
            Matter.Body.applyForce(player, {x: player.position.x,y: player.position.y}, {x: Math.cos(player.angle) * accel,y: Math.sin(player.angle) * accel});//特定のの位置からボディにフォースを与える。
            if (accel < 0.00023) {accel = accel + 0.0000027};//時間と共に足し算で加速する
            machine.position.z = player.position.y;//matter.jsのplayerの位置を、three.jsのmachineに変換する
            machine.position.x = player.position.x;
            machine.rotation.y = - player.angle - Math.PI / 4;//matter.jsのplayerは、three.jsのmachineでは、上が底になる
            oldcamex5 = oldcamex4;//視覚表現のため、カメラの位置を5つ前の場所に
            oldcamez5 = oldcamez4;
            oldcamex4 = oldcamex3;
            oldcamez4 = oldcamez3;
            oldcamex3 = oldcamex2;
            oldcamez3 = oldcamez2;
            oldcamex2 = oldcamex;
            oldcamez2 = oldcamez;
            oldcamex = machine.position.x - Math.cos(player.angle) * 100;//カメラをthree.jsのmachineの後ろ100の位置にセットする
            oldcamez = machine.position.z - Math.sin(player.angle) * 100;
        }
        camera.position.x = oldcamex5;//カメラの位置を5つ前の場所にセットする
        camera.position.z = oldcamez5;
        camera.lookAt(machine.position);//カメラをthree.jsのmachineに向ける
        renderer.render(scene, camera);
    });

    //最初にここから実行する
    plane.position.set(30 * 26, 0,30 * 26);//three.js用3D地面をセットする
    plane.rotation.x = Math.atan2(1,0);
    scene.add(plane);
    player = Bodies.rectangle( 0, 0,13,13,{density: 0.00058,friction: 0,frictionAir: 0.13,restitution: 0.3,label: 'player'});
    World.add(engine.world, [player]);//matter.js物理演算ワールドにplayerをセットする
    scene.add(machine);//three.jsのシーンにmachineを表示
    Initialset();//ファンクションInitialset()を実行
    refresh();//ファンクションrefresh()を実行、あとはフレーム毎実行が繰り返される

    //matter.js物理演算ワールドでマップ配列をセットして、同時にthree.jsでシーンに表示する
    function Initialset() {
        for (i = 0, max = MAP.length; i < max; i++) {
            for (j = 0, max2 = MAP[i].length; j < max2; j++) {
                //matter.js用の外壁
                if (MAP[j][i] == 1) {
                    brock1 = Bodies.rectangle(i * 26 + 13, j * 26 + 13, 26, 26, {label: 'block',isStatic: true});
                    World.add(engine.world, [brock1]);
                    //three.js用の3D外壁
                    thbrock1 = new THREE.Mesh(geobrock1, matebrock1);
                    scene.add(thbrock1);
                    thbrock1.position.z = brock1.position.y;
                    thbrock1.position.x = brock1.position.x;
                    thbrock1.position.y = 2;
                }
                //matter.js用の植え込み
                if (MAP[j][i] == 2) {
                    brock2 = Bodies.rectangle(i * 26 + 13, j * 26 + 13, 26, 26, {label: 'block',isStatic: true});
                    World.add(engine.world, [brock2]);
                    //three.js用の3D植え込み
                    thbrock2 = new THREE.Mesh(geobrock1, matebrock2);
                    scene.add(thbrock2);
                    thbrock2.position.z = brock2.position.y;
                    thbrock2.position.x = brock2.position.x;
                    thbrock2.position.y = 2;
                }
                //matter.js用のコーナー1
                if (MAP[j][i] == 3) {
                    brock3 = Bodies.rectangle(i * 26 + 4.3, j * 26 + 4.3, 37, 24, {label: 'block',isStatic: true});
                    World.add(engine.world, [brock3]);
                    Matter.Body.rotate(brock3,Math.PI*3/4);
                    //three.js用の3Dコーナー1
                    thbrock3 = new THREE.Mesh(geobrock3, matebrock3);
                    scene.add(thbrock3);
                    thbrock3.position.z = brock3.position.y;
                    thbrock3.position.x = brock3.position.x;
                    thbrock3.position.y = 2;
                    thbrock3.rotation.y = - brock3.angle + Math.PI;
                }
                //matter.js用のコーナー2
                if (MAP[j][i] == 4) {
                    brock4 = Bodies.rectangle(i * 26 + 21.3, j * 26 + 21.3, 36, 24, {label: 'block',isStatic: true});
                    World.add(engine.world, [brock4]);
                    Matter.Body.rotate(brock4,Math.PI*3/4);
                    //three.js用の3Dコーナー2
                    thbrock4 = new THREE.Mesh(geobrock3, matebrock3);
                    scene.add(thbrock4);
                    thbrock4.position.z = brock4.position.y;
                    thbrock4.position.x = brock4.position.x;
                    thbrock4.position.y = 2;
                    thbrock4.rotation.y = - brock4.angle;
                }
                //matter.js用のコーナー3
                if (MAP[j][i] == 5) {
                    brock5 = Bodies.rectangle(i * 26 + 21.3, j * 26 + 4.3, 36, 24, {label: 'block',isStatic: true});
                    World.add(engine.world, [brock5]);
                    Matter.Body.rotate(brock5,Math.PI*1/4);
                    //three.js用の3Dコーナー3
                    thbrock5 = new THREE.Mesh(geobrock3, matebrock3);
                    scene.add(thbrock5);
                    thbrock5.position.z = brock5.position.y;
                    thbrock5.position.x = brock5.position.x;
                    thbrock5.position.y = 2;
                    thbrock5.rotation.y = - brock5.angle;
                }
                //matter.js用のコーナー4
                if (MAP[j][i] == 6) {
                    brock6 = Bodies.rectangle(i * 26 + 4.7, j * 26 + 21.3, 36, 24, {label: 'block',isStatic: true});
                    World.add(engine.world, [brock6]);
                    Matter.Body.rotate(brock6,Math.PI*1/4);
                    //three.js用の3Dコーナー4
                    thbrock6 = new THREE.Mesh(geobrock3, matebrock3);
                    scene.add(thbrock6);
                    thbrock6.position.z = brock6.position.y;
                    thbrock6.position.x = brock6.position.x;
                    thbrock6.position.y = 2;
                    thbrock6.rotation.y = - brock6.angle + Math.PI;
                }
            }
        }
    }

    //リフレッシュ
    function refresh() {//ゲームクリア時に再度読み込み用
       accel = 0;//アクセル
       game = false;//ゲーム開始
       right = false;//右折
       left = false;//左折
       down = false;//ブレーキ
       Matter.Body.setPosition(player,{x:26 * 4,y:26 * 4});//matter.jsのplayerポジション
       Matter.Body.setAngle(player, 0);
       Matter.Body.setVelocity(player, {x:0, y:0});
       Matter.Body.setAngularVelocity(player, 0);
       machine.position.z = player.position.y;//matter.jsのplayerポジションを、three.jsのmachineに変換する
       machine.position.x = player.position.x;
       machine.rotation.y = - player.angle - Math.PI / 4;//THREE.CylinderGeometry(上半径,底半径,高さ,分割数4)を45度回して配置
       oldcamex = player.position.x - Math.cos(player.angle) * 100;////カメラをmatter.jsのplayerの後ろ100の位置にセットする
       oldcamez = player.position.y - Math.sin(player.angle) * 100;
       oldcamex2 = oldcamex;
       oldcamez2 = oldcamez;
       oldcamex3 = oldcamex;
       oldcamez3 = oldcamez;
       oldcamex4 = oldcamex;
       oldcamez4 = oldcamez;
       oldcamex5 = oldcamex;
       oldcamez5 = oldcamez;
    }

    //three.jsのlight
    var ambient = new THREE.AmbientLight(0xFFFFF0);//環境光源を生成
    scene.add(ambient);
    //three.jsのcamera
    var camera = new THREE.PerspectiveCamera(45, 1, 1, 2200);//THREE.PerspectiveCamera(視野角45度, アスペクト比, near, far)
    camera.position.y = 30;//カメラの地面からの高さ(固定)
    //three.js用rendering
    var renderer = new THREE.WebGLRenderer();//ウェブの3D表現
    renderer.setClearColor( 0x87CEEB, 1 );
    renderer.setSize(window.innerWidth, window.innerHeight);//ウインドウ全体をthree.jsの大きさにする
    renderer.shadowMapEnabled = false;//影は付けない
    document.body.appendChild(renderer.domElement);

    //run the engine=matter.js物理演算エンジン開始
    Engine.run(engine);

    //スマホでタッチイベント
    window.addEventListener('touchstart',function(e) {//タッチが開始された瞬間に発生
        e.preventDefault();//規定の動作をキャンセル
        var touch = e.changedTouches[0];
        if (touch.pageX > center_x) {right = true;down = true;//画面の右タッチでブレーキを掛けつつ右
        } else {left = true;down = true;}//画面の左タッチでブレーキを掛けつつ左
    },false);
    window.addEventListener('touchend',function(e) {//タッチされた後、画面から離した時に発生
        e.preventDefault();//規定の動作をキャンセル
        if (!game) {game = true;info.innerHTML = ' ';}//ゲームスタートと文字を消す
        var touch = e.changedTouches[0];
        if (touch.pageX > center_x) {right = false;down = false;
        } else {left = false;down = false;}
    },false);

    //three.js表示の上に文字を表示する
    container = document.createElement( 'div' );
    document.body.appendChild( container );
    var info = document.createElement( 'div' );
    info.style.position = 'absolute';
    info.style.left = '10%'; 
    info.style.top = '20%';
    info.style.width = '80%';
    info.style.color = "navy";
    info.style.fontWeight = "bold";
    info.style.fontSize = '5vw';
    info.innerHTML = 'PC:左右カーソルで左右に曲がり、シフトキーでブレーキです。<br>スマホ:画面を左右タップで、ブレーキを掛けながら左右に曲がります。';
    container.appendChild( info );
})();