Skip to content

AI向け ShirasUdonAssembly JSON v1 作成ガイド ​

この文書は、生成AIがShirasUdonのテスト用Assembly JSONを安全に作成するための 実践ガイドである。厳密な定義は ShirasUdonAssembly JSON v1仕様書を正とする。

1. AIへ渡す資料 ​

最低限、AIへ次の2ファイルを渡す。

  1. このガイド
  2. ShirasUdonAssembly JSON v1正式仕様書

既存のサンプルコードはサンプルコードページから選び、必要に応じてAIへ渡す。

使用可能なExtern Signatureは、cannorin様提供のUdon Extern Search 2で検索する。

Udon Extern Search 2を提供されているcannorin様に感謝します。

AIはExtern Signatureを推測してはならない。必ず一覧に完全一致する文字列を使用する。

2. 出力形式 ​

出力はUTF-8の厳密なJSONとする。コメント構文、末尾カンマ、NaN、Infinityは使用しない。

json
{
  "version": 1,
  "comment": "サンプルの説明",
  "data": [],
  "code": []
}
プロパティ必須内容
version○常に1
comment×サンプル選択UIに表示する説明
data○論理Heap Symbolの宣言と初期値
code○exportまたは内部ラベルとなるコードブロック

3. data ​

すべての値は、先に一意なHeap Symbolとして宣言する。

json
{
  "name": "__value",
  "type": "SystemInt32",
  "value": 10
}

nameとtypeは文字列、valueは必須である。同じnameを重複させない。

代表的な型名:

text
SystemBoolean
SystemInt32
SystemSingle
SystemDouble
SystemString
UnityEngineVector3
UnityEngineGameObject
VRCSDKBaseVRCPlayerApi

型名もExtern一覧のType_...またはVariable_...に対応するUdon型名を使う。

JSON値 ​

text
Boolean型       -> true / false
数値型          -> JSON Number
String型        -> JSON String
参照型の初期値  -> null
値型の箱        -> null(default(T)としてロードされる)
enum            -> enum名ではなく基底数値
SystemType      -> 対象型のUdon型名を表すJSON String

enumの例:

json
{
  "name": "__ease",
  "type": "VRCSDK3ComponentsVRCTweenEase",
  "value": 1
}

"Linear"のような名前は使用しない。数値とenumの対応はSDKの定義を確認する。

typeof(T)の例:

json
{
  "name": "__int_type",
  "type": "SystemType",
  "value": "SystemInt32"
}

valueには"System.Int32"ではなく、Externシグネチャと同じUdon型名を書く。 APIが返すSystemTypeの格納先にはvalue: nullを使用する。

4. codeとexport ​

json
{
  "name": "AddSpeed",
  "export": true,
  "instructions": []
}
  • export: trueのブロックはUIから実行できるエントリーポイントになる。
  • export: falseのブロックはJUMP先などの内部ラベルになる。
  • コードブロック名を重複させない。
  • _startは特別に必須ではない。目的に合うexport名を使ってよい。
  • 命令列の末尾へ到達すると正常終了する。

VMはRun()ごとにStackと命令位置を初期化するが、論理Heapは維持する。Heapを初期値へ 戻すのはLoad()時である。同じexportを再実行すると前回の結果から続行する。

5. Stackの基本 ​

Stackへ積むのは値ではなくHeap Symbol名である。

json
{
  "op": "PUSH",
  "operand": "__value"
}

AIは各コードブロックについて、命令ごとのStack深度を追跡する。

text
PUSH                    +1
POP                     -1
COPY                    -2
EXTERN                   -argumentCount
戻り値ありEXTERN         さらに+1
JUMP_IF_FALSE           -1
JUMP / JUMP_INDIRECT     仕様書に従う

Stack深度は途中で負になってはならず、通常はコードブロック終了時に0にする。

6. COPY ​

COPYは上から2つのSymbolを取り出し、1つ目から2つ目へコピーする。

json
{
  "op": "PUSH",
  "operand": "__source"
},
{
  "op": "PUSH",
  "operand": "__destination"
},
{
  "op": "COPY"
}
text
__destination = __source

enumと数値型の間のCOPYでは、VMが必要なenum変換を行う。

7. EXTERN ​

json
{
  "op": "EXTERN",
  "signature": "SystemInt32.__op_Addition__SystemInt32_SystemInt32__SystemInt32",
  "argumentCount": 2,
  "return": "__result"
}

引数 ​

引数はC#の呼び出し順にPUSHする。インスタンスメソッドではreceiverが第0引数になる。

csharp
player.SetWalkSpeed(speed);
json
{ "op": "PUSH", "operand": "__player" },
{ "op": "PUSH", "operand": "__speed" },
{
  "op": "EXTERN",
  "signature": "VRCSDKBaseVRCPlayerApi.__SetWalkSpeed__SystemSingle__SystemVoid",
  "argumentCount": 2
}

argumentCountにはreceiverを含むが、戻り値格納先は含めない。

戻り値 ​

  • 戻り値あり:returnへ既存のHeap Symbol名を必ず指定する。
  • 戻り値なし:returnを指定してはならない。
  • 戻り値ありEXTERNは、実行後にreturnのSymbolをStackへ積む。
  • 戻り値を後続命令で使わない場合、直後にPOPする。
json
{
  "op": "EXTERN",
  "signature": "VRCSDKBaseNetworking.__get_LocalPlayer__VRCSDKBaseVRCPlayerApi",
  "argumentCount": 0,
  "return": "__player"
},
{
  "op": "POP"
}

通常、末尾が__SystemVoidなら戻り値なしである。ただし、Udonの一部の プロパティsetterは__SystemVoidを省略する。.__set_を含むSignatureも必ず戻り値なしと して扱う。

text
UnityEngineVector3.__set_x__SystemSingle

このSignatureにreturnを付けてはならない。

refArguments ​

ref、out、または値型receiverの変更をHeapへ書き戻す引数Indexを指定する。 Indexは0始まりで、argumentCount未満でなければならない。省略時は空配列である。

json
{
  "op": "EXTERN",
  "signature": "UnityEngineVector3.__set_x__SystemSingle",
  "argumentCount": 2,
  "refArguments": [0]
}

Vector3などの値型receiverを変更するsetterでは、通常receiverの0を指定する。 参照型オブジェクトのプロパティsetterには通常不要である。

8. 分岐 ​

無条件ジャンプ:

json
{
  "op": "JUMP",
  "operand": "_loop"
}

条件付きジャンプ:

json
{ "op": "PUSH", "operand": "__condition" },
{
  "op": "JUMP_IF_FALSE",
  "operand": "_end"
}

JUMP_IF_FALSEはBoolean Symbolを1つ消費し、falseなら指定ブロックへ移動する。 ジャンプ先はcodeに存在する一意なブロック名でなければならない。

9. 最小例 ​

次の例はRunを押すたびに整数を1増やしてログへ出す。

json
{
  "version": 1,
  "comment": "整数を1増やしてログへ出力します。",
  "data": [
    { "name": "__value", "type": "SystemInt32", "value": 0 },
    { "name": "__one", "type": "SystemInt32", "value": 1 },
    { "name": "__next", "type": "SystemInt32", "value": 0 }
  ],
  "code": [
    {
      "name": "Run",
      "export": true,
      "instructions": [
        { "op": "PUSH", "operand": "__value" },
        { "op": "PUSH", "operand": "__one" },
        {
          "op": "EXTERN",
          "signature": "SystemInt32.__op_Addition__SystemInt32_SystemInt32__SystemInt32",
          "argumentCount": 2,
          "return": "__next"
        },
        { "op": "POP" },
        { "op": "PUSH", "operand": "__next" },
        { "op": "PUSH", "operand": "__value" },
        { "op": "COPY" },
        { "op": "PUSH", "operand": "__value" },
        {
          "op": "EXTERN",
          "signature": "UnityEngineDebug.__Log__SystemObject__SystemVoid",
          "argumentCount": 1
        }
      ]
    }
  ]
}

10. 生成手順 ​

AIは次の順序で作業する。

  1. ユーザーの目的をexport単位へ分割する。
  2. 必要なAPIをExtern一覧から検索する。
  3. 各Signatureのreceiver、引数、戻り値、ref/outを確定する。
  4. 必要な定数、一時値、戻り値格納先をすべてdataへ宣言する。
  5. C#相当の処理をPUSHとEXTERNへ展開する。
  6. 戻り値を使わない箇所へPOPを入れる。
  7. 値型receiverの変更にrefArgumentsを入れる。
  8. 全命令のStack深度を計算する。
  9. 全Symbol、ラベル、Signatureを照合する。
  10. JSON parserで構文検証する。

11. 検証チェックリスト ​

JSON ​

  • versionは整数の1か。
  • JSONとしてparseできるか。
  • dataとcodeは配列か。
  • data.nameとcode.nameはそれぞれ一意か。

Symbolと型 ​

  • すべてのPUSH operandがdataに存在するか。
  • すべてのEXTERN returnがdataに存在するか。
  • 戻り値格納先の型がSignatureの戻り値型と一致するか。
  • enum初期値を数値で記述したか。
  • typeof(T)をSystemTypeと対象型のUdon型名で記述したか。
  • API戻り値用の参照型・値型Symbolをnullで初期化したか。

EXTERN ​

  • Signatureが一覧に完全一致するか。
  • argumentCountが実際にPUSHした引数数と一致するか。
  • インスタンスメソッドのreceiverを数えたか。
  • 戻り値ありEXTERNにreturnがあるか。
  • Voidおよびsetterにreturnを付けていないか。
  • 不要な戻り値をPOPしたか。
  • ref/outおよび値型receiverへ正しいrefArgumentsを指定したか。

制御フロー ​

  • Stack深度が途中で負にならないか。
  • 各終了経路のStackが原則0か。
  • すべてのジャンプ先が存在するか。
  • 無限ループになっていないか。
  • 再実行時にHeapが維持されても意図どおりか。
  • 加算系テストには必要に応じてReset exportがあるか。

12. よくある失敗 ​

text
SignatureをC#名から推測する
return Symbolをdataへ宣言し忘れる
戻り値をPOPせずStackへ残す
Void EXTERNへreturnを書く
setterを戻り値ありと誤判定する
instance receiverをargumentCountへ含めない
COPYのsourceとdestinationを逆にする
Vector3 setterへrefArguments:[0]を付けない
enumを文字列名で初期化する
Runのたびにdataが初期化されると仮定する

UdonはException発生後、そのExecutorを停止する。無効な型、null引数、誤ったSignature、 誤ったpush数を「実行して確認する」前提にせず、生成時に検証する。

13. AIへ渡すプロンプト雛形 ​

text
ShirasUdonAssembly JSON v1のテストサンプルを作成してください。

目的:
<実現したい処理>

制約:
- 添付したAI向け作成ガイドと正式仕様書に従う
- Extern SignatureはUdon Extern Search 2で確認できる文字列だけを使う
- Signatureを推測しない
- versionは1
- 必要な値と一時値をすべてdataへ宣言する
- enum初期値は数値で書く
- argumentCountにはinstance receiverを含め、return slotは含めない
- 戻り値ありEXTERNにはreturnを指定する
- 戻り値を使用しない場合は直後にPOPする
- Voidおよび.__set_ Signatureにはreturnを書かない
- ref/outと値型receiverの変更にはrefArgumentsを指定する
- 全コードブロックのStack深度を検証する
- 再実行時にHeapが維持されることを考慮する
- 説明用commentを付ける
- 最終回答はMarkdown fenceを付けず、JSONだけを出力する

出力前に内部で次を検証してください:
- JSON構文
- Symbol参照
- コードブロック名とジャンプ先
- Extern Signatureの存在
- argumentCount
- returnの有無
- refArguments
- 各終了経路のStack収支

14. 実行確認 ​

  1. DemoのInputFieldへJSONを貼る。
  2. Loadを押す。
  3. export数と名前を確認する。
  4. 各ボタンを1回実行する。
  5. ConsoleとVRCクライアントログを確認する。
  6. 状態保持を確認する場合は再実行する。
  7. 初期値を再確認する場合は、もう一度Loadしてから実行する。

Editor上の成功だけで完了とせず、可能ならVRCクライアントでも確認する。Udonのbox化、 シリアライズ、ClientSimとの差が現れる場合があるためである。