Chapter 17

2-11: Web アプリ (Web App Layout)

ちょまど (千代田まどか)
ちょまど (千代田まどか)
2022.05.20に更新

このページの原文: https://doc.babylonjs.com/start/chap2/app2

🎁 2-11: Web アプリ (Web App Layout)

さまざまな Web アプリのレイアウト

web アプリの例として、たとえば、ゲーム画面でページの大部分を占め、端のほうに少し説明用のスペースが必要になる場合があります。
<canvas> 要素を <div> に配置し、必要に応じて他の要素を配置するだけです。

<div id = "holder">
    <canvas id="renderCanvas" touch-action="none"></canvas> <!-- touch-action="none" for best results from PEP -->
</div>
<div id = "instructions">
    <br/>
    <h2>Instructions (説明)</h2>
    <br/>
    Instructions Instructions Instructions Instructions Instructions 
    Instructions Instructions Instructions Instructions Instructions 
</div>

CSS も少し追加しましょう。

<style>
    #holder {
        width: 80%;
        height: 100%;
        float: left;
    }

    #instructions {
        width: 20%;
        height: 100%;
        float: left;
        background-color: grey;
    }
</style>

例1:.glb モデルをインポートして表示させているサイト

デモサイト「Babylon Template インストラクション(説明)付きのアプリ」:村のモデルをインポートした。

https://doc.babylonjs.com/webpages/app3

このデモサイトのソースコードを見てみると、<script> 部分のなかでモデルを読み込んでいる箇所がわかります。

const canvas = document.getElementById("renderCanvas");
const engine = new BABYLON.Engine(canvas, true); // BABYLON 3D エンジン

const createScene = function () {
    const scene = new BABYLON.Scene(engine);  
    // ↓ ここでモデルを読み込んでいる
    BABYLON.SceneLoader.ImportMeshAsync("", "https://assets.babylonjs.com/meshes/", "village.glb");
    // ↓ カメラとライトの設定
    const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 15, new BABYLON.Vector3(0, 0, 0));
    camera.attachControl(canvas, true);
    const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0));
    return scene;
};

const scene = createScene(); 

script 部分で最初に const canvas = document.getElementById("renderCanvas"); していた通り、
この body 部分で renderCanvas が ID の canvas 要素があります。

<canvas id="renderCanvas" touch-action="none"></canvas>

ここにモデルが展開されます。

例2:コードでモデルを組み立てて表示させているサイト

先ほどの例では完成した .glb 3D モデルファイルを読み込んで表示させているだけでしたが、
もちろん、モデルを完全にコードで組み立ててそれを表示させることもできます。

デモサイト「Babylon Template インストラクション(説明)付きのアプリ」:村のモデルをコードで作った版です。

https://doc.babylonjs.com/webpages/app4.html

(見た目は先ほどと全く同じなので写真は省略)

この デモサイトのソースコード を見てみると、モデルを組み立てている箇所がわかります。

const canvas = document.getElementById("renderCanvas"); // Get the canvas element
const engine = new BABYLON.Engine(canvas, true); // Generate the BABYLON 3D engine

// Add your code here matching the playground format
const createScene =  () => {
    const scene = new BABYLON.Scene(engine);
    
    buildDwellings();
    
    /**** Set camera and light *****/
    const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 15, new BABYLON.Vector3(0, 0, 0));
    camera.attachControl(canvas, true);
    const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0));

    return scene;
}

/******Build Functions***********/
const buildDwellings = () => {
    const ground = buildGround();

    const detached_house = buildHouse(1);
    detached_house.rotation.y = -Math.PI / 16;
    detached_house.position.x = -6.8;
    detached_house.position.z = 2.5;

    const semi_house = buildHouse(2);
    semi_house .rotation.y = -Math.PI / 16;
    semi_house.position.x = -4.5;
    semi_house.position.z = 3;

    const places = []; //each entry is an array [house type, rotation, x, z]
    places.push([1, -Math.PI / 16, -6.8, 2.5]);
    places.push([2, -Math.PI / 16, -4.5, 3]);
    places.push([2, -Math.PI / 16, -1.5, 4]);
    places.push([2, -Math.PI / 3, 1.5, 6]);
    places.push([2, 15 * Math.PI / 16, -6.4, -1.5]);
    places.push([1, 15 * Math.PI / 16, -4.1, -1]);
    places.push([2, 15 * Math.PI / 16, -2.1, -0.5]);
    places.push([1, 5 * Math.PI / 4, 0, -1]);
    places.push([1, Math.PI + Math.PI / 2.5, 0.5, -3]);
    places.push([2, Math.PI + Math.PI / 2.1, 0.75, -5]);
    places.push([1, Math.PI + Math.PI / 2.25, 0.75, -7]);
    places.push([2, Math.PI / 1.9, 4.75, -1]);
    places.push([1, Math.PI / 1.95, 4.5, -3]);
    places.push([2, Math.PI / 1.9, 4.75, -5]);
    places.push([1, Math.PI / 1.9, 4.75, -7]);
    places.push([2, -Math.PI / 3, 5.25, 2]);
    places.push([1, -Math.PI / 3, 6, 4]);

    //Create instances from the first two that were built 
    const houses = [];
    for (let i = 0; i < places.length; i++) {
        if (places[i][0] === 1) {
            houses[i] = detached_house.createInstance("house" + i);
        } else {
            houses[i] = semi_house.createInstance("house" + i);
        }
        houses[i].rotation.y = places[i][1];
        houses[i].position.x = places[i][2];
        houses[i].position.z = places[i][3];
    }
}

const buildGround = () => {
    //color
    const groundMat = new BABYLON.StandardMaterial("groundMat");
    groundMat.diffuseColor = new BABYLON.Color3(0, 1, 0);

    const ground = BABYLON.MeshBuilder.CreateGround("ground", {width:15, height:16});
    ground.material = groundMat;
}

const buildHouse = (width) => {
    const box = buildBox(width);
    const roof = buildRoof(width);

    return BABYLON.Mesh.MergeMeshes([box, roof], true, false, null, false, true);
}

const buildBox = (width) => {
    //texture
    const boxMat = new BABYLON.StandardMaterial("boxMat");
    if (width == 2) {
        boxMat.diffuseTexture = new BABYLON.Texture("https://assets.babylonjs.com/environments/semihouse.png") 
    } else {
        boxMat.diffuseTexture = new BABYLON.Texture("https://assets.babylonjs.com/environments/cubehouse.png");   
    }

    //options parameter to set different images on each side
    const faceUV = [];
    if (width == 2) {
        faceUV[0] = new BABYLON.Vector4(0.6, 0.0, 1.0, 1.0); //rear face
        faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.4, 1.0); //front face
        faceUV[2] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0); //right side
        faceUV[3] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0); //left side
    }
    else {
        faceUV[0] = new BABYLON.Vector4(0.5, 0.0, 0.75, 1.0); //rear face
        faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.25, 1.0); //front face
        faceUV[2] = new BABYLON.Vector4(0.25, 0, 0.5, 1.0); //right side
        faceUV[3] = new BABYLON.Vector4(0.75, 0, 1.0, 1.0); //left side
    }
    // top 4 and bottom 5 not seen so not set

    /**** World Objects *****/
    const box = BABYLON.MeshBuilder.CreateBox("box", {width: width, faceUV: faceUV, wrap: true});
    box.material = boxMat;
    box.position.y = 0.5;

    return box;
}

const buildRoof = (width) => {
    //texture
    const roofMat = new BABYLON.StandardMaterial("roofMat");
    roofMat.diffuseTexture = new BABYLON.Texture("https://assets.babylonjs.com/environments/roof.jpg");

    const roof = BABYLON.MeshBuilder.CreateCylinder("roof", {diameter: 1.3, height: 1.2, tessellation: 3});
    roof.material = roofMat;
    roof.scaling.x = 0.75;
    roof.scaling.y = width;
    roof.rotation.z = Math.PI / 2;
    roof.position.y = 1.22;

    return roof;
}

const scene = createScene(); //Call the createScene function

script 部分で最初に const canvas = document.getElementById("renderCanvas"); していた通り、
この body 部分で renderCanvas が ID の canvas 要素があります。

<canvas id="renderCanvas" touch-action="none"></canvas>

ここに上記で組み立てたモデルが展開されます。

🚴‍♀️ 次は? - アニメーション

これで第二章が終わりですね!お疲れ様でした。

次の第三章では、アニメーションについて学びます。

最初の目標として、まずは、とても基本的な車にアニメーション(動き)を付けてみることから始めます。

車には、車体とは独立して回転する車輪が必要です。
これがどのように達成されるかを確認するには、ホイールをボディにペアレント化する方法を検討する必要があります。