はじめに
UdonがVRChatに現れてから早いもので1年が経過しました。
近頃はUdonを書く人も大分増えて、日本界隈でも市民権を得てきているなぁ、と思う今日この頃です。
本格的にUdonを触るようになって、エディタ拡張で自動化したいシチュエーションが増えてきたのですが、VRCTriggerと違ってUdonBehaviourはserializedObjectからUdonの変数に触る事は出来ないようです。
そんなわけでエディタ拡張からUdonの変数を設定する方法を調べてみました。
Udonの構造
UdonはUdonAssemblyProgramAssetを継承したクラス(Udonコンパイラ)によってコンパイルされます。
その際、シリアライズされた変数は型と変数名を合わせたSymbolという形で保存されます。
このSymbolに値は含まれておらず、値はPublicVariablesという、変数名と紐づけされた形でUdonBehaviourに保存されます。
Symbolの取得
SymbolはUdonコンパイラによって生成されるSerializedUdonProgramAssetが持っています。UdonBehaviourからは下記の経路でSymbolにアクセスできます。
using VRC.Udon;
using VRC.Udon.Common.Interfaces;
public class SymbolTable
{
public UdonBehaviour udonBehaviour;
public IUdonSymbolTable GetSymbolTable()
{
IUdonSymbolTable symbolTable = udonBehaviour.programSource.SerializedProgramAsset.RetrieveProgram().SymbolTable;
return symbolTable;
}
}
SymbolTableから下記関数を使用してSymbolの情報を取得できます。
-
ImmutableArray<string> GetExportedSymbols()
- 変数名の配列を返す。
-
bool HasExportedSymbole(string publicVariableSymbol)
- 指定した変数名がテーブルに含まれるならTrueを返す。
-
Type GetSymbolType(string exportedSymbol)
- 変数名からSymbolの型を返す。
PublicVariablesの編集
PublicVariablesはUdonBehaviourのメンバ変数から取得できます。
IUdonVariableTable publicVariables = udonBehaviour.publicVariables;
PublicVariablesが持つ下記関数から変数の値にアクセスできます。
-
bool TryGetVariableValue(string exportedSymbol, out object variableValue)
- 変数名からSymbolの値をobject型で取得する。失敗したらFalseを返す。
-
bool TrySetVariableValue(string exportedSymbol, object variableValue)
- 変数名からSymbolの値をSetする。失敗したらFalseを返す。
サンプルスクリプト
Scene内の全てのAudioSourceを取得してUdonBehaviourに設定するエディタ拡張です。
using UnityEngine;
using UnityEditor;
using VRC.Udon;
using VRC.Udon.Common.Interfaces;
public class UdonAudioSetup : MonoBehaviour
{
public UdonBehaviour udonBehaviour;
public string symbolName;
}
[CustomEditor(typeof(UdonAudioSetup))]
public class UdonAudioSetupEditor : Editor
{
UdonAudioSetup udonAudioSetup;
private void OnEnable()
{
udonAudioSetup = (UdonAudioSetup)target;
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
EditorGUILayout.Space();
EditorGUILayout.Space();
// Symbolを取得出来なければボタンを無効化
using (new EditorGUI.DisabledScope(!IsSetupReady(udonAudioSetup)))
{
if (GUILayout.Button("Setup"))
{
SetupUdonBehaviour(udonAudioSetup.udonBehaviour, udonAudioSetup.symbolName);
}
}
}
private void SetupUdonBehaviour(UdonBehaviour udonBehaviour, string exportedSymbol)
{
// PublicVariables取得
IUdonVariableTable publicVariables = udonBehaviour.publicVariables;
var audioSources = GameObject.FindObjectsOfType<AudioSource>();
// Undo用意
Undo.RecordObject(udonBehaviour, "Modify Public Variable");
// 値をPublicVariablesに設定
if (!publicVariables.TrySetVariableValue(exportedSymbol, audioSources))
{
Debug.Log("Error! Failed Setting Public Variables.");
return;
}
Debug.Log("Setup Completed.");
}
private bool IsSetupReady(UdonAudioSetup udonAudioSetup)
{
// Symbolテーブル取得
IUdonSymbolTable symbolTable = udonAudioSetup.udonBehaviour?.programSource?.SerializedProgramAsset?.RetrieveProgram()?.SymbolTable;
if (symbolTable == null)
return false;
// Symbolが存在しなければFalse
if (!symbolTable.HasExportedSymbol(udonAudioSetup.symbolName))
return false;
// Symbolの型がAudioSource[]でなければFalse
if (symbolTable.GetSymbolType(udonAudioSetup.symbolName) != typeof(AudioSource[]))
return false;
return true;
}
}
使用バージョン
- VRCSDK3-WORLD-2021.03.22.18.27_Public
- UdonSharp_v0.19.8