🛠️

Unity上でClusterScriptを動かせるようにしたよ!という話。

2023/12/09に公開

初めまして!!かおもと申します!!!!!!

0. CSEmulatorの宣伝

Unity上でClusterScriptを動かせるようにしました!!
アップロードせずに(ある程度)確認できるのでタイパよいです!!!
もちろん無料!!!!
https://vkao.booth.pm/items/5111235

現在はv2.10となり、だいぶ安定してきたのではないかと思っています。
これもすべて皆様のバグ報告のおかげです!!!本当にありがとうございます…!!!!!!!

本文は以上です!!!!

以下はおまけです。

1. 関数の細かい仕様とか、関数へのお気持ちとかをゆるく書いてく

去年なんで宣伝してなかったんだろうと思ったら、v1.00が23年5月でした。

せっかくの記事ですし、ClusterScriptの細かい仕様(挙動)についてはギリ需要がありそうなので、書いていこうと思います。
例えば「最大で10回/秒まで他のハンドルに対して操作することができます。 瞬間的にこの制限を超えることはできますが、平均回数はこの制限を下回るようにしてください」っていう記述の 「 瞬間的にこの制限を超えることはできます」って具体的にどういうことなの、という内容です。
(各種挙動は主に2023.10.25時点でのものです)

ただそれだけだとあまり書くことがなさそうなので、関数に対するお気持ち?とかも交えて緩い感じで書いていきます。

1.1 以下の内容はすべて予想です

筆者は中の人ではないし、逆コンパイルもしていないので、あくまでも調査した結果から予想した仕様です。

2. $(ClusterScript)編

2.1 [仕様]onXxxx()系

onGrab、onRideは共存可。onInteract、onUseは共存不可。

詳細

共存可は「On Grab Item Trigger」コンポーネントが追加されていても、onGrabに登録した関数も呼び出されるという意味です。
共存不可は「On Collide Item Trigger」コンポーネントが追加されていると、onInteractに登録した関数が呼び出されないという意味です。

2.2 [仕様+お気持ち]useGravity

「物理挙動をするMovableItem」はRigidbodyのisKinematicの話。

詳細

カッコつきでいいので(RigidbodyのIsKinematicにチェックがない)とまで書いて欲しいなぁ。

2.3 [お気持ち]addForce()系

物理挙動しんどい!!!いまだにcluster上での物理挙動と一致できてない!!!!

詳細

onPhysicsUpdateなくなったら、OnFixedUpdateの処理に予約登録するような実装になるのかな?

2.4 [注意点]createItem()

CSEmulatorではプロジェクト内のPrefabからcreateItemします。生成回数の制限についてはこちら

詳細

BOOTHのページにも書いてありますが、Prefabに「CS Emulator Prefab Item」コンポーネントをつけて、ItemTemplateIdを指定してください。
そうすればcreateItemできます。

2.5 [仕様]頻度の制限、ClusterScriptError (rateLimitExceeded)

同フレームでは連続5回まで。0.09秒経過で1回分回復。

詳細

この制限があるものは、おそらく全部同じ条件です。
多少余裕があって、きっちり「最大で10回/秒」ではないようです。具体的にはギリギリ11回いける。

CSEmulatorをインポートしてもらえばソースを見ることができますが、頑張って調査したので転記します。
処理を1回行うごとにTryChage()、onUpdateなどで経過した秒数分Dischage()させるという使い方です。

ソース
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Assets.KaomoLab.CSEmulator
{
    /// <summary>
    /// 瞬間的な流入回数を許容する流入量制限機能
    /// </summary>
    public class BurstableThrottle
    {
        public int burstLimit { get; private set; }

        readonly double charge;
        readonly double[] charges;
        int head = 0; //この位置に入れる
        int tail = 0; //この位置から出す

        /// <summary>
        /// 瞬間的な流入回数を許容する流入量制限機能
        /// </summary>
        /// <param name="charge">1回のCharge量</param>
        /// <param name="burstLimit">瞬間的にChargeできる回数</param>
        public BurstableThrottle(
            double charge,
            int burstLimit
        )
        {
            this.charge = charge;
            this.burstLimit = burstLimit;
            charges = new double[burstLimit].Select(n => 0d).ToArray();
        }
        /// <summary>
        /// 流入させてみる。
        /// </summary>
        /// <returns>流入に失敗した場合はfalse。成功した場合はtrue。</returns>
        public bool TryCharge()
        {
            if (charges[head] > 0) return false;
            charges[head] = charge;
            head++;
            if (head == burstLimit) head = 0;
            return true;
        }
        /// <summary>
        /// 放出させる。
        /// </summary>
        /// <param name="amount">放出量</param>
        public void Discharge(double amount)
        {
            while (charges[tail] > 0)
            {
                charges[tail] -= amount;
                if (charges[tail] > 0)
                    break;

                amount += -charges[tail];
                charges[tail] = 0;
                tail++;
                if (tail == burstLimit) tail = 0;
            }
        }
    }
}

2.6 [仕様]destroy()

ワークラワールドでは、クラフトモードで配置したアイテムも動的扱い。

詳細

createItem()や「Create Item Gimmick」で生成したアイテムは動的扱い。
CCKワールドでは、Unity上で配置したアイテムは静的扱い。

2.7 [仕様+お気持ち]getOverlaps()

UnityのOnTrigger系で実装している模様。

詳細

どうやらPhysics.Overlap系の機能ではない模様。
MeshColliderなShapeに対応する必要があったから?
OnTrigger系での実装なので「アイテムの生成時点ですでに重なっていたり、重なっている状態で有効化された場合など、空間的には重なっている場合にもこの関数の返り値に含まれない場合があります」という条件も追加になった?

CSEmulatorではかなり無理やり実装しているので、clusterではどう実装してるのか興味があります!!

2.8 [仕様]onCollide()

「アイテムは物理挙動をする必要があります」なのでRigidbodyが必要。

詳細

UnityのOnCollide系の機能なので、Unity公式が参考になります。

2.9 [仕様]raycast()系

Is TriggerなColliderには反応しない。

詳細

Physics.Raycast()なので、トリガーにも反応できる仕様ですが、あえてしていない模様。

2.10 [お気持ち]sendSignalCompat

CCKのロジックをC#で組み立ててSignalを送っているぞ!!!

詳細

clusterなら何かこうもっと簡単な方法で送ってるんだろうなぁいいなぁというお気持ち。

2.11 [お気持ち]setStateCompat

double値を送れるのに、CCKのロジック系ではdoubleが対応していなくてつらい。

詳細

sendSignalCompatみたいにロジックをC#で組み立てて送ろうとしてたけども、CCK(CreatorKit.Operation.ConstantValue)がdoubleに対応してなくて詰み。
この様子を見てると、CCKのLogicとかには今後あまり手が加わらないのかな?

2.12 [面白い]$.log($)

「ClusterVR.Presentation.Item.Internal.JsEngine.ApiInterfaceV2」と出るぞ!!!

詳細
  • ククク…ToString()を書き忘れておられるな…堪能させていただきます!!!
  • ベータじゃないワールドでもでるぞ!通常ワールドでエラーを出すため?
  • Presentation.ItemとCreatorKit.Itemがあるのも興味深い。なんならUseCase.Itemもある。
  • JsEngineがInternalなのも興味深い。Externalはあるのかな?
  • ClusterScriptの本名がApiInterfaceV2というのも興味深い。
  • ApiAudioを思い出したけど、ApiAudioにだけApiとついてるのも興味深い。
  • CCKではinterface実装系はImplements以下だけど、そうではないのも興味深い。
  • ソースコードがとても見たい。

3. Vector2、Vector3、Quaternion編

3.1 [仕様]equals()

「ほとんど等しいとき」とは、差が「0.00001」未満のとき。

詳細

==で比較しているみたいで、Unity公式に説明があります。

3.2 [仕様]multiply()

Quaternionのみ回転になっている。

詳細

*オペレーターで計算している模様(Unity公式)。
Vector3とVector2は単純に各項の掛け算(Unity公式)。

4. HumanoidAnimation編

4.1 [お気持ち]getSample()

そういう機能がUnityに用意されているのかと思ってました。

詳細

公式の実装を見て震えました。

5. ItemHandle編

5.1 [仕様] AddImpulsiveXxxx()系

回数の制限についてはこちら

5.2 [仕様+お気持ち]send()

ItemHandleをsendできるのがヤバすぎる。回数の制限についてはこちら

詳細

距離制限の確認のために、ItemHandleの実行元である$についての情報を持つ必要があります。
なので、sendすると$から別の$に渡されると、そのタイミングでItemHandleの実行元の$を書き換える必要がありました。
これに気づけずに盛大にバグらせました。

書き換え処理自体も再帰的に行っていて、だいぶ面倒なことになってます。
これ、clusterではどう実装しているのかとても気になります!!

6. Muscles編

6.1 [お気持ち]各プロパティ

多すぎるし、undefinedを受け付けられるようにするのが大変。

詳細

一部分だけ動かせるようにするという挙動のために、undefinedを受け付ける仕様のようです。
UnityではMusclesは単純な配列なので、一から手作りで大変でしたね…。

7. PlayerHandle編

7.1 [仕様]各関数の実行回数制限

こちら

7.2 [仕様]ダッシュと歩きの速度

ダッシュは1.8倍、歩きは0.5倍の速度。

7.3 [お気持ち]setGravity()

9.81がCCK内にそのまま書かれてて、外から変更できません!!

詳細

仕方ないのでMoveをもう1回呼ぶことで、差の重力を適用しています。
しかし、Moveは複数回呼ぶなと公式に書いてあるので不安な日々を送っています。

7.4 [宣伝]setHumanoidPose()

CCKプレビューではPlayerは矢印ですが、CSEmulatorを入れると人型アバターが出ます!!!

詳細

なので、ある程度のポーズの確認ができるはずです!!!
三人称視点はまだ実装していないので、シーンビューで確認してください。

8. $.state(StateProxy)編

8.1 [仕様]値を受け取る(get)ときに起きていること

完全にディープコピーされたものが出てくる。

詳細

公式サンプルの再代入しましょうという記述はそういう意味かと思われます。
なおCSEmulatorのコピーは不完全です…治します…!!!!

9. SubNode編

9.1 [仕様]setPosition()、setRotation()

連打や長距離だと即時。単発だと0.2secかけて動く。

詳細

完全に理解したイベントだったかで「よしなに動きます」と聞いた記憶があります。
かなり「よしな」になるように制御が入っているっぽい。
調べた結果、おおよそ以下のようなルールの模様。

  • setPositionした次のフレームでsetPositionしたら、その値になる。
  • setPositionの場合は10m以上、setRotationの場合は45degree以上離れていたら、その値になる。
  • 上記以外の場合は0.2secかけて、その値になろうとする。

この1つ目のルールがポイントで、これがないとsetPositionを連打してもぴったり動かず、0.2secのラグでついてくる感じの動きになります。
ソースコードはカオスなので貼りません。

10. ベータ2楽しみですね

今日は12/5なのですが、記事公開頃にはベータ2来てたりするのかな?
→来ました!!!随時対応していきます!!!!!!!!

よきClusterScriptライフを!!!!!!!!!!!!

Discussion