5 分で出来るGrimoire.jsプラグイン作成

# Grimoire.jsのプラグイン制作について

オブジェクトの移動や回転はGrimoire.jsの表現を記述する際によく使用します。このような一連のロジックを一つの機能としてまとめることは開発の効率化につながります。この機能の一まとまりをGrimoire.jsではコンポーネントと呼んでいます。今回は自分がよく使うであろう機能を実際にコンポーネントととして作成してみました。

Grimoire.jsではコンポーネントはGOMLに記述することで、対象のノード(`<mesh/>`など)に適用することができます。

```xml:GOMLの例
<mesh geometry="cube">
  <mesh.components>
    <!-- ここに適用するコンポーネントのタグを記述する-->
  </mesh.components>
</mesh>
```

プラグイン制作者はコンポーネントを作成したり、GOMLに記述されるタグを自由に定義したりすることになります。

## スキャフォールディングツールの使用

プラグイン制作はNode.jsの環境で行います。
Grimoire.jsにはプラグイン制作支援のためのスキャフォールディングツールが用意されているので、それを使用します。`grimoirejs-cauldron`を導入することで可能になります。

```
$ npm install -g grimoirejs-cauldron
$ mkdir grimoirejs-movement-toolkit
$ cd grimoirejs-movement-toolkit
$ cauldron init
```

> * Project名は`grimoirejs-`のプレフィックスをつける必要があります。
* TypeScriptを使用するか聞かれます。今回は使用するので`true`にしました。

以上のコマンドで、開発環境は整いました。Grimoire.jsのプラグインはスキャフォールディングツールを使わなくても、[プラグイン仕様](http://grimoire.gl/guide/plugin-specification.html)に従うことで自由に作成できます。
ディレクトリ構成とpackage.jsonはこんな感じ。

>
root/
　├ package.json
　├ tsconfig.json
　├ src/
　│└ index.ts
　│└ main.ts
　│└ raw.d.ts
　├ .gitignore
　└ webpack.config.js

```json:package.json
{
  "name": "grimoirejs-movement-toolkit",
  "version": "1.0.0",
  "description": "",
  "main": "./ref/index.js",
  "typings": "./ref/index.d.ts",
  "files": [
    "src",
    "ref",
    "register"
  ],
  "keywords": [
    "grimoire",
    "grimoirejs"
  ],
  "dependencies": {
    "grimoirejs": "latest",
    "grimoirejs-math": "latest",
    "grimoirejs-fundamental": "latest"
  },
  "devDependencies": {
    "babel-cli": "^6.18.0",
    "babel-core": "^6.18.2",
    "babel-loader": "^6.2.8",
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-stage-2": "^6.18.0",
    "grimoirejs-cauldron": "^2.1.0",
    "raw-loader": "^0.5.1",
    "ts-loader": "^1.2.2",
    "typescript": "^2.0.9",
    "webpack": "^1.13.3",
    "webpack-shell-plugin": "^0.4.3",
    "yargs": "^6.4.0"
  },
  "repository": "",
  "scripts": {
    "start": "webpack --watch --progress",
    "test": "echo 'add your own test'",
    "prepublish": "npm run build -- --prod&&npm test",
    "build": "webpack --progress",
    "generate-expose": "cauldron generate-exposure --src ./src --dest ./src/index.ts --ts --main ./src/main.ts --dts ./ref",
    "generate-reference": "cauldron generate-reference --src ./src --dest ./src/index.ts --ts --main ./src/main.ts --dts ./ref"
  },
  "license": "MIT"
}
```

## コンポーネントの作成について

今回はコンポーネントのみ作成します。

基本的には初めに
* GOMLに露出させる属性の定義
* 定義した属性をローカル変数にバインドする
* 毎フレーム呼び出される`$update()`内でロジックを記述する

ことが必要になるでしょう。
その他必要な`Vector3`や`Quaternion`などは`プラグイン名/ref/名称`の形で呼び出して使うことが可能です。

以下はコンポーネントの一例として、RotateAroundコンポーネントを取り上げています。

```js:RotateAroundComponent.ts
import Component from "grimoirejs/ref/Node/Component";
import IAttributeDeclaration from "grimoirejs/ref/Node/IAttributeDeclaration";
import Vector3 from "grimoirejs-math/ref/Vector3";
import Quaternion from "grimoirejs-math/ref/Quaternion";
import Matrix from "grimoirejs-math/ref/Matrix";
import TransformComponent from "grimoirejs-fundamental/ref/Components/TransformComponent";
import Attribute from "grimoirejs/ref/Node/Attribute";
export default class RotateAroundComponent extends Component {

    public static componentName: string = "RotateAroundComponent";

    public static attributes: { [key: string]: IAttributeDeclaration } = {
        center: {
            defaultValue: "0,0,0",
            converter: "Vector3"
        },
        speed: {
            defaultValue: 1,
            converter: "Number"
        },
        axis: {
            defaultValue: "0,1,0",
            converter: "Vector3"
        }
    };
    private _transform: TransformComponent;
    private _center: Vector3;
    private _speed: number;
    private _axis: Vector3;
    private distance: number;
    public $awake(): void {
        this.getAttribute("center").boundTo("_center");
        this.getAttribute("speed").boundTo("_speed");
        this._speed *= 0.05;
        this.getAttribute("axis").boundTo("_axis");
        this._axis = this._axis.normalized;
        this._transform = this.node.getComponent("Transform") as TransformComponent;
    }
    public $mount(): void {
        this.distance = this._transform.localPosition
            .subtractWith(this._center)
            .magnitude;
    }
    public $update(): void {
      const rotationMat = Matrix.rotationQuaternion(Quaternion.angleAxis(this._speed, this._axis));
      this._transform.localPosition = Matrix.transformNormal(rotationMat,this._transform.localPosition.subtractWith(this._center)).addWith(this._center);
    }
}
```

制作したコンポーネントには`registerComponent`メソッドを使用します。`main.ts`にコンポーネントを適用する際のタグ名とコンポーネントを渡すだけで、登録は完了します。
あとはHTML内で`register/プラグイン名.js`を読み込むだけで実行できます。

> デバッグの際にはプリセットプラグインが導入された`grimoire-preset-basic.min.js`も同時に読み込むことが必要になるでしょう。

```js:main.ts
import gr from "grimoirejs";
import RotateAroundComponent from "./Components/RotateAroundComponent";
export default () => {
    gr.register(async () => {
        gr.registerComponent("RotateAround", RotateAroundComponent);
};
```

実際につくったコンポーネントはGOMLで以下のように記述しています。

```xml
<mesh geometry="cube">
  <mesh.components>
    <RotateAround/>
  </mesh.components>
</mesh>
```

(使用例)
![sample-cube.gif](https://qiita-image-store.s3.amazonaws.com/0/147288/803a1d8d-f8b9-a2a2-6121-7fad9e19b72f.gif)

必要なコンポーネントも簡単に自作することができました。Grimoire.jsでWebGL表現の幅を広げるためにはプラグインの作成が有効です。スキャフォールディングツールを使用すると比較的容易にプラグインを作成することできます。一度プラグインを作ってみるとその容易さを実感できると思います。