チュートリアル - 3D空間を使った簡単webサイト 5/5

[前回](https://qiita.com/sutobu000/items/8380dfa812e60b15cea4)

#Publishして誰でも見れるように公開！
プロジェクトを作成してビルドまでやってくれるのもPlayCanvasのすごいところ。

ここまで作成したものをPlayCanvasの公開環境に公開してみましょう。

左のメニュー、またはシーンの左上のManage Scenesから以下のPublish画面を開きます。

![スクリーンショット 2019-06-21 11.15.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109168/004a509c-55eb-bc96-cedd-8dd5230d12b7.png)


PUBLISH TO PLAYCANVASを選択して、一番下のPUBLISH NOWをクリックしてビルドは完了です。
URLでPC、スマホ、他ユーザーにもログインすることなく共有することができます。

![スクリーンショット 2019-06-21 11.18.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109168/c3d5fbda-dc50-56c1-ebd7-5de00825f255.png)


以上がチュートリアルの大枠となる説明でした。
おつかれさまでした。

 

 

以降はWebサイトとして追加したいものを説明するおまけです。

-------------------------------------------

#おまけ
##SCENEの名前を変更する

sceneの名前もUntitledのままじゃカッコがつかないですね。
これはPlayCanvasの設定から変更ができます。

左上のPlayCanvasのロゴをクリックするとメニューが開きます。
その中のSettingsでインスペクター上にSETTINGSを開き、上のScene Nameから名前を変更できます。

![スクリーンショット 2019-06-24 19.35.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109168/0387ed02-9354-e9b9-ce52-ed902ab9e62c.png)


---

##headerの処理
headerのhtmlも追加しているので、同じようにglobal変数を使って同じように実装していきます。

addhtml.jsのinitializeに以下に書き換えます。
さらに、headerのナビゲーションをクリックするためgnavClickのイベントの処理も追加します。
headerのナビゲーションではどのsceneを選択したのかを、data-scene属性を作成して参照します。

```js
Addhtml.prototype.initialize = function() { // init

    globalPc.scene = 0; // どのシーンページを開いているか保管

    var htmlNameArr = []; // attributesのデータの名前を配列にします
    var arrIndex = Addhtml.attributes.index;
    for(var i = 0; i < Object.keys(arrIndex).length; i++){ // attributesのデータを取得してfor文で回す
        if(!Object.keys(arrIndex)[i].indexOf("scene")){ // 名前にhtmlが入っているデータをif文
            htmlNameArr.push(Object.keys(arrIndex)[i]); // htmlデータの名前だけ配列化する
        }
    }

    var head = document.getElementsByTagName("head")[0]; // headタグ取得
    var body = document.getElementsByTagName("body")[0]; // bodyタグ取得

    var wrapper = document.createElement("div"); // DOMを囲う要素を作成
    wrapper.className = "wrapper"; // クラス名指定
    body.appendChild(wrapper); // bodyタグの最後に要素を追加

    container = document.createElement("main"); // DOMを囲う要素を作成
    container.className = "container"; // クラス名指定
    wrapper.appendChild(container); // bodyタグの最後に要素を追加

    var style = document.createElement("style"); // cssのstyleタグ

    wrapper.insertAdjacentHTML("afterbegin", this.header.resource); // attrで追加したヘッダーを追加

    // ヘッダー内のgnaviを取得
    var gnavLists = document.querySelector(".gnav_lists"); 
    gnavLists.innerHTML = ""; // .gnav_lists内のhtmlを空っぽに
    var gnavItem = document.createElement("div"); // gnavの各リンクのdivを作成
    gnavItem.className = "gnav_item"; // gnavリンク要素のclass名を指定
    var gnavItemCl = []; // gnav_itemを整理する

    style.append(this.css._resources[0]);
    head.appendChild(style); // headの最後にstyleを追加

    for(var i = 0; i < htmlNameArr.length; i++){

        container.insertAdjacentHTML("beforeend", this[htmlNameArr[i]].resource); // アセットから取得したhtmlを追加

        // 今回はgnavのリンクの数とhtmlのアセットの数が同じなのでどう処理を行う。
        gnavItemCl[i] = gnavItem.cloneNode(true); // gnavItemのクローンを作成
        gnavItemCl[i].innerHTML = '<label for="menu"><span>' + htmlNameArr[i] + '</span></label>'; // innerHTMLを作成
        gnavItemCl[i].setAttribute("data-scene",i+1); // scene1,scene2,scene3を切り替えるため数値をdata-sceneに追加
        gnavLists.append(gnavItemCl[i]); // gnav_lists内に追加
        gnavItemCl[i].addEventListener("click", gnavClick, false); // gnav_item各々にイベントをセット
    }
};
```

```js
function gnavClick(e){ // gnav_itemをクリックしたら発火
    if(globalPc.scene === 0)  {
        globalPc.scene = this.getAttribute("data-scene"); // クリックしたgnav_itemのdata-sceneを取得
    }
}
```

---

##ホバーとクリックのアクションを追加
クリックでhtmlにイベントを取得させましたが、アニメーションを追加します。
ホバーすればscaleが少し大きくなり、クリックすればワイヤーフレームになるといった感じです。
このアクションではもっと色んな広がりを見せることができます。
PlayCanvasではアニメーションを作れるTweenライブラリが公開されていますのでご参照ください。
https://developer.playcanvas.com/en/tutorials/tweening/

hotspot.jsを以下にまるっと書き換えます。

```js
var Hotspot = pc.createScript('hotspot');

// canvasのclickやhoverなどの処理を行う

Hotspot.attributes.add("cameraEntity", {type: "entity", title: "Camera Entity"}); // カメラのentityを取得
Hotspot.attributes.add("radius", {type: "number", title: "Radius"}); // entityのヒットエリアの範囲を指定

Hotspot.prototype.initialize = function() { // init
    this.hitArea = new pc.BoundingSphere(this.entity.getPosition(), this.radius); // ヒットエリアを作成。BoundingSphereがentityの境界エリアを作成(Photoshopでいうバウンディングボックス的な)
    this.ray = new pc.Ray(); // cameraからentityへ直進する線のデータを作成。（Rayは光線の意でstart pointからentityまでの距離を測ったりすることが可能）

    this.directionToCamera = new pc.Vec3(); // Vector座標の型を取得

    this.app.mouse.on(pc.EVENT_MOUSEMOVE, this.onMouseHover, this); // マウスカーソルがホバーした時
    this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onMouseDown, this); // クリックが押された時

    this.localScale = this.entity.getLocalScale().clone(); // 初期のscaleを保持
    this.hoverScale = this.localScale.clone().scale(1.2); // ホバー時のentityのscaleを保持

};

Hotspot.prototype.update = function(dt) { // update
    this.cameraPosition = this.cameraEntity.getPosition(); // cameraのポジション(座標)を取得
    this.directionToCamera.sub2(this.cameraPosition, this.entity.getPosition()); // 2つの3次元ベクトルの値を互いに減算
    this.directionToCamera.normalize(); // 3次元ベクトルを単位ベクトルに変換

    if(globalPc.scene === 0){ // sceneが選択されていない場合
        for (var i = 0; i < this.entity.model.meshInstances.length; i++) {
            this.entity.model.meshInstances[i].renderStyle = pc.RENDERSTYLE_SOLID; // レンダラーのスタイルをソリッドに変更
        }
    }
    if(this.entity.tags._list[0].replace(/[^0-9]/g, '') === globalPc.scene) { // gnav_itemから選択されたsceneを参照
        this.entity.model._model.generateWireframe(); // モデルのワイヤーフレームを準備
        for (var j = 0; j < this.entity.model.meshInstances.length; j++) { // モデルのメッシュを取得
            this.entity.model.meshInstances[j].renderStyle = pc.RENDERSTYLE_WIREFRAME; // ワイヤーフレームにレンダリング
        }
    }
};

Hotspot.prototype.doRayCast = function (screenPosition) { // レイキャスト処理（ある地点から特定方向に直線で線を引いて、その線上で物体があるか検知する処理）
    if (this.hitArea.intersectsRay(this.ray)) {
        globalPc.scene = this.entity.tags._list[0].replace(/[^0-9]/g, ''); // entityで設定したタグを取得し、s1、s2...の数字以外をreplaceで削除し数字のみ代入
        this.entity.model._model.generateWireframe(); // モデルのワイヤーフレームを準備
        for (var i = 0; i < this.entity.model.meshInstances.length; i++) { // モデルのメッシュを取得
            this.entity.model.meshInstances[i].renderStyle = pc.RENDERSTYLE_WIREFRAME; // ワイヤーフレームにレンダリング
        }
    }
};

Hotspot.prototype.onMouseHover = function(screenPosition) { // マウスホバー時
    this.cameraEntity.camera.screenToWorld(screenPosition.x, screenPosition.y, this.cameraEntity.camera.farClip, this.ray.direction); // ポジションを2Dスクリーンから3D空間へ変換
    this.ray.origin.copy(this.cameraEntity.getPosition()); // レイのオリジナルのポジションにカメラのポジションをコピー
    this.ray.direction.sub(this.ray.origin).normalize(); // 3次元ベクトルを他の場所から減算し、単位ベクトルに変換

    if (this.hitArea.intersectsRay(this.ray) && globalPc.scene === 0) { // sceneが選択されていなくて、ヒットエリアとレイが交差した場合
        this.entity.setLocalScale(this.hoverScale); // entityのスケールを大きく
    }else{
        this.entity.setLocalScale(this.localScale); // entityのスケールを小さく
    }
};

Hotspot.prototype.onMouseDown = function(event) { // クリックが押されている時
    if (event.button == pc.MOUSEBUTTON_LEFT) { // 左クリックが押された時
        this.doRayCast(event); // レイキャストを呼ぶ
    }
};
```

さらにおまけで、マウスのクリックボタンの判定を以下のように取っていましたが、

```js
CameraMove.prototype.onMouseDown = function (event) { // クリックダウンしたら
    if (event.button === pc.MOUSEBUTTON_LEFT) {} // 左クリック
    if (event.button === pc.MOUSEBUTTON_MIDDLE) {} // 中クリック
    if (event.button === pc.MOUSEBUTTON_RIGHT) {} // 右クリック
};
```

以下のようにもっと簡単に左・中・右クリックのbooleanを取ることもできます。

```js
CameraMove.prototype.onMouseDown = function (event) { // クリックダウンしたら
    if (event.buttons[0]) {} // 左クリック
    if (event.buttons[1]) {} // 中クリック
    if (event.buttons[2]) {} // 右クリック
};
```

---

##3Dオブジェクトのモデルを変えたい！
PlayCanvasにはAssets Storeなるものがあり、ここから指定のプロジェクトのAssetsライブラリにアセットを追加することができます。
http://store.playcanvas.com/

![スクリーンショット 2019-06-21 10.53.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109168/0954918c-c866-de67-8e17-ac15726ccd2e.png)


アセットを選択し、Downloadをするとログインしているアカウントが所有しているプロジェクトの一覧が表示されますので、指定のプロジェクトを選択することでアセットが使用できます。

![スクリーンショット 2019-06-21 10.56.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109168/10f840e3-d3d0-fa3a-05f7-e3e9ae87b553.png)

![スクリーンショット 2019-06-21 10.59.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109168/89882694-8c0c-4b0d-929e-2a9fccc40d3f.png)