PlayCanvasでモデルがクリックされたときにHTMLの表示を切り替える方法

## PlayCanvasとHTMLを組み合わせる
PlayCanvasとHTMLを組み合わせる方法について紹介をします。

PlayCanvasは特性上、エディターを使用した開発でHTMLを触ろうとすると少し癖があります。JavaScriptのフレームワークと組み合わせることで解消も可能だったりもします。




一例
[playcanvas-next-test](https://github.com/yushimatenjin/playcanvas-next-test)

PlayCanvas界隈では様々な方法が試されていると思いますがその中で自分が、メンテナンス性などのも考えて、使いやすかったものを紹介します。

今回はPlayCanvasの基本的な説明は省いています。

### 今回作るもの

このような3Dモデルが並んでいるページで、モデルがクリックされたらHTMLの表示を切り替えるものを作ります。


![](https://i.imgur.com/0512SBE.gif)

例: https://playcanv.as/p/9uV03p1Y/

モデルがクリックされたらモデルの情報をDOMを切り替えるものになります。
　　
（モデルは[サンディちゃん](https://playcanvas.jp/sandy/)を使用しています。）

### HTMLを組み込む

[PlayCanvasのエディター上でHTML, CSSを組み込む](https://qiita.com/yushimatenjin/items/814b4a32db53397219df)

HTMLを組み込むためにそれぞれのスクリプトを追加します。
こちらは様々な方法があるかと思いますが、`insertAdjacentHTML`を使用してHTMLを組み込みます。

#### 1. ui.js index.html, style.cssを作成する
```ui.js
const Ui = pc.createScript("ui");

Ui.attributes.add("css", {
  type: "asset",
  assetType: "css",
  title: "CSS Asset"
});
Ui.attributes.add("html", {
  type: "asset",
  assetType: "html",
  title: "HTML Asset"
});

Ui.prototype.initialize = function() {
  const body = document.getElementsByTagName("body")[0];
  const head = document.getElementsByTagName("head")[0];
  const style = `<style>${this.css.resource}</style>`;
  body.insertAdjacentHTML("beforebegin", this.html.resource);
  head.insertAdjacentHTML("afterbegin", style);
};
```


```index.html
<div class="container">
    <h2 class="title">タイトル</h2>
    <h2 class="url">URL</h2>
    <img class="image" />
</div>
```


```style.css
.container{
    position: absolute;

    background-color: white;
    z-index:1;
}

.container img{
    object-fit: cover;
    width:300px;
    height:300px;
}
```

### スクリプトに`CSS`と`HTML`を追加する

![](https://i.imgur.com/UA8eM6m.png)

`Root`のエンテティに`ui.js`を追加します。追加ができたら、`style.css`、`index.html`を設定します。

> スクリプト属性
スクリプトのアトリビュート機能は、スクリプト内で使用する変数をPlayCanvasエディタ内で編集することができるようにする便利な機能です。この機能を使うことで、一度コードを書いた後にエンティティごと作られるインスタンスにそれぞれ違うパラメータを設定する調整ができるようになります。これにより、アーティスト、デザイナーやその他のプログラマーではないチームメンバーがコードを書かずに値を変更できるにプロパティを露出させることができます。
> https://developer.playcanvas.com/ja/user-manual/scripting/script-attributes/



### 情報を保持するためのスクリプトを追加

次に、各モデルに、クリックされた時の情報を追加します。

#### info.jsを作成

`info.js`としてスクリプトを作成します。
このスクリプトの役割は、プログラムを変更せずに変更できる値を保存しておくために存在しています。


```info.js
/*jshint esversion: 6, asi: true, laxbreak: true*/
const Info = pc.createScript('info');
Info.attributes.add("title", {"type": "string", default: "example"})
Info.attributes.add("url", {"type": "string" , default: "https://example.com"})
Info.attributes.add("image", {"type": "asset"})
```

#### スクリプトを適用する

スクリプトを追加すると、それぞれ`title`、`url`、`image`と値を設定できるようになります。クリックされたときにはエンテティのこの値を使用して表示を切り替えるようにします。

![](https://i.imgur.com/zLuMZub.png)


### クリックされたときに値を切り替えるスクリプトを追加
最後にクリックされたときに情報を切り替えるためのスクリプトを追加します。
こちらは`CAMERA`コンポーネントを持っている持っているエンテティに追加します。

![](https://i.imgur.com/AF4JcSe.png)

マウスが押されたときに、直接DOMの切り替えを行なっています。

```picker-raycast.js
/*jshint esversion: 6, asi: true, laxbreak: true*/

const PickerRaycast = pc.createScript('pickerRaycast');

// initialize code called once per entity
PickerRaycast.prototype.initialize = function() {
    
    // マウスがクリックされたときに、onSelect関数を実行します
    this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onSelect, this);
};

PickerRaycast.prototype.onSelect = function (e) {
    const from = this.entity.camera.screenToWorld(e.x, e.y, this.entity.camera.nearClip);
    const to = this.entity.camera.screenToWorld(e.x, e.y, this.entity.camera.farClip);
    
    // クリックされた位置にエンティティがあれば result.entityにentityの情報が入ります
    const result = this.app.systems.rigidbody.raycastFirst(from, to);
     
    if (result) {
        const entity = result.entity;
        // entityにinfoというスクリプトが含まれているかを調べます。
        if(entity.script && entity.script.hasOwnProperty('info')){
        
        // title: string
        // スクリプト名 infoの属性 titleを参照します
        const title = entity.script.info.title
        
        // url: string
        // スクリプト名 infoの属性 url
        const url = entity.script.info.url

        // image: Asset
        // スクリプト名 infoの属性 imageを参照します
        // imageのtypeはAssetなので、getFileUrl()関数を使用してAssetのURLを参照します。
        const image = entity.script.info.image
        const imageUrl = image.getFileUrl()
        
        // ui.jsで追加したHTMLの要素を取得します
        const titleElement = document.getElementsByClassName("title")[0]
        const urlElement = document.getElementsByClassName("url")[0]
        const imgElement = document.getElementsByClassName("image")[0]
        
        // HTMLの要素を書き換えます
        titleElement.textContent = title
        urlElement.textContent = url
        imgElement.src = imageUrl
        }    
    }
};
```

### 感想



すべてのモデルで決まった値を表示したい場合にはこちらの方法を使うことで表示を切り替えることができます。
ただ、複雑に切り替わる場合などには向いていないためシンプルに実装したいときに使っております。

今回のプロジェクトで質問や意見がありましたら。@mxcn3まで連絡をお願いします。


<font color="OrangeRed">
PlayCanvas開発で参考になりそうな記事の一覧です。
</font>
<font color="OrangeRed">
入門

- [PlayCanvas入門- モデルの作成～ゲームに入れ込むまで](https://qiita.com/yushimatenjin/items/2a2c35fe7b1d08f9e361)
- [JavaScriptでスロットを実装する。【PlayCanvas】](https://qiita.com/yushimatenjin/items/e84c42da74df1eb07386)
- [3Dモデルのビューワーを3分で作る【初めてのPlayCanvas】](https://qiita.com/yushimatenjin/items/4b09c9e2076a0b2a6933)
</font>
<font color="OrangeRed">
応用
</font>

- [PlayCanvasのコードエディターでes6に対応する](https://qiita.com/yushimatenjin/items/a61a21c64c1c1a550dd4)
- [Gulpのプラグインを書いたらPlayCanvasでの開発がめちゃくちゃ便利になった](https://qiita.com/yushimatenjin/items/5f0f178e8a4ba4a5ee57)
- [PlayCanvas Editorに外部スクリプトを読み込む新機能が追加されたので開発方法を考える。- Reduxを組み込む](https://qiita.com/yushimatenjin/items/7a64220cceac66843d7d)
</font>
<font color="OrangeRed">
その他の記事はこちらになります。

- [React Native + PlayCanvasを使ってスマートフォンゲームを爆速で生み出す](https://qiita.com/yushimatenjin/items/7c7ad5d35473c11f32f2)
- [PlayCanvasのエディター上でHTML, CSSを組み込む方法](https://qiita.com/yushimatenjin/items/814b4a32db53397219df)
- [【iOS13】新しくなったWebVRの使い方 ](https://qiita.com/yushimatenjin/items/344d86b3d193e0a88567)
</font>

<font color="OrangeRed">
その他関連
- [PlayCanvasタグの付いた記事一覧](https://qiita.com/tags/playcanvas)
</font>


### PlayCanvasのユーザー会のSlackを作りました！
少しでも興味がありましたら、ユーザー同士で解決・PlayCanvasを推進するためのSlackを作りましたので、もしよろしければご参加ください！

[日本PlayCanvasユーザー会 - Slack](https://t.co/IdSRIfJhN5?amp=1)