【PlayCanvas(JavaScript)】PlayCanvasを触ってみた

##PlayCanvas

>PlayCanvasは、デスクトップとモバイルブラウザ向けに作られた
WebGL/HTML5ゲームエンジンです。豊富な機能を揃えた3Dエンジンと
クラウドホスティングされた開発環境およびツールセットを備えています。

引用元：[PLAYCANVAS](https://playcanvas.jp/)

サンプルも非常に多くチュートリアルも丁寧でありがたいです。
Unityに似たところもあるのでUnityエンジニアの方はとっつきやすいと思います。
**※ただしJavaScript**

私自身Unityをメインウエポンとしてお仕事やらせていただいているので
ところどころUnityと比較しながらメモしていきます。

今回はPlayCanvasに慣れるためにチュートリアルの簡単そうなことを中心に試してみます。

**JavaScriptを書いたことない**ので酷いこと書いてるかもしれません。
どうか優しくしてやってください。

##下準備
アカウントを作成してログインしたらHomeからプロジェクトを作成します。
![PlayCanvasプロジェクト作成.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/208081e5-4258-d84c-9bc0-3975e2db6f38.png)

##Entity
まず最初に理解しないといけないのは`Entity`かな～と思いました。

**PlayCanvasはゲーム内の全てのオブジェクトが`Entity`**と呼ばれます。
Unityで言うところの`GameObject`みたいな感じです。

Editor上の`HIERARCHY`から新しい`Entity`を生成すると下記画像のように
**何の振るまいも持たない空の`Entity`**が生成されます。


![Entity.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/ef2bc87e-f0c4-839d-75fe-1a25de5f29a9.png)

**`ADD COMPONET`を選択していろいろな振るまい(Component)を追加する**ことができます。

【参考リンク】：[エンティティ](https://developer.playcanvas.com/ja/user-manual/packs/entities/)

##Script作成

先ほどの`ADD COMPONET`で追加する`振る舞い`ですが、
**自分で独自のものを定義**することができます。

それが`Script`と呼ばれるものです。

Editor上のASSETS配下にJavaScriptのファイルを作成してその中に記述していきます。

試しに`test`というScriptを作成してみます。
([Lower camel case](https://qiita.com/terra_yucco/items/ec437c6005932fd73fb9)で作成するのが慣習？)

```JavaScript:何も追記してないデフォルトの状態
var Test = pc.createScript('test');

// initialize code called once per entity
Test.prototype.initialize = function() {
    
};

// update code called every frame
Test.prototype.update = function(dt) {
    
};

// swap method called for script hot-reloading
// inherit your script state here
// Test.prototype.swap = function(old) { };

// to learn more about script anatomy, please read:
// http://developer.playcanvas.com/en/user-manual/scripting/
```



---
###initialize
`initialize`はUnityで言うところの`Awake`や`Start`とほとんど同じように振るまいます。

簡潔に述べると初期化時に各Entityごとに呼び出されます。
`初期化時とはいつか？`というのは下記のような記述が公式チュートリアルに記述がありました。
>最初の更新ループまたはフレームのレンダリングされる前に呼び出されます

【参考リンク】：[スクリプトの構造](https://developer.playcanvas.com/ja/user-manual/scripting/anatomy/)

Entity及びScriptがenableになったタイミングでinitializeが呼ばれる辺りも
Unityと似ています。

【参考リンク】：[【PlayCanvas】Tips](https://scrapbox.io/DevelopmentTips/%E3%80%90PlayCanvas%E3%80%91Tips)

---
###update
`update`はUnityで言うところの`update`とほとんど同じように振るまいます。

**引数の`dt`はDeltaTimeのことで、1フレーム分を更新するために経過した時間**を指します。

`postUpdate`と呼ばれるUnityで言うところのLateUpdate？のような
振るまいをしてくれそうなメソッドもありました。

>postUpdateは、すべてのスクリプトが更新された後に呼び出されるupdateメソッドです。すべてのスクリプトが更新されていると仮定することができる機能を実行するために使用します。例えば、他のエンティティを追跡するカメラは、別のエンティティがフレームで動作を完了できるように、postUpdateでその位置を更新する必要があります。


【参考リンク】：[スクリプトの構造](https://developer.playcanvas.com/ja/user-manual/scripting/anatomy/)

##Attribute
`Attribute`はUnityで言うところの
`[SerializeField]`や`public`で宣言したフィールド変数のような振るまいをしてくれます。

簡潔に述べると、**アトリビュートはEntity同士で参照を持たせる際に便利**な機能です。

下記にサンプルを用意しました。
`log`という文字通りログを出すだけのScriptを作成し、
`test`から参照してメソッドを呼び出してみます。

```JavaScript:log：適当なEntityにアタッチ
var Log = pc.createScript('log');

//testで呼び出してもらう
Log.prototype.successRef = function(){
    console.log('他のScriptからの参照に成功');
};

```

```JavaScript:test：適当なEntityにアタッチ
var Test = pc.createScript('test');

Test.attributes.add('logEntity', {type: 'entity', title: 'Log Entity'});

Test.prototype.initialize = function() {
    //アトリビュートで参照したEntityからScriptコンポーネントを取得
    var logScript  = this.logEntity.findComponent('script');
    //取得したScriptコンポーネントからLogを出すメソッドを呼び出す
    logScript.log.successRef();
};
```

`Test.attributes.add`の箇所がアトリビュートです。
**今回のコードで言うと`logEntity`がコード内で参照可能な変数名**です。
**typeの後は型名のようなもの**です。
`string`や`boolean`もこのtypeで指定できると言えばわかりやすいかもしれません。

あとはUnityでInspectorにアタッチする要領で
Editor上でドラッグアンドドロップすればOKです。
ただし、下記の**パースボタンを一度押下しないとアトリビュートが表示されない**ので要注意です。

![PC_ref.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/e071598a-7ee4-3f7c-8c78-82cd40393d4c.png)


実行してブラウザ上でF12キーを押してコンソールを開いてみます。
無事、他のScriptのメソッド呼び出しができました。
![PC_log.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/64d16cfe-f635-e060-c941-4ba1bda9397a.png)

---
###Scriptが複数の場合
Scriptが複数の場合を見ていきます。
例として`log2`というScriptを`log`を既に設定してあるEntityに設定してみます。
![PC_log2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/b7d421b8-5e69-c63b-1f5f-b161f3cb7f65.png)

```JavaScript:log2：logがアタッチされたEntityにアタッチ 
var Log2 = pc.createScript('log2');

//testで呼び出してもらう
Log2.prototype.successRef = function(){
    console.log('Log2:他のScriptからの参照に成功');
};
```

```JavaScript:test：適当なEntityにアタッチ
var Test = pc.createScript('test');

Test.attributes.add('logEntity', {type: 'entity', title: 'Log Entity'});

Test.prototype.initialize = function() {
    var logScript  = this.logEntity.findComponent('script');
    logScript.log.successRef();
    logScript.log2.successRef();
};
```

Scriptの中に追加した`log`、`log2`のメソッドをそれぞれ呼び出せました。
![PC_log2出力.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/bbfdf26e-3a69-b59c-5b62-8c7afa08bdc5.png)

`findComponent('script')`でそのEntityにアタッチされたScriptを取得すれば、
全部まとめて利用することができるのは便利だなと思いました。

##おわりに
まだまだPlayCanvasの1パーセント程度も触り切れていませんが
入門の入門レベルでは理解できました。

VR、ARもできる？っぽいのでその辺りも触っていきたいです。