TypeScriptでResonite開発!! Mirage-X入門ガイド

# TypeScriptでResonite開発‼
皆様、Mirage-Xはご存知でしょうか。Resoniteで生活をしていたら、名前だけは耳にしたことがある方も多いと思います。実はあの[UniPocket](https://uni-pocket.com/ja)の開発にも使用されているフレームワークです。今回は、このフレームワークを使った開発手法について解説します。

## 想定読者
- Resoniteに精通している者
    - 具体的には、インスペクターを難なく扱え、簡単なProtoFluxは何も見ずに書ける程度
- プログラミングに興味がある者
    - 具体的には、例えばProtoFluxに対して忌避感を抱いていない者
- コマンド画面に抵抗がない者
- JavaScriptまたはHTMLについて、ある程度の知識があることが望ましい。

## Mirage-Xは何がすごいのか
Mirage-Xの良い点は以下の通りです。
- リアルタイムにモノを作ることが出来る
    - これはすごいことです。Web開発のホットリロードのような仕組みがあり、コードに変更を加えると即座にResonite内のオブジェクトがリロードされ、書いた通りの動きをするオブジェクトに変化します。
- Gitが使える
    - Gitとは、一言で言えばバージョン管理システムです。Resoniteの開発においては、「どれが最新版のアイテムか？」ということを管理するのが非常に難しいです。Gitを使うことによって、最新版のみならず、周りの人の変更を取り込んだり、元に戻したりということが容易に行えます。
- TypeScriptが使える
    - TypeScriptは現代においてはサーバ、Webページ、スマホアプリなど幅広く使われているプログラミング言語です。つまるところ、Web検索すれば情報は溢れていますし、**生成AIの恩恵を受けることも出来る**という点は強調しておくべきでしょう。世の中のプログラマが開発した便利なライブラリも、そのまま利用できるのはすごいことです。
- 仕組みを秘匿できる
    - Resoniteでのものづくりは、作ったものを誰でも分解できるという世界であるため、仕組みを秘匿化することが難しいです。しかし、Mirage-Xではプログラムを実行した結果(=つまり、見た目)だけを送信してくるため、**APIのアクセスキーなど、公開するとマズイ情報もコードに組み込む**ことが出来ます。

## 取り敢えず動かそう
まず、環境構築を行う必要がありますので、環境を構築していきましょう。`Win + R` を押して出てくる画面に、`cmd`を入力します。そうして出てくる黒い画面に、次のコマンドを入力します。

```
winget install nodejs microsoft.visualstudiocode microsoft.powershell microsoft.windowsterminal git.git
```

このコマンドでは、次のソフトウェアがインストールされます。必要に応じて「管理者として実行」のプロンプトが出ますので、許可をしてください。
- Node.js
    - JavaScriptというプログラミング言語は本来ブラウザで動作する言語です。これをあなたのPCでも動作させるためにこのソフトウェアを必要とします。(『ランタイム』などと呼ばれます。)
- Visual Studio Code
    - テキストファイルを編集するソフトウェアを『エディタ』といいます。プログラミング言語を扱うに当たっては、Notepadなどよりももっと高機能なエディタを使うことが推奨されます。Visual Studio Codeは有名なエディタの一つで、しばしば、「VSCode」「Code」と呼ばれます。他にお好みのエディタがあれば、これはスキップして構いません。
- PowerShell
    - コマンドプロンプトと同様、Windowsをコマンド操作出来るプログラムです。こちらでインストールされるのは「PowerShell」というものです。実はWindowsには標準で「パワーシェル」と名のつくソフトウェアが入っているのですが、こちらは「Windows PowerShell」というもので、青い画面が特徴のソフトウェアです。今回インストールされるものは黒い画面で、バージョンが 7.x.x となっています。

- Windows Terminal
    - コマンドプロンプトやPowerShellのように、コマンドベースで操作するソフトウェアのインターフェースを提供するソフトウェアです。UIをカスタマイズ可能なので、取り敢えず入れておくとよいです。(まあ、入れなくてもいいんですけど…)

- Git
    - Gitは、バージョン管理システムの一つです。ここでは、あくまでGitリポジトリをクローンするための目的で利用しますが、差分によるソースコード管理が出来るため、1つ前のバージョンに戻したり、特定の変更だけを取り込んだりと言った作業が簡単に行えます。

### Step1: クローンする
GitHubなどにアップロードされているものを複製してPC内にダウンロードすることを「クローンする」と表現します。

今回は早速インストールしたVSCodeを使って、Mirage-Xのリポジトリ(≒プロジェクト)をクローンしましょう。

ここをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/c5eab0d2-6208-6e06-7ef3-d6d57f83963d.png)

対象のリポジトリのURLを入力します。ここでは以下通りに入力します。

```
https://github.com/rheniumNV/mirage-x-template.git
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/7f8071fb-c265-e76a-1808-01325c5054ab.png)

入力すると、「どこにクローンしますか？」と聞かれるので適当なフォルダを選択。完了すると「いまクローンしたリポジトリを開きますか？」と聞かれるので "はい" と答えましょう。以上でクローンは終了です。

### Step2: 依存関係を解決する
Mirage-Xはたくさんのプロジェクトを必要とします。この時、「Mirage-Xは〇〇というプロジェクト(パッケージ)に依存している」と表現します。依存するパッケージをダウンロードして使えるように、つまり「依存関係を解決する」必要があるのです。勿論、それらをまとめてインストールできる仕組みが用意されていますので、コマンドを入力して対応します。

以下の画面で、`Ctrl+J`を押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/a3729a10-9057-3213-f748-e9d57a425583.png)

するとコンソールが開きますので、これをクリックし、以下のコマンドを実行します。(スクリーンショットは実行後の画面です。また、`npm i`は `npm install` の省略形です。)

```
npm install
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/34490128-00b8-b827-e357-4c5c44b517ab.png)

このように、 `added n packages, and ...` の表示になればOKです。

#### Step2.1: 脆弱性に対応する
世の中のパッケージには問題が見つかり、悪用されると情報の流出などインシデントに繋がる恐れがあるものをはらんでいる可能性があります。スクリーンショットのように、赤文字で `2 vulnerabilities (1 low, 1 high)` と書いてある場合、深刻度が `high` である問題が見つかっている、ということを意味します。

これはあまり良くないので、修正します。

```
npm audit fix
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/1da67c6c-6635-9ff7-a21a-02aa2bf6d7df.png)

緑色で `0 vulnerabilities` となりました。

### Step3: 動かす!
以下のコマンドを入力します。
```
npm run dev
```

数秒待つと、何やら次のような文言が表示されます。
```
start generating version 1733048887510
end generating version 1733048887510
start compressing
end compressing
generated version 1733048887510
version changed: 0 -> 1733048887510
```

そうすると、Mirage-Xをクローンしたフォルダの `dist/res` フォルダに `output.brson` が生成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/a3f3c3f9-c658-2224-2c8a-e66c724acf81.png)

これをResoniteの画面にドラッグ・アンド・ドロップします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/24d4b417-2be2-a824-256d-3669d67a6b91.png)

やりました！これで、ResoniteにMirage-X製のコンテンツを出現させることが出来ました！

## 箱を出現させてみよう
Mirage-Xは所謂ホットリロードに対応しているため、ソースコードを書き換えると、それがResonite内のオブジェクトにも即座に反映されます。今は写真の通りSamplesがResoniteにありますが、これを別のものに置き換えてみましょう。

`src/core/main/index.tsx`を開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/411bfdf1-d2f1-5eb9-9f9c-55d53c65ad25.png)

内容を以下の内容に上書きし、保存しましょう。

```ts
import { Box } from "../unit/package/Primitive/main";

export const App = () => {
  return <Box />;
};
```

Resoniteの画面を見てみましょう。さっきまでSamplesだった物体が、保存した瞬間Boxに変わりました!

![2024-12-06 13.47.53.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/963f40de-bf94-2207-1419-f22971a1c78a.jpeg)

## Mirage-Xの仕組み

::: note warn
此処から先は、TypeScriptやReactの知識がある前提での説明が続きます。
TypeScriptについては [TS入門 サバイバルTypeScript](https://typescriptbook.jp/)、Reactについては[React スタートガイド](https://ja.react.dev/learn)などを参照するか、生成AIに頼ってください。
:::

Mirage-Xは、私が理解している限りでは、次のように成り立っています。

1. 「Unit」と呼ばれる最小構成のパーツを、Mirage-X側に予め登録しておく。
1. その内容をResonite内で構築・改造する。
1. ResoniteのAPIを利用してオブジェクト本体を取得したあと、内容を展開し、そのUnitごとにファイルを保持しておく。
1. 開発者は、Mirage-Xを利用してプログラミングする
1. Resonite側で、Mirage-Xに登録されているオブジェクトを適切に組み立て直す。

更に詳しい内部の事情は、以下の図を参照してください。
```mermaid
stateDiagram
	ロジック-->ユニットコンポーネント: 呼び出し
	ユニットコンポーネント-->MirageXコントローラー: 同期イベント

	MirageXコントローラー-->MirageXクライアント:同期イベント
	MirageXクライアント-->ユーザー:知覚

	ユーザー-->MirageXクライアント:ボタンを押す
	MirageXクライアント-->MirageXコントローラー:操作イベント
	MirageXコントローラー-->ロジック:関数呼び出し


	state サーバー {
		state メインロジック（React）{
			ロジック
			ユニットコンポーネント
		}
		MirageXコントローラー
	}

	state Resonite {
		state MirageXクライアント {
			ユニットプレハブ
		}
		ユーザー
	}

```
> 出典: Notion [TypeScriptでResoniteのアイテム開発をするMirageX（ミラージュクロス）](https://sharedx.notion.site/TypeScript-Resonite-MirageX-5a5dae2e62f9439d9ddf87f6dafd7f51)

従って、実際にモノを作ろうと思ったら、まずは『Unit』を作る必要があります。
このUnitをパッケージングして多くの人が使える状態にすれば、Mirage-Xもますます発展することでしょう。

## Unitを作る
Unitは、`src/core/unit/package/` に格納されています。`Package` は `Unit` の親にあたり、目的やカテゴリ別に新規作成すると良いでしょう。幸い、Mirage-Xのテンプレートではパッケージのテンプレートが用意されていますので、これを利用してUnitを新規作成していきます。

src/core/unit/package/000_templateを任意の名前でコピーします。
```
cp -r src/core/unit/package/000_template src/core/unit/package/MyPackage
```

この中にUnitのテンプレートがあります。これをリネームして、そのまま使ってしまいましょう。
unitTemplateResFeedbackを任意の名前にリネームします。
```
mv src/core/unit/package/MyPackage/unitTemplateResFeedback src/core/unit/package/MyPackage/SpinningBox
```

続いてMirage-Xに、新しいパッケージを追加したときの処理を自動でやってもらいます。
```
npm run unitPackage:sync
```

最後に、新規作成したPackageをexportします。
- `src/core/unit/main.ts`を編集します。
```tsx
export * as MyPackage from "./package/MyPackage/main";
```
- `src/core/unit/res.ts`を編集します。

```tsx
import { Units as MyPackage } from "./package/MyPackage/res";

export const Units = {
  // 他のパッケージ
  // ...
  MyPackage,
}
```

### Unitの構成
Unitは以下のファイルから構成されます。
- detail.ts
    - Unitの基本情報とPropsを定義します。
- main.tsx
    - detail.tsの内容を元に、ReactElementを生成しているようです。
- res.ts
    - フィードバックに関する何かを担っているようです。
- フィードバック (ResFeedback.json / Resfeedback.yaml / ResFeedbackMeta.json)
    - フィードバックを行うことで追加・更新される、Resoniteのオブジェクトの実体です。

よほど特殊なことをしない限りは、この detail.ts を編集するだけで十分です。
早速編集してみましょう。

### detail.tsを編集する
detail.tsの内容は以下のとおりです。

```tsx
const detail = {
  // Unitの名前
  code: "Template/Empty",

  // UnitのProps
  propsConfig: {
    name: UnitProp.String("Empty"),
  },

  // 子を持てるかどうか?
  children: "multi",
} as const satisfies DetailBase;
```

Unitの名前は、他のものと衝突しない名前である必要があります。無難に、「Package名/Unit名」とするのが良いでしょう。
propsConfigは、そのUnitのPropsを定義します。この項目は、コード側から操作したい値の名前とその型を記述する必要があります。例えばここでは、`name`という名前で、Resonite内Unitの何らかの`String`型プロパティを操作したいということがわかります。

一旦この状態で、Resonite内のオブジェクトを見てみましょう。

`MirageXSampleApp/AppRoot/Main/Package/MyPackage` に、パッケージとユニットが追加されています！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/ee3d0908-1913-6988-6f57-40942d0954c0.png)

### Slotの役目
#### MyPackage/SpinningBox
Rootです。

#### DV
`Ref`がどこにあるかの情報を持つDVがアタッチされています。

#### Ref/Main
対象の物体です。これがUnitの実体です。DV/Staticで正しく指定されていてれば、必ずしもこの名前でなくとも構いません。

#### DV/Static
Ref/Mainがどこにあるかの情報を持つDVがアタッチされています。Ref/Mainが子を持てる場合、どのSlotに子を入れるか？という情報も持っています。

#### DV/Props
details.tsで定義した内容のDynamicFieldがアタッチされています。これとRef/Main内のオブジェクトのパラメータをマッピングすることが出来ます。

このように、対象の物体のフィールドをDynamicField内のTargetFieldに指定することにより、対象の特定のFieldの値をTypeScript側から制御可能になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911135/7b828e17-f8b1-48a5-37d6-d31442c1a797.png)

## フィードバック
Resonite内で加えた変更を取り込むことを、Mirage-Xでは『フィードバック(する)』と表現しているようです。フィードバックするには、以下の下準備をする必要があります。

### 設定
フィードバックをするには、Resonite側でパブリックフォルダを、プロジェクト側で環境変数ファイルを、それぞれ作る必要があります。

#### パブリックフォルダ
インベントリ内にフォルダを作成し、それを公開した後、URLを取得します。

#### 環境変数ファイル
`.env.sample`というファイルをコピーして、`.env`というファイルを作ります。
作ったファイルを開き、中の `FEEDBACK_LINK`という変数の値を、先程取得したURLの内容に置き換えます。

```
FEEDBACK_LINK="resrec:///U-Sweshelo/R-XXXXXXXXXXXXXXXXXXXXXXXX"
```

### Feedback

フィードバックを行うには、まず変更を加えたMirageXSampleAppを丸ごと、先程作ったパブリックフォルダ内に保存します。

SpinnigBoxという名前で作りましたので、Ref/Mainを、Spinnerアタッチ済みのBoxに変えてから保存すると、違いがよく分かるかもしれません。

:::note warn
Mirage-Xでは、変更を加えられたUnitプレハブの座標を自動リセットしたりはしません。そのため、対象ユニットの出現位置をMirage-Xの中心座標としたい場合、位置をリセットしておくことを強くおすすめします。
:::

保存と同期が完了したら、再びVSCode側での作業に戻ります。以下のコマンドを実行し、フィードバックします。
```
npm run feedback:unit MyPackage/.*
```

以下のような出力になります。(パッケージ名が違う方は、適切に設定してください。パッケージ名には正規表現が使えます。指定しない場合、全てのパッケージがフィードバックされます。)
```
> mirage-x-template@0.0.1 feedback:unit
> npm run feedback:unit:fetch & npm run feedback:unit:attach MyPackage/.*


> mirage-x-template@0.0.1 feedback:unit:fetch
> ts-node --require dotenv/config -P ./tsconfig.dev.json ./src/dev/resFeedback/fetch

updated  2024-12-06T15:48:56.8305984Z --> 2024-12-07T03:51:24.9017227Z

> mirage-x-template@0.0.1 feedback:unit:attach
> ts-node --require dotenv/config -P ./tsconfig.dev.json ./src/dev/resFeedback/attachUnit MyPackage/.*

matchPattern=MyPackage/.*
unit: 1 / 35
attached to MyPackage/SpinningBox
```

APIがリアルタイムでないのか、たまにうまくフィードバックされないことがあります。根気よく待つか、再度Resonite側で保存し直してやり直しましょう。`attached`の文字が出れば、成功です。

## 利用する
`src/core/main/index.tsx` を編集し、先程フィードバックした内容でUnitが利用できるか、やってみましょう。

```tsx
import { SpinningBox } from "../unit/package/MyPackage/main";

export const App = () => {
  return <SpinningBox />;
};

```

うまく行けば成功です！SpinnerのSpeedを操作するためのPropsを作って、

```tsx
import { SpinningBox } from "../unit/package/MyPackage/main";

export const App = () => {
  return <SpinningBox speed={[0, 360, 0]}/>;
};
```

のようにすることで、コード側でスピードを制御可能です。

## まとめ
TypeScriptでResonite開発が行えるため、UniPocketのようにバックエンドがあってこそのサービス構築には非常に強力な選択肢です。Mirage-Xには、予めスタイリングされたUIXのUnitが付属しているため、これを利用してなにか作ってみてはいかがでしょうか。

今後は、onClickイベントなど、ResoniteからMirage-X側のイベントを発火させる方法なども調査して取り上げたいと思います。お読み頂き、ありがとうございました。

Packageを作ったら、是非メンションください‼️