summaryrefslogtreecommitdiff
path: root/VRCSDK3Worlds/Assets/Udon/Editor/UdonBehaviourEditor.cs
diff options
context:
space:
mode:
Diffstat (limited to 'VRCSDK3Worlds/Assets/Udon/Editor/UdonBehaviourEditor.cs')
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Editor/UdonBehaviourEditor.cs239
1 files changed, 239 insertions, 0 deletions
diff --git a/VRCSDK3Worlds/Assets/Udon/Editor/UdonBehaviourEditor.cs b/VRCSDK3Worlds/Assets/Udon/Editor/UdonBehaviourEditor.cs
new file mode 100644
index 00000000..edb26138
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Editor/UdonBehaviourEditor.cs
@@ -0,0 +1,239 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using UnityEditor;
+using UnityEditor.SceneManagement;
+using UnityEngine;
+using UnityEngine.SceneManagement;
+using VRC.Udon.Editor.ProgramSources.Attributes;
+
+namespace VRC.Udon.Editor
+{
+ [CustomEditor(typeof(UdonBehaviour))]
+ public class UdonBehaviourEditor : UnityEditor.Editor
+ {
+ private const string VRC_UDON_NEW_PROGRAM_TYPE_PREF_KEY = "VRC.Udon.NewProgramType";
+
+ private SerializedProperty _programSourceProperty;
+ private SerializedProperty _serializedProgramAssetProperty;
+ private int _newProgramType = 1;
+
+ private void OnEnable()
+ {
+ _programSourceProperty = serializedObject.FindProperty("programSource");
+ _serializedProgramAssetProperty = serializedObject.FindProperty("serializedProgramAsset");
+ _newProgramType = EditorPrefs.GetInt(VRC_UDON_NEW_PROGRAM_TYPE_PREF_KEY, 1);
+
+ UdonEditorManager.Instance.WantRepaint += Repaint;
+ }
+
+ private void OnDisable()
+ {
+ UdonEditorManager.Instance.WantRepaint -= Repaint;
+ }
+
+ public override void OnInspectorGUI()
+ {
+ UdonBehaviour udonTarget = (UdonBehaviour)target;
+
+ using (new EditorGUI.DisabledScope(Application.isPlaying))
+ {
+ bool dirty = false;
+
+ EditorGUILayout.BeginVertical(new GUIStyle(EditorStyles.helpBox));
+ {
+ // We skip the first option, Unknown, as it's reserved for older scenes.
+ VRC.SDKBase.Networking.SyncType method = (VRC.SDKBase.Networking.SyncType)(1 + EditorGUILayout.Popup("Synchronization", (int)udonTarget.SyncMethod - 1, Enum.GetNames(typeof(VRC.SDKBase.Networking.SyncType)).Skip(1).ToArray()));
+
+ if (method != udonTarget.SyncMethod)
+ {
+ udonTarget.SyncMethod = method;
+ dirty = true;
+ }
+
+ switch (method)
+ {
+ case VRC.SDKBase.Networking.SyncType.None:
+ EditorGUILayout.LabelField("Replication will be disabled.", EditorStyles.wordWrappedLabel);
+ break;
+ case VRC.SDKBase.Networking.SyncType.Continuous:
+ EditorGUILayout.LabelField("Continuous replication is intended for frequently-updated variables of small size, and will be tweened. Ideal for physics objects and objects that must be in sync with players.", EditorStyles.wordWrappedLabel);
+ break;
+ case VRC.SDKBase.Networking.SyncType.Manual:
+ EditorGUILayout.LabelField("Manual replication is intended for infrequently-updated variables of small or large size, and will not be tweened. Ideal for infrequently modified abstract data.", EditorStyles.wordWrappedLabel);
+ break;
+ default:
+ EditorGUILayout.LabelField("What have you done?!", EditorStyles.wordWrappedLabel);
+ break;
+ }
+ }
+ EditorGUILayout.EndVertical();
+
+ EditorGUILayout.Space();
+
+ EditorGUILayout.LabelField("Udon");
+
+ EditorGUILayout.BeginHorizontal();
+ EditorGUI.BeginChangeCheck();
+ _programSourceProperty.objectReferenceValue = EditorGUILayout.ObjectField(
+ "Program Source",
+ _programSourceProperty.objectReferenceValue,
+ typeof(AbstractUdonProgramSource),
+ false
+ );
+
+ if (EditorGUI.EndChangeCheck())
+ {
+ if (_programSourceProperty.objectReferenceValue == null)
+ {
+ _serializedProgramAssetProperty.objectReferenceValue = null;
+ }
+
+ dirty = true;
+ serializedObject.ApplyModifiedProperties();
+ }
+
+ if (_programSourceProperty.objectReferenceValue == null)
+ {
+ List<(string displayName, Type newProgramType)> programSourceTypesForNewMenu = GetProgramSourceTypesForNewMenu();
+ if (GUILayout.Button("New Program"))
+ {
+ (string displayName, Type newProgramType) = programSourceTypesForNewMenu.ElementAt(_newProgramType);
+
+ string udonBehaviourName = udonTarget.name;
+ Scene scene = udonTarget.gameObject.scene;
+ if (string.IsNullOrEmpty(scene.path))
+ {
+ Debug.LogError("You need to save the scene before you can create new Udon program assets!");
+ }
+ else
+ {
+ AbstractUdonProgramSource newProgramSource = CreateUdonProgramSourceAsset(newProgramType, displayName, scene, udonBehaviourName);
+ _programSourceProperty.objectReferenceValue = newProgramSource;
+ _serializedProgramAssetProperty.objectReferenceValue = newProgramSource.SerializedProgramAsset;
+ serializedObject.ApplyModifiedProperties();
+ }
+ }
+
+ EditorGUILayout.EndHorizontal();
+ EditorGUILayout.BeginHorizontal();
+ GUILayout.FlexibleSpace();
+
+ EditorGUI.BeginChangeCheck();
+ _newProgramType = EditorGUILayout.Popup(
+ "",
+ Mathf.Clamp(_newProgramType, 0, programSourceTypesForNewMenu.Count),
+ programSourceTypesForNewMenu.Select(t => t.displayName).ToArray(),
+ GUILayout.ExpandWidth(false)
+ );
+
+ if (EditorGUI.EndChangeCheck())
+ {
+ EditorPrefs.SetInt(VRC_UDON_NEW_PROGRAM_TYPE_PREF_KEY, _newProgramType);
+ }
+ }
+ else
+ {
+ EditorGUILayout.EndHorizontal();
+ EditorGUILayout.BeginHorizontal();
+ using (new EditorGUI.DisabledScope(true))
+ {
+ EditorGUI.indentLevel++;
+ EditorGUILayout.ObjectField(
+ "Serialized Udon Program Asset ID: ",
+ _serializedProgramAssetProperty.objectReferenceValue,
+ typeof(AbstractSerializedUdonProgramAsset),
+ false
+ );
+
+ EditorGUI.indentLevel--;
+ }
+
+ AbstractUdonProgramSource programSource = (AbstractUdonProgramSource)_programSourceProperty.objectReferenceValue;
+ AbstractSerializedUdonProgramAsset serializedUdonProgramAsset = programSource.SerializedProgramAsset;
+ if (_serializedProgramAssetProperty.objectReferenceValue != serializedUdonProgramAsset)
+ {
+ _serializedProgramAssetProperty.objectReferenceValue = serializedUdonProgramAsset;
+ serializedObject.ApplyModifiedPropertiesWithoutUndo();
+ }
+ }
+
+ EditorGUILayout.EndHorizontal();
+
+ udonTarget.RunEditorUpdate(ref dirty);
+ if (dirty && !Application.isPlaying)
+ {
+ EditorSceneManager.MarkSceneDirty(udonTarget.gameObject.scene);
+ }
+ }
+ }
+
+ private static AbstractUdonProgramSource CreateUdonProgramSourceAsset(Type newProgramType, string displayName, Scene scene, string udonBehaviourName)
+ {
+ string scenePath = Path.GetDirectoryName(scene.path) ?? "Assets";
+
+ string folderName = $"{scene.name}_UdonProgramSources";
+ string folderPath = Path.Combine(scenePath, folderName);
+
+ if (!AssetDatabase.IsValidFolder(folderPath))
+ {
+ AssetDatabase.CreateFolder(scenePath, folderName);
+ }
+
+ string assetPath = Path.Combine(folderPath, $"{udonBehaviourName} {displayName}.asset");
+ assetPath = AssetDatabase.GenerateUniqueAssetPath(assetPath);
+
+ AbstractUdonProgramSource asset = (AbstractUdonProgramSource)CreateInstance(newProgramType);
+ AssetDatabase.CreateAsset(asset, assetPath);
+ AssetDatabase.SaveAssets();
+ AssetDatabase.Refresh();
+ return asset;
+ }
+
+ private static List<(string displayName, Type newProgramType)> GetProgramSourceTypesForNewMenu()
+ {
+ Type abstractProgramSourceType = typeof(AbstractUdonProgramSource);
+ Type attributeNewMenuAttributeType = typeof(UdonProgramSourceNewMenuAttribute);
+
+ List<(string displayName, Type newProgramType)> programSourceTypesForNewMenu = new List<(string displayName, Type newProgramType)>();
+ foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
+ {
+ UdonProgramSourceNewMenuAttribute[] udonProgramSourceNewMenuAttributes;
+ try
+ {
+ udonProgramSourceNewMenuAttributes = (UdonProgramSourceNewMenuAttribute[])assembly.GetCustomAttributes(attributeNewMenuAttributeType, false);
+ }
+ catch
+ {
+ udonProgramSourceNewMenuAttributes = new UdonProgramSourceNewMenuAttribute[0];
+ }
+
+ foreach (UdonProgramSourceNewMenuAttribute udonProgramSourceNewMenuAttribute in udonProgramSourceNewMenuAttributes)
+ {
+ if (udonProgramSourceNewMenuAttribute == null)
+ {
+ continue;
+ }
+
+ if (!abstractProgramSourceType.IsAssignableFrom(udonProgramSourceNewMenuAttribute.Type))
+ {
+ continue;
+ }
+
+ programSourceTypesForNewMenu.Add((udonProgramSourceNewMenuAttribute.DisplayName, udonProgramSourceNewMenuAttribute.Type));
+ }
+ }
+
+ programSourceTypesForNewMenu.Sort(
+ (left, right) => string.Compare(
+ left.displayName,
+ right.displayName,
+ StringComparison.OrdinalIgnoreCase
+ )
+ );
+
+ return programSourceTypesForNewMenu;
+ }
+ }
+}