Grimoire.jsのプラグインをサクッと作る

[Grimoire.js](https://grimoire.gl/)のプラグインをサクッと作ります。

# そもそもプラグインとは？
一言で言えば、ただのnpmパッケージです。
npmで他のGrimoire.jsプラグインを利用したり、ビルドして ` html`にスクリプトタグで読み込むことができます。
Grimoire.jsの機能は**[GOMLタグ](https://grimoire.gl/guide/1_essentials/03_goml.html)**で構成されるので、プラグイン開発は主に**新しいタグを追加する**作業になります。

# 必要環境
#### Typescript
推奨です。書きましょう。
#### Node.js
npmなので。
#### ローカルwebサーバ
建てられるとデバッグで便利です。というかないとデバッグできない

# プロジェクト作成
Grimoire.jsプラグインは、一応[仕様](https://grimoire.gl/guide/2_advanced/plugin-specification.html)のようなものがあるのでゼロから作ってもいいのですが、さすがに辛いので **[grimoirejs-cauldron](https://github.com/GrimoireGL/grimoirejs-cauldron)**というたいへん便利なツールを使います。

## grimoirejs-cauldron
パッケージ生成、コンポーネントファイル生成、ビルド環境などが全部入ってます。
公式のパッケージもすべてこれを使ってます。

### インストール
```shell-session
$ npm install -g grimoirejs-cauldron
```
### パッケージテンプレート生成
空のフォルダで

```shell-session
$ cauldron init -n <パッケージ名>
```

やってることは、[ts-boilerplate](https://github.com/GrimoireGL/ts-boilerplate)をクローンしてきて名前とかを書き換えてるだけです。
[ts-boilerplate](https://github.com/GrimoireGL/ts-boilerplate)を直接クローンしてもOKです。

# プロジェクトのビルド

クローンしただけでは依存関係はインストールされないので、

```shell-session
$ npm install
```
して入れます。

ビルドは、

```shell-session
$ npm run build
```
です。 **ただし、デフォルトではNode環境向けビルド**なので、htmlでスクリプトタグで読み込みたい場合は、

```shell-session
$ npm run build -- --env.browser
```

とします。ちなみに、

```shell-session
$ npm run start -- --env.browser
```
でwatchできます。(ファイル変更を監視して自動でビルドしてくれる。)

# プロジェクトを使う。
ビルドすると、 `register`フォルダの中に`<パッケージ名>.js`と`index.js`があると思います。
`index.js`はノード環境向けです。
`html`からは、`<パッケージ名>.js`のほうを使います。

```html
<html>
  <head>
    <script src="https://unpkg.com/grimoirejs-preset-basic/register/grimoire-preset-basic.min.js" charset="utf-8"></script>
    <script src="/path/to/proj/register/<パッケージ名>.js" charset="utf-8"></script>
  </head>
```
ブラウザで開くと、コンソールに


<img width="310" alt="23ed4d5b39c46c7b09fd95326fd0a9c7.png" src="https://qiita-image-store.s3.amazonaws.com/0/116979/013f08a4-b624-06a9-7045-14208bb59c9d.png">

という感じで表示されます。`Plugins:`の最後に自分のパッケージが表示されていたら成功です！

# プラグイン実装
コンポーネントの中身をつくります
まずは、テンプレートを生成

### ファイルテンプレート
```shell-session
$ cauldron scaffold -n <コンポーネント名> -t component
```

```shell-session
$ cauldron scaffold -n <コンバータ名> -t converter
```

たとえば、 `cauldron scaffold -n　Rotate -t component`で`Rotate`コンポーネントを作ってみます。`src/Components`の中に、`RotateComponent.ts`が作られるはずです。

```ts:RotateComponent.ts
import Component from "grimoirejs/ref/Node/Component";
import IAttributeDeclaration from "grimoirejs/ref/Node/IAttributeDeclaration";

export default class RotateComponent extends Component {

      public static componentName:string = "Rotate";

      public static attributes: { [key: string]: IAttributeDeclaration } = {
        // Specify the attributes user can intaract
      };
}
```

こんな感じで生成されます。とりあえず、ノードをくるくる回す機能を実装してみます。

```ts:RotateComponent.ts
(〜略)
      public static attributes: { [key: string]: IAttributeDeclaration } = {
        // Specify the attributes user can intaract
        speed:{
          converter:"Number",
          default: 0.2
        }
      };

      private _r=0;

      public $update(){
        this._r+=this.getAttribute("speed");
        this.node.setAttribute("rotation",[0,this._r,0]);
      }

(略〜)
```

`speed`属性を作って、それを`_r`に足していって、ノードの`rotation`属性に設定しています。
**属性**とか`$update`ってそもそも何？という方は[公式ガイド](https://grimoire.gl/guide/1_essentials/05_componentsystem.html)が詳しいです。

これだけでは、まだ作っただけなので、`main.ts`で**本体に登録する処理を書く必要があります**。

```ts:main.ts
import gr from "grimoirejs";
import Rotate from "./Components/RotateComponent";

export default ()=>{
  gr.register(async ()=>{
    gr.registerComponent(Rotate);
  });
};

```
もともとテンプレがあるので、`gr.registerComponent(Rotate);`を一行追加するだけです。

追記）
古いバージョンでは、

```ts:main.ts
    gr.registerComponent("Rotate",Rotate);
```
となっていましたが、最近のバージョン(0.19.3?)で登録するオブジェクトの`componentName`を参照するように変更になりました。古い書き方も使えますが非推奨です。

# プラグイン使う
コンポーネントを作ったので、GOMLでそれをつかってみます

```xml:index.goml
<goml>
  <scene>
    <camera/>
      <mesh>
        <mesh.components>
          <Rotate/>
        </mesh.components>
      </mesh>
  </scene>
</goml>

```

完成！

# おまけ：よくあるミス
- ビルドしてない
- `main.ts`に登録処理がない
- htmlにプラグインのスクリプトタグを書いてない
- `setAttribute`してる属性のスペルミス。