PlayCanvas Editorに外部スクリプトを読み込む新機能が追加されたので開発方法を考える。- Reduxを組み込む

## はじめに
PlayCanvas EditorにExternal Scriptsという外部スクリプトを読み込むための機能が追加されました。  
この機能ではエディターで設定をした外部のスクリプトを、起動時に<head>タグに挿入する機能となっております。機能自体はこの歯車の設定画面に追加されています。
 <img src="https://user-images.githubusercontent.com/39250588/62590230-2c1cf080-b8bb-11e9-894f-21abee1d7c61.PNG"  width=500 />


### 外部スクリプトを追加する
今までのPlayCanvasではJavaScriptを埋め込む場合にはエディター上で埋め込むスクリプトを書かなければいけませんでしたがこのアップデートによりjQueryやFirebaseなどのSDKを埋め込む場合にもURLで指定が指定できるため、常に最新版を取ってくることができたりと便利になりました。  
![firebaseauth](https://user-images.githubusercontent.com/39250588/62590667-9f733200-b8bc-11e9-8a12-097d6e85b97b.PNG)  

追加したスクリプトはエディター上で起動をしたときにタグのPlayCanvasが読み込まれる前に挿入されます。  
![Head123](https://user-images.githubusercontent.com/39250588/62590852-40fa8380-b8bd-11e9-87a3-6fc62916cbdf.PNG)  


<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">朝起きたらPlayCanvasで外部スクリプトを読み込むのが簡単になったのでFirebaseとつないでみました。<br> <a href="https://t.co/c2GmILSFK7">https://t.co/c2GmILSFK7</a></p>&mdash; はが (@Mxcn3) <a href="https://twitter.com/Mxcn3/status/1158587699340595201?ref_src=twsrc%5Etfw">August 6, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## PlayCanvasのプロジェクトはこちらになります。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">External Scriptsで外部スクリプトを追加できるようになったのでRedux x PlayCanvasを試してみた。<br>Stateの値によってカメラが寄るサンプルです。 <a href="https://twitter.com/hashtag/playcanvas?src=hash&amp;ref_src=twsrc%5Etfw">#playcanvas</a><a href="https://t.co/WaT9W5Gzbj">https://t.co/WaT9W5Gzbj</a></p>&mdash; はが (@Mxcn3) <a href="https://twitter.com/Mxcn3/status/1158935672998117376?ref_src=twsrc%5Etfw">August 7, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

(公開リポジトリなのでフォークができます)
[https://playcanvas.com/project/631609/](https://playcanvas.com/project/631609/)


### counter-vanillaを追加する  
PlayCanvasはReactではないので、Reduxのサンプルの中の[counter-vanilla](https://github.com/reduxjs/redux/tree/master/examples/counter-vanilla)を使用してReduxの導入をします。  


```counter-vanilla/index.html
<!DOCTYPE html>
<html>
  <head>
    <title>Redux basic example</title>
    <script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>
  </head>
  <body>
    <div>
      <p>
        Clicked: <span id="value">0</span> times
        <button id="increment">+</button>
        <button id="decrement">-</button>
        <button id="incrementIfOdd">Increment if odd</button>
        <button id="incrementAsync">Increment async</button>
      </p>
    </div>
    <script>
      function counter(state, action) {
        if (typeof state === 'undefined') {
          return 0
        }
        switch (action.type) {
          case 'INCREMENT':
            return state + 1
          case 'DECREMENT':
            return state - 1
          default:
            return state
        }
      }
      var store = Redux.createStore(counter)
      var valueEl = document.getElementById('value')
      function render() {
        valueEl.innerHTML = store.getState().toString()
      }
      render()
      store.subscribe(render)
      document.getElementById('increment')
        .addEventListener('click', function () {
          store.dispatch({ type: 'INCREMENT' })
        })
      document.getElementById('decrement')
        .addEventListener('click', function () {
          store.dispatch({ type: 'DECREMENT' })
        })
      document.getElementById('incrementIfOdd')
        .addEventListener('click', function () {
          if (store.getState() % 2 !== 0) {
            store.dispatch({ type: 'INCREMENT' })
          }
        })
      document.getElementById('incrementAsync')
        .addEventListener('click', function () {
          setTimeout(function () {
            store.dispatch({ type: 'INCREMENT' })
          }, 1000)
        })
    </script>
  </body>
</html>
```  

`counter-vanilla/index.html`はindex.htmlだけあれば特にフレームワークを必要としないサンプルとなっています。
それではこのScriptをPlayCanvasに組み込んでいきましょう

1. External ScriptsへReduxの最新版を追加
index.html内のScript srcで読み込まれているスクリプトをExternal Scriptsの中へ追加します。

 <img src="https://user-images.githubusercontent.com/39250588/62591105-10671980-b8be-11e9-9ed3-e13e8d409873.PNG"  width=500 />

`https://unpkg.com/redux@latest/dist/redux.min.js`

2.HTML, CSSをPlayCanvas Editorに追加します。

```index.html
      <p id="counter">
        Clicked: <span id="value">0</span> times
        <button id="increment">+</button>
        <button id="decrement">-</button>
      </p>
```

```style.css
#counter{
    position: absolute;
    z-index: 1;
    color: red;
}

button{
    padding: 10px;
}
```



3. Store, HTML,CSSの表示をする部分のScriptを追加しAttributesに追加をする
HTMLをPlayCanvas上に追加をする為にAttributesとしてEditor上で先ほどEnditor上に追加をしたHTMLとCSSセットします。

 <img src="https://user-images.githubusercontent.com/39250588/62591886-a9972f80-b8c0-11e9-91d0-a7356e18a754.PNG"  width=500 />

```Ui.js
constUi = 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"
});
    
const counter = (state, action) => {
    if (typeof state === 'undefined') {
      return 1
    }
    switch (action.type) {
      case 'INCREMENT':
        return state + 1
      case 'DECREMENT':
        return state - 1
      default:
        return state
    }
}

const store = Redux.createStore(counter)

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("afterbegin", this.html.resource);
  head.insertAdjacentHTML("afterbegin", style);
  //HTML, CSSを挿入する
    
  var valueEl = document.getElementById('value')
  function render() {
    valueEl.innerHTML = store.getState().toString()
  }
  render()
  store.subscribe(render)
  document.getElementById('increment')
    .addEventListener('click', function () {
      store.dispatch({ type: 'INCREMENT' })
    })
  document.getElementById('decrement')
    .addEventListener('click', function () {
      store.dispatch({ type: 'DECREMENT' })
    })

};
```
このScriptを追加することでPlayCanvas上にカウンターが追加されました。
 <img src="https://user-images.githubusercontent.com/39250588/62592236-f3cce080-b8c1-11e9-998d-29bfdc920dd4.PNG"  width=500 />



## コンポーネントにStateの値を適用させる

### テキストコンポーネント
このままだと、HTMLを上にかぶせているだけです、Reduxの状態を参照してインクリメントした文字に変更するエンティティを追加します。　　
1. `2D Screen`を追加　　
 <img src="https://user-images.githubusercontent.com/39250588/62592108-73a67b00-b8c1-11e9-9076-db32b79ddf6d.PNG"  width=500 />

2. `2D Screen`以下に`Text`エンティティを追加
3. フォントを追加　　
4. 
<img src="https://user-images.githubusercontent.com/39250588/62592190-c7b15f80-b8c1-11e9-82c9-637f757dd966.PNG"  width=500 />

4.Scriptを追加する
<img src="https://user-images.githubusercontent.com/39250588/62592308-41e1e400-b8c2-11e9-9662-a017392bad2a.PNG"  width=500 />

`Text`エンティティに対して`text.js`というスクリプトを追加します。

```text.js
const Text = pc.createScript('text');
Text.prototype.update = function(dt) {
    this.entity.element.text = store.getState().toString()
};
```

### カメラの値を変更する

1. `Camera`エンティティに`camera.js`を追加する

```camera.js
const Camera = pc.createScript('camera');

Camera.prototype.update = function() {
     this.entity.camera.fov = store.getState() * 2 - 30
};
```

これでReduxのアクションにより`Camera`エンティティの`fov`が寄る、`Text`エンティティの値を変更することができました
![output](https://user-images.githubusercontent.com/39250588/62592654-80c46980-b8c3-11e9-95c3-df0cc7ffcc44.gif)


## PlayCanvasのプロジェクトはこちらになります。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">External Scriptsで外部スクリプトを追加できるようになったのでRedux x PlayCanvasを試してみた。<br>Stateの値によってカメラが寄るサンプルです。 <a href="https://twitter.com/hashtag/playcanvas?src=hash&amp;ref_src=twsrc%5Etfw">#playcanvas</a><a href="https://t.co/WaT9W5Gzbj">https://t.co/WaT9W5Gzbj</a></p>&mdash; はが (@Mxcn3) <a href="https://twitter.com/Mxcn3/status/1158935672998117376?ref_src=twsrc%5Etfw">August 7, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


<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)