🥝

ScriptableItemでTypeScriptを活用してみた

2022/12/03に公開

クラスター Advent Calendar 2022 3日目

この記事はCluster Creator KitのScriptable Itemの開発でTypeScriptを使ってみた紹介記事です。

TypeScriptはそのままではScriptable Itemで使うことができないので、一度ビルドツールを使ってJavaScriptに変換する必要があります。
この記事では変換にRollupライブラリを使い、TypeScriptからJavaScriptに変換してかつ複数ファイルを1つに纏めることも行います。

想定読者

JavaScriptの書き方は分かるけどNode.jsやTypeScriptの導入方法は分からない、導入してみたいという人。

この記事では書かない事

Cluster Creator Kitの使い方の説明
Cluster Creator Kitの使い方は公式ドキュメントCreators Guideが非常に参考になるのでこの記事ではScriptable Item以外にはとくに触れません。

設定ファイルの説明
今回使ったTypeScriptやRollup(ビルドツール)は、設定ファイルを書く事でビルド方法のカスタムができます。
それらの設定方法を紹介をするとそれだけで1記事できてしまうので、今回は設定ファイルを含めたソースコードを公開することで、設定ファイルの説明は省略しています。

成果物

src/items/gun/main.ts元のファイルの1つ

import { createThrottle } from "../../utils/throttle";
...

const shoot = (deltaTime: number) => {
  const isUseDown = stateClient.getState("isUseDown");
  const bulletChargeCount = stateClient.getState("bulletChargeCount") ?? 0;
  if (isUseDown || bulletChargeCount <= 0) {
    return;
  }
  shootThrottle(
    () => {
      $.log(`shoot ${bulletChargeCount}`);
      cckClient.sendSignal("signal/action/shoot");
      stateClient.setState("bulletChargeCount", bulletChargeCount - 1);
    },
    deltaTime,
    SHOOT_INTERVAL,
  );
};
...

これをビルドコマンドでJavaScriptにすると以下のようになります。

dist/gun.jsビルド結果

'use strict';
const createThrottle = (key) => {
    const stateClient = getStateClient(`throttle_${key}`);
    return (fn, deltaTime, waitSecond) => {
        var _a;
        const currentPassedTime = (_a = stateClient.getState("passedTime")) !== null && _a !== void 0 ? _a : 0;
        const nextPassedTime = currentPassedTime + deltaTime;
        stateClient.setState("passedTime", nextPassedTime);
        if (nextPassedTime >= waitSecond) {
            stateClient.setState("passedTime", 0);
            fn();
        }
    };
};

...

const shoot = (deltaTime) => {
    var _a;
    const isUseDown = stateClient.getState("isUseDown");
    const bulletChargeCount = (_a = stateClient.getState("bulletChargeCount")) !== null && _a !== void 0 ? _a : 0;
    if (isUseDown || bulletChargeCount <= 0) {
        return;
    }
    shootThrottle(() => {
        $.log(`shoot ${bulletChargeCount}`);
        cckClient.sendSignal("signal/action/shoot");
        stateClient.setState("bulletChargeCount", bulletChargeCount - 1);
    }, deltaTime, SHOOT_INTERVAL);
};
...

今回試してみたソースコードは https://github.com/warabiiiii/script-sample に置いてあります。
以下今回のソースコードを試すための環境構築手順を紹介します。

導入手順

Node.jsの導入

TypeScriptやRollup(ビルドツール)を使うために、JavaScript実行環境のNode.jsをインストールします。
今回はもっとも単純な方法である公式サイトから直接ダウンロードする手順でインストールをします。バージョンは推奨版のv18.12.1を使います。
Node.js公式

ターミナルでnode --versionとコマンドを入力した時にバージョン情報を出力できれば、インストールが成功しています。

node --version
> v18.12.1

必要ライブラリのインストール

以降のコマンドは作業ディレクトリをpackage.jsonが存在するディレクトリ(今回の例ではscript-sample/Assets/Script)にした状態で行います。
npm installコマンドを実行することでpackage.jsonで設定済みの各種ライブラリをインストールします。
npmコマンドはNode.js導入時に追加されているはずなので、npmコマンドが見つからない時はNode.jsの導入になんらか問題が起きている可能性があります。

$ npm install                                                                                                                                        (setup)

added 198 packages, and audited 199 packages in 26s

77 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

コマンドの実行が完了するとnode_modulesディレクトリに各種ライブラリがインストールされています。

$ ls ./node_modules 
@clustervr
@clustervr.meta
@eslint
@eslint.meta
...

ビルド

npm run buildコマンドを実行することでsrcディレクトリのファイルがdistディレクトリにビルドされて出力されます。

 $ npm run build

> build
> rollup --config


src/items/gun/main.ts → dist/gun.js...
created dist/gun.js in 171ms

コマンドが完了するとdistディレクトリに成果物のJavaScriptファイルが作られます。

Scriptable Itemとの紐付け

生成されたJavaScriptファイルをScriptable Itemと紐づけます。
Scriptable Item componentのSource Code AssetAssets/Script/dist/gun.jsを設定することで、生成されたJavaScriptファイルをScriptable Itemと紐づけることができます。

Gun Object Scriptable Itemとの紐付け

ワールドアップロード

clusterにワールドをアップロードすることでスクリプトの動作が確認できます。
今回作ったスクリプトは「クリック長押しでチャージした分だけ弾を撃つシグナルを送る」ものでしたが、正常に機能していることが確認できます。

シグナルを受け取って弾丸を生成する方法はCreatorsGuideの記事が参考になります。
https://creator.cluster.mu/2020/06/11/howto_shootinggame/

おわりに

あっさりめの記事でしたが、今回はScriptableItemのコードを書く時にTypeScriptを活法する例を紹介してみました。
JavaScriptはエコシステムが非常に充実している言語で、今回紹介したTypeScriptやRollup以外にも様々なライブラリがあります。
(有名どころではコードフォーマッターのPrettier、LinterのESLint、テスティングフレームワークのJestなどなど)
それらを活用してScriptableItemの開発もどんどん加速させていきましょう👍

おまけ1 buildコマンドは何をしている?

導入手順で紹介したnpm run buildコマンドはpackage.jsonに書いたカスタムコマンドを実行するもので、中ではビルドツールのrollupを呼び出しています。

    "build": "rollup --config"

おまけ2 ファイルの変更を検知して自動でbuildする

rollupはwatch機能が実装されていて、ビルドの対象ファイル(今回の例ではsrcディレクトリ内のファイル)を更新した時に自動で再ビルドを動かせます。

"watch": "rollup --config --watch"

package.jsonにwatchコマンドを追記してnpm run watchを実行すると、ファイルを監視する状態になりsrc内のファイルを変更すると自動でdistディレクトリも更新されるようになるので、都度npm run buildを実行しなくてよくなります。

Discussion