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で検索してもらうなどで対応。

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

2. 出力形式

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

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

3. data

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

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

nametypeは文字列、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名ではなく基底数値

enumの例:

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

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

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

refout、または値型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できるか。
  • datacodeは配列か。
  • data.namecode.nameはそれぞれ一意か。

Symbolと型

  • すべてのPUSH operandがdataに存在するか。
  • すべてのEXTERN returnがdataに存在するか。
  • 戻り値格納先の型がSignatureの戻り値型と一致するか。
  • enum初期値を数値で記述したか。
  • 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との差が現れる場合があるためである。