LoginSignup
2
3

More than 5 years have passed since last update.

Three.jsとCANNON.jsで作る「VR3D脱出迷路アプリ」作成(その2)

Last updated at Posted at 2015-10-28

壁マップデータの作成

今回は「壁マップデータ」を作成していきたいと思います。
作成する壁マップデータは・・・

画像1.jpg

このような「壁マップデータ」を作成していくのですが、迷路アプリを作るためには「迷路の壁面位置の座標」を管理していく必要があります。

座標軸と原点座標

座標原点と座標の方向は次のように設定しました。

画像2.jpg

この図では、わかりやすさのために座標原点に壁オブジェクトを1つ作成しています。

図の上下方向がX軸、左右方向がZ軸となるようにしていますので、この図の奥行方向はY軸となっています。

壁マップデータの作成方法

そして、「壁面の位置座標」を指定して、壁オブジェクトを「Three.js」で生成すれば、上図のように迷路の壁面が描画されるのですが、壁面の座標値を1つ1つ手入力で作成するのは大変なので、今回はEXCELでマップを描画し、描画した壁面の座標値を出力できるようにVBAマクロを作成しました。

次の図は別のマップのものになりますが、迷路マップを作成した時のEXCELシートです。
画像3.jpg

左下の迷路の出口部分を拡大したものが下図になります。

画像4.jpg

セルの中に書いてある値は座標を表しています。
例えば、一番左下の「7_10」はX方向が7、Z方向が10の位置ということを表しています。
グレーに着色している部分は壁面の部分になりますので、グレーに着色されている部分の値をEXCELのVBAマクロで出力すれば「壁面の座標位置のデータ」を作成することができます。

この方法なら、例えば、自分以外の人に迷路マップを作成してもらいたい時も、EXCELのセルに着色をしてもらうだけなので、簡単なルールを伝えるだけで迷路マップを作成してもらうこともできます。

作成したマップデータはJavaScriptの配列に格納しておき、マップデータ取得用関数からステージ毎のマップデータを取得することで、「Three.js」と「CANNON.js」の壁オブジェクト作成に使用しています。

MapData.js
var w1 = new Array("50_10","49_10","48_10","47_10","46_10","45_10","44_10","43_10","42_10","41_10","40_10","39_10","38_10","37_10","36_10","35_10","34_10","33_10","32_10","31_10","30_10","29_10","28_10","27_10","26_10","25_10","24_10","23_10","22_10","21_10","20_10","19_10","18_10","17_10","16_10","15_10","14_10","13_10","12_10","11_10","10_10","50_11","33_11","25_11","14_11","50_12","33_12","25_12","14_12","50_13","33_13","25_13","14_13","50_14","46_14","45_14","44_14","43_14","42_14","41_14","40_14","39_14","38_14","37_14","33_14","29_14","25_14","21_14","20_14","19_14","18_14","10_14","50_15","46_15","33_15","29_15","21_15","18_15","10_15","50_16","46_16","33_16","29_16","21_16","18_16","10_16","50_17","46_17","45_17","44_17","43_17","42_17","41_17","40_17","39_17","38_17","37_17","33_17","29_17","21_17","18_17","17_17","16_17","15_17","14_17","13_17","12_17","11_17","10_17","50_18","37_18","29_18","21_18","10_18","50_19","37_19","29_19","10_19","50_20","37_20","29_20","10_20","50_21","49_21","48_21","47_21","46_21","45_21","44_21","43_21","42_21","41_21","37_21","36_21","35_21","34_21","33_21","29_21","28_21","27_21","26_21","25_21","10_21","50_22","37_22","25_22","21_22","20_22","19_22","18_22","17_22","16_22","15_22","14_22","13_22","12_22","11_22","10_22","50_23","37_23","25_23","10_23","50_24","37_24","25_24","10_24","50_25","46_25","41_25","37_25","25_25","10_25","50_26","46_26","41_26","40_26","39_26","38_26","37_26","36_26","35_26","34_26","30_26","29_26","28_26","27_26","26_26","25_26","24_26","23_26","22_26","21_26","10_26","50_27","46_27","15_27","10_27","50_28","46_28","15_28","10_28","50_29","46_29","15_29","10_29","50_30","46_30","45_30","44_30","43_30","42_30","38_30","34_30","33_30","32_30","31_30","30_30","29_30","28_30","27_30","21_30","15_30","10_30","50_31","38_31","27_31","21_31","10_31","50_32","38_32","27_32","21_32","10_32","50_33","38_33","27_33","21_33","10_33","50_34","49_34","48_34","47_34","46_34","45_34","41_34","40_34","39_34","38_34","34_34","30_34","29_34","28_34","27_34","26_34","25_34","21_34","20_34","19_34","18_34","17_34","16_34","15_34","14_34","13_34","12_34","11_34","10_34","50_35","34_35","10_35","50_36","34_36","10_36","50_37","34_37","10_37","50_38","46_38","45_38","44_38","43_38","42_38","41_38","40_38","39_38","38_38","34_38","33_38","32_38","31_38","27_38","26_38","25_38","24_38","23_38","22_38","21_38","20_38","19_38","10_38","50_39","38_39","27_39","15_39","10_39","50_40","38_40","27_40","15_40","10_40","50_41","38_41","27_41","15_41","10_41","50_42","49_42","48_42","47_42","46_42","42_42","38_42","34_42","33_42","32_42","31_42","27_42","15_42","10_42","50_43","42_43","38_43","31_43","27_43","23_43","19_43","15_43","10_43","50_44","42_44","38_44","31_44","27_44","23_44","19_44","15_44","10_44","50_45","42_45","38_45","31_45","27_45","23_45","19_45","15_45","10_45","50_46","46_46","42_46","38_46","37_46","36_46","35_46","31_46","27_46","26_46","25_46","24_46","23_46","19_46","15_46","10_46","50_47","46_47","31_47","19_47","10_47","50_48","46_48","31_48","19_48","10_48","50_49","46_49","31_49","19_49","10_49","50_50","49_50","48_50","47_50","46_50","45_50","44_50","43_50","42_50","41_50","40_50","39_50","38_50","37_50","36_50","35_50","34_50","33_50","32_50","31_50","30_50","29_50","28_50","27_50","26_50","25_50","24_50","23_50","22_50","21_50","20_50","19_50","18_50","17_50","16_50","15_50","14_50","13_50","12_50","11_50","10_50");

var mapData = new Array(w1);

function getMapData(mapNum){
    return mapData[mapNum - 1];
}

マップを扱うJSファイルを別に作成し、そこにデータを記述していますが、たったこれだけのマップでもかなり多くの座標データが必要なことがわかりますね。

壁オブジェクトの作成

次にこのデータを元に「Three.js」と「CANNON.js」の壁オブジェクトを作成していきます。

「壁オブジェクト作成のフロー」は下図のようになります。

画像5.jpg

壁座標データから「Three.js」と「CANNON.js」の壁オブジェクトの元になるオブジェクトを作成します。

var mapData = getMapData(1); //Stage1の壁座標を取得
var boxObjArray = new Array(); //ルートオブジェクト

for(var cnt = 0; cnt < mapData.length; cnt++){
    var mData = mapData[cnt]; //配列内の壁座標データを取得
    var wall = mData.split("_"); //XとZのデータを分割

    //ルートオブジェクトを生成
    boxObjArray[cnt] = createBoxObject(Number(wall[0]), 1, Number(wall[1]), 1, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x000000 );
}

マップデータ取得用関数「getMapData」の引数にステージ番号を渡すと、そのステージのマップデータを取得することができますので、取得したデータを「mapData」という変数に格納しています。

そして、「mapDataの壁座標データ」を取り出して、X座標とZ座標に分割してから「Three.js」と「CANNON.js」の壁オブジェクトの「元になるオブジェクト(以降は「ルートオブジェクト」と呼ぶことにします)」を作成しているのですが、その処理を行っている「createBoxObject」というのは自作関数です。

これは単にルートオブジェクトを作成して、プロパティに引数の値をセットして、作成したオブジェクトを返しているだけの関数です。

function createBoxObject(posX, posY, posZ, sizeX, sizeY, sizeZ, velocX, velocY, velocZ, angVelocX, angVelocY, angVelocZ, mass, dampVal, color){
    var box = new Object();
    box.posX = posX;             //X座標(Three・CANNON)
    box.posY = posY;             //Y座標(Three・CANNON)
    box.posZ = posZ;             //Z座標(Three・CANNON)
    box.sizeX = sizeX;           //X方向サイズ(Three・CANNON)
    box.sizeY = sizeY;           //Y方向サイズ(Three・CANNON)
    box.sizeZ = sizeZ;           //Z方向サイズ(Three・CANNON)
    box.velocX = velocX;         //X方向加速度(CANNON)
    box.velocY = velocY;         //Y方向加速度(CANNON)
    box.velocZ = velocZ;         //Z方向加速度(CANNON)
    box.angVelocX = angVelocX;   //X軸回転角加速度(CANNON)
    box.angVelocY = angVelocY;   //Y軸回転角加速度(CANNON)
    box.angVelocZ = angVelocZ;   //Z軸回転角加速度(CANNON)
    box.mass = mass;             //質量(CANNON)
    box.dampVal = dampVal;       //減衰値(CANNON)
    box.color = color;           //色(Three)

    return box;
}

このルートオブジェクトには、「Three.js」と「CANNON.js」の壁オブジェクトを作成する際の設定値が混在しています。

「Three.js」と「CANNON.js」はオブジェクトを別々に管理しているため、オブジェクトのサイズなど「Three.js」と「CANNON.js」で共用している設定値を変更した場合は、「Three.js」と「CANNON.js」のそれぞれのオブジェクトの値を変更しなければいけないものがあります。

それらの設定値を一つずつ確認して変更をするのは大変なので、今後、動きのあるオブジェクトを作成した場合(マップ内に敵キャラクターを表示しランダムに移動させる)などに備えて、「Three.js」と「CANNON.js」のオブジェクトの設定値はルートオブジェクトを作成し一元的に管理することにしました。

実際に「Three.js」と「CANNON.js」のオブジェクトを作成している部分は次のようになります。

■Three.js

//Three Box--------------------------------------------------------------
for(var cnt = 0; cnt < boxObjArray.length; cnt++){
thrBoxArray[cnt] = makeBox( 0, boxObjArray[cnt].posX, boxObjArray[cnt].posY, boxObjArray[cnt].posZ, boxObjArray[cnt].sizeX, boxObjArray[cnt].sizeY, boxObjArray[cnt].sizeZ, boxObjArray[cnt].velocX, boxObjArray[cnt].velocY, boxObjArray[cnt].velocZ, boxObjArray[cnt].angVelocX, boxObjArray[cnt].angVelocY, boxObjArray[cnt].angVelocZ, boxObjArray[cnt].mass, boxObjArray[cnt].dampVal, boxObjArray[cnt].color );

scene.add(thrBoxArray[cnt]);

■CANON.js

//Cannon Box--------------------------------------------------------------
for(var cnt=0; cnt<boxObjArray.length; cnt++){
    canBoxArray[cnt] = makeBox( 1, boxObjArray[cnt].posX, boxObjArray[cnt].posY, boxObjArray[cnt].posZ, boxObjArray[cnt].sizeX, boxObjArray[cnt].sizeY, boxObjArray[cnt].sizeZ, boxObjArray[cnt].velocX, boxObjArray[cnt].velocY, boxObjArray[cnt].velocZ,boxObjArray[cnt].angVelocX, boxObjArray[cnt].angVelocY, boxObjArray[cnt].angVelocZ, boxObjArray[cnt].mass, boxObjArray[cnt].dampVal, boxObjArray[cnt].color );

world.add(canBoxArray[cnt]);
}

どちらもルートオブジェクト(boxObjAttay)を元にオブジェクトを作成していることがわかるかと思います。

「同じような処理なら一括でやってしまえばいいんじゃないか?」という思いがあったのですが、「Three.js」と「CANNON.js」の処理を混在させて書いてしまうと、どこに何の処理を書いたのか把握しづらいという懸念があったので、別々に書いていき、ある程度完成したところで、より処理数の少なくなる方法を検討する方法を取ることにしました。

と言っても、壁オブジェクトを作成するのは迷路探索を開始する直前の1回限りの処理なので、この処理は迷路探索処理には影響しないので、このままでも問題ないと思います。

ちなみに「makeBox」というのも自作関数です。

function makeBox(mode, posX, posY, posZ, sizeX, sizeY, sizeZ, velocX, velocY, velocZ, angVelocX, angVelocY, angVelocZ, mass, dampVal, color ){
    var retObj = null;


    if(mode === 0){
        //Create Three Object
        thrBox = new THREE.Mesh(new THREE.BoxGeometry(sizeX, sizeY, sizeZ, 10, 10), new THREE.MeshBasicMaterial({
            //color: color
            map: wall_texture
        }));
        thrBox.castShadow = true;
        thrBox.receiveShadow = true;
        thrBox.position.x = posX;
        thrBox.position.y = posY + ( sizeY /2 );
        thrBox.position.z = posZ;

        retObj = thrBox;
    } else if(mode === 1){
        //Create CANNON Object
        canBox = new CANNON.Body({mass: mass});
        canBox.addShape(new CANNON.Box(new CANNON.Vec3(sizeX/2, sizeY/2, sizeZ/2)));
        canBox.position.set(posX, posY, posZ);
        canBox.velocity.set(velocX, velocY, velocZ);
        canBox.angularVelocity.set(angVelocX, angVelocY, angVelocZ);
        canBox.angularDamping = dampVal;

        retObj = canBox;
    }

    return retObj;
}

この関数は、1番目の引数が「0」なら「Three.js」オブジェクト、「1」なら「CANNON.js」オブジェクトを作成して返します。

「Three.js」に関しては、壁用のテクスチャをあらかじめ用意しておく必要があります。

var wall_texture = THREE.ImageUtils.loadTexture( './assets/images/wall.jpg' );

壁面画像を用意してパス指定してあげるだけなので簡単ですね。

今回は「壁マップデータの作成」について書いてきました。
次回は「ウォークスルー機能」を実装する方法について書いていきたいと思います。

WebSite
GitHub

INDEX

その1(地面の作成)
その2(迷路マップの作成)
その3(ウォークスルー機能の作成)

2
3
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
2
3