エディタスクリプト
UdonSharpにはエディタスクリプトAPIが含まれており、これを使用することで、通常のC#版のMonoBehaviourを操作するのと同様に、UdonSharpBehaviourを操作するカスタムエディタやエディタスクリプトを作成できます。
ほとんどの場合、エディタスクリプトAPIを使用すれば、C#版のスクリプトで記述するのと全く同じ方法でC#コードを記述できますが、スクリプトを扱う際にはいくつか留意すべき点があります。
UdonSharpにおけるエディタスクリプトの基本は、UdonSharpが自身のビヘイビアスクリプトのC#版インスタンスを作成し、UdonBehaviour版スクリプトのフィールドを舞台裏でC#の proxy_ にコピーするという仕組みです。エディタスクリプトから操作を行う際、基本的にはこの proxy_ を介してやり取りすることになります。
Proxies
UdonSharpスクリプトは有効なC#コードであるため、通常のスタンドアロンコンポーネントと同様に、SerializedObject(Proxy)としてGameObjectに追加できます。ProxyはUnityの SerializedObject 型と似た仕組みです。SerializedObjectに適用した変更は元のオブジェクトに反映させる必要があり、同様に元のオブジェクトへの変更もSerializedObject上で更新する必要があります。
Proxyは、対応する UdonBehaviour と同じGameObject上に作成され、無効化された状態で配置されます。Unityがこれらのコンポーネントに対してイベントを実行しないよう、常に無効のままにしておく必要があります。ProxyはGameObjectインスペクター上で非表示に設定されているため、直接確認することはできませんが、実際には存在しています。また、Proxyにはシーンやビルドに保存されないようフラグが設定されているため、エディタ上でのみ機能し、パッケージサイズやダウンロードサイズの肥大化を気にする必要はありません。
Proxy上のメソッドを実行すると、UdonBehaviour でイベントを実行するのと同様に機能します。ここで注意すべき主な点は、C#において UdonSharpBehaviour は UdonBehaviour そのものではないという点です。これについては以下で詳しく説明します。
通常のC#やU#との動作の違い
UdonSharpBehaviourはUdonBehaviourではありません
UdonSharpのコードでは、内部的に同じオブジェクトとして表現されるため、UdonBehavioursをUdonSharpBehavioursとして扱うことが可能です。技術的には、UdonSharpBehavioursは実際にはUdonBehavioursを継承していないため、UdonSharpで作業する際は、UdonBehaviour の代わりとして UdonSharpBehaviour を変数型に使用するのが最善です。
このため、グラフから構築されたUdon behaviourを他人がプラグインできるようにする場合を除き、UdonBehaviour の代わりとして、常に UdonSharpBehaviour を変数型として使用することを推奨します。
プロキシ参照はUdonSharpBehaviour変数でのみ自動的に処理されます
プロキシが自動的に処理されるのは、UdonSharpBehaviourの変数型のみです。プロキシbehaviour内で別のプロキシbehaviourを参照する場合、その参照はプロキシシステムによって自動的にUdonBehaviourの参照へと変換されます。このため、他の UdonSharpBehaviours を参照する変数は、特定の UdonSharpBehaviour 型、または任意の UdonSharpBehaviour 型を格納できる場合は基底クラスである UdonSharpBehaviour 型として保持する必要があります。
UdonBehaviour 型の変数を使用したり、単純な Component 参照やその他曖昧な型を使用したりした場合、プロキシシステムは基盤となるUdonBehaviourへの参照を代入するだけとなります。これは、グラフアセットへの参照を許可したい場合(プロキシAPIの対象外であるため)には有効です。
心に留めておくべき重要な点として、UdonSharpBehaviour変数やそのサブクラスではない変数に直接プロキシbehaviourへの参照を格納すると、ビルド時にその参照はnullにクリアされます。例えば、Component の参照を持たせたい場合は、それがプロキシのUdonSharpBehaviourではなく、必ずUdonBehaviourを参照していることを確認してください。
プロキシは常に無効化されており、無効のままにしておく必要があります
プロキシは、Unityがゲームプレイ中にイベントを呼び出したり、同じロジックを二重に実行したりすることを防ぐために無効化されます。プロキシbehaviourを再度有効化してはいけません。プロキシbehaviourは無効化されているため、無効化されたbehaviourも取得するように指定せずに GetComponentInChildren のようなメソッドをプロキシbehaviourに対して実行した場合、プロキシは返されません。
UdonSharpBehaviourのカスタムインスペクターを作成する
UdonSharpBehaviour用のカスタムエディターを作成する際、ほとんどの要素は通常のカスタムインスペクターの作成と全く同じになるよう抽象化されています。Editor を継承したクラスを作成し、対象の UdonSharpBehaviour の型を指定して CustomEditor 属性を追加するだけです。
U# や C# スクリプト用のカスタムエディターを作成する際は、Unityのゲーム内で使用が許可されていない Editor ライブラリを使用しているため、エディターコードがゲームビルドに含まれないようにする必要があります(含まれているとワールドのビルドに失敗します)。
エディターコードをワールドから除外するには、以下の2つの推奨方法があります。
- インスペクターのスクリプトを Editor という名前のフォルダー内に配置する。
- プリプロセッサ定義
UNITY_EDITORを使用してコードを囲む。例:
#if UNITY_EDITOR
[CustomEditor(typeof(CustomInspectorBehaviour))]
public class CustomInspectorEditor : Editor
{
...
}
#endif
UnityEditor のようなエディター専用の名前空間を使用する場合も、同様に UNITY_EDITOR で囲む必要があります。
インスペクターを UdonSharpBehaviour スクリプトと同じファイル内に記述したい場合は、COMPILER_UDONSHARP プリプロセッサ定義を使用して、UdonSharp がエディター専用コードを解析しないようにする必要があります。上記の例でこれを使用すると、以下のようになります。
public class CustomInspectorBehaviour : UdonSharpBehaviour
{
...
}
#if !COMPILER_UDONSHARP && UNITY_EDITOR
[CustomEditor(typeof(CustomInspectorBehaviour))]
public class CustomInspectorEditor : Editor
{
...
}
#endif
COMPILER_UDONSHARP プリプロセッサ定義が true となるのは、UdonSharpBehaviour と同じスクリプト内に記述した場合のみです。UdonSharpBehaviour を含まず、UdonSharpProgramAsset に関連付けられていない外部スクリプトでは、COMPILER_UDONSHARP が true になることはありません。
UdonSharpBehaviours に対してフィールドを条件付きで削除または追加するために、COMPILER_UDONSHARP や UNITY_EDITOR を使用しないでください。予期しない動作を引き起こす原因となります。
カスタムインスペクターを作成する際は、常に OnInspectorGUI を以下で開始してください。
if (UdonSharpGUI.DrawDefaultUdonSharpBehaviourHeader(target)) return;
これにより、C# スクリプト用の「convert to behaviour」ボタン、同期設定、インタラクト設定、およびユーティリティを含む標準の UdonSharp ヘッダーの描画が処理されます。各セクションを個別に描画することも可能です。描画可能な内容については DrawDefaultUdonSharpBehaviourHeader() の実装を参照してください。
インスペクターの例
この例は UdonSharp に同梱されています。
using UnityEngine;
using VRC.SDK3.Components;
using VRC.SDKBase;
using VRC.Udon;
#if !COMPILER_UDONSHARP && UNITY_EDITOR // These using statements must be wrapped in this check to prevent issues on builds
using UnityEditor;
using UdonSharpEditor;
#endif
namespace UdonSharp.Examples.Inspectors
{
/// <summary>
/// Example behaviour that has a custom inspector
/// </summary>
public class CustomInspectorBehaviour : UdonSharpBehaviour
{
public string stringVal;
private void Update()
{
Debug.Log($"CustomInspectorBehaviour: {stringVal}");
}
}
// Editor scripts must be wrapped in a UNITY_EDITOR check to prevent issues while uploading worlds. The !COMPILER_UDONSHARP check prevents UdonSharp from throwing errors about unsupported code here.
#if !COMPILER_UDONSHARP && UNITY_EDITOR
[CustomEditor(typeof(CustomInspectorBehaviour))]
public class CustomInspectorEditor : Editor
{
public override void OnInspectorGUI()
{
// Draws the default convert to UdonBehaviour button, program asset field, sync settings, etc.
if (UdonSharpGUI.DrawDefaultUdonSharpBehaviourHeader(target)) return;
CustomInspectorBehaviour inspectorBehaviour = (CustomInspectorBehaviour)target;
EditorGUI.BeginChangeCheck();
// A simple string field modification with Undo handling
string newStrVal = EditorGUILayout.TextField("String Val", inspectorBehaviour.stringVal);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(inspectorBehaviour, "Modify string val");
inspectorBehaviour.stringVal = newStrVal;
}
}
}
#endif
}
Handles の使用
これはカスタムインスペクターのGUIを作成する場合と同様に動作し、ほとんどの処理は自動的に行われます。エディター上で OnSceneGUI イベントを使用するだけで、期待通りに動作するはずです。
Gizmos の使用
Gizmos を期待通りに動作させるには、少し特殊な処理が必要です。サンプルスクリプト の1つが Gizmos を使用しています。Gizmos を使用する場合、エディタを囲んだものと同じ #if !COMPILER_UDONSHARP && UNITY_EDITOR チェックで OnDrawGizmos イベント自体を囲む必要があります。また、OnDrawGizmos および OnDrawGizmosSelected イベントは、Behavior 自体に記述してください。
Gizmos は、UdonSharpProgramAssets を持つすべての UdonBehaviour にアタッチされる proxy_ behavior を使用して描画されます。プレイモード中でないときは Udon が UdonBehaviour を実行しないため、これらは Udon を通じて実行されません。Gizmos イベントは UdonSharp によって管理されないため、proxy_ behavior が最新であることを確認するために少し手順が必要です。これを行うには、以下の2つのメソッドのいずれかを呼び出してください。どちらも同じ処理を行います。UpdateProxy は、シリアライズされたオブジェクト用の Unity API を模倣するために作られたものです。
// Call this
UdonSharpEditorUtility.CopyUdonToProxy(this);
// Or this
this.UpdateProxy();
// Do not call both since you'd be doing redundant work
この例については、前述のリンク先にあるサンプルスクリプトで確認できます。
インスペクター以外のエディター用スクリプト
UdonSharpBehaviourを作成、削除、変更するエディター用スクリプトを作成する場合、プロキシの更新と変更の適用を自身で管理する必要があります。
UdonSharpBehaviourの追加
新しいUdonSharpBehaviourを追加するには、対象のGameObjectを取得し、AddComponentの代わりに AddUdonSharpComponent<T>() を呼び出すだけです。
GameObject targetGameObject = ... // Get some game object from somewhere here
MyComponentType newComponent = targetGameObject.AddUdonSharpComponent<MyComponentType>();
これで newComponent は、コンポーネント型 MyComponentType の有効な UdonSharpBehaviour プロキシとなります。エディター用スクリプトから、他のC#コンポーネントと同様に操作できます。コンポーネントの作成を取り消し可能(undoable)にしたい場合は、代わりに以下を使用してください。
GameObject targetGameObject = ... // Get some game object from somewhere here
MyComponentType newComponent = UdonSharpUndo.AddComponent<MyComponentType>(targetGameObject);
既存のUdonSharpBehaviourの取得
UdonSharpには、GameObjectの拡張メソッドとして定義された GetComponent(s) に相当する機能があります。エディター用スクリプトで作業する場合、GetComponent<T>() を呼び出す代わりに、その等価メソッドである GetUdonSharpComponent<T>() を呼び出す必要があります。
GameObjectの子要素から、指定した型 MyComponentType を持つすべての UdonSharpBehaviour を取得するには、以下のようにします。
GameObject sourceGameObject = ... // Get some game object from somewhere here
MyComponentType[] myComponents = sourceGameObject.GetUdonSharpComponentsInChildren<MyComponentType>();
UdonSharpBehaviourの操作と変更
UdonSharpBehaviour を取得したら、プロキシに対する変更が確実にUdonへ反映されるように処理する必要があります。
Udonがビヘイビアに対して何らかの変更を行っている場合、GetUdonSharpComponent(s) を使用していれば、それらが自動的にビヘイビアを更新するため、自動的に反映されます。ビヘイビアへの参照を保持している場合は、自身で更新する必要があります。これを行うには、そのビヘイビアに対して UpdateProxy() を呼び出します。
ビヘイビアを変更したら、プロキシビヘイビアの変更をUdonに適用しなければなりません。これを行うには、そのビヘイビアに対して ApplyProxyModifications() を呼び出します。
これは、Unityの SerializedObject を扱うのと同様の考え方です。
MyComponentType myComponent = ...
// We only need to update the proxy if we storing some persistent reference to it
myComponent.UpdateProxy();
// Add 5 to a `float` on our behaviour
myComponent.myFloatField += 5f;
// Apply the changes to myComponent to the Udon copy of it
myComponent.ApplyProxyModifications();
UdonSharpBehaviourの破棄
UdonSharpBehaviourを破棄し、その基盤となるUdonBehaviourを削除するには、UdonSharpEditorUtility.DestroyImmediate() メソッドを使用してください。
最終更新: