using System; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Serialization; using JetBrains.Annotations; using UnityEditor; using UnityEditor.SceneManagement; using UnityEditorInternal; using UnityEngine; using UnityEngine.Assertions; using VRC.Udon.Common; using VRC.Udon.Common.Interfaces; using VRC.Udon.ProgramSources; namespace VRC.Udon.Editor.ProgramSources { public class UdonProgramAsset : AbstractUdonProgramSource, ISerializationCallbackReceiver { protected IUdonProgram program; [SerializeField] protected AbstractSerializedUdonProgramAsset serializedUdonProgramAsset; public override AbstractSerializedUdonProgramAsset SerializedProgramAsset { get { AssetDatabase.TryGetGUIDAndLocalFileIdentifier(this, out string guid, out long _); if(serializedUdonProgramAsset != null) { if(serializedUdonProgramAsset.name == guid) { return serializedUdonProgramAsset; } string oldSerializedUdonProgramAssetPath = Path.Combine("Assets", "SerializedUdonPrograms", $"{serializedUdonProgramAsset.name}.asset"); AssetDatabase.DeleteAsset(oldSerializedUdonProgramAssetPath); } string serializedUdonProgramAssetPath = Path.Combine("Assets", "SerializedUdonPrograms", $"{guid}.asset"); serializedUdonProgramAsset = (SerializedUdonProgramAsset)AssetDatabase.LoadAssetAtPath( Path.Combine("Assets", "SerializedUdonPrograms", $"{guid}.asset"), typeof(SerializedUdonProgramAsset) ); if(serializedUdonProgramAsset != null) { return serializedUdonProgramAsset; } serializedUdonProgramAsset = CreateInstance(); if(!AssetDatabase.IsValidFolder(Path.Combine("Assets", "SerializedUdonPrograms"))) { AssetDatabase.CreateFolder("Assets", "SerializedUdonPrograms"); } AssetDatabase.CreateAsset(serializedUdonProgramAsset, serializedUdonProgramAssetPath); AssetDatabase.SaveAssets(); RefreshProgram(); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); return serializedUdonProgramAsset; } } public sealed override void RunEditorUpdate(UdonBehaviour udonBehaviour, ref bool dirty) { if(program == null && serializedUdonProgramAsset != null) { program = serializedUdonProgramAsset.RetrieveProgram(); } if(program == null) { RefreshProgram(); } DrawProgramSourceGUI(udonBehaviour, ref dirty); if(dirty) { EditorUtility.SetDirty(this); } } protected virtual void DrawProgramSourceGUI(UdonBehaviour udonBehaviour, ref bool dirty) { DrawPublicVariables(udonBehaviour, ref dirty); DrawProgramDisassembly(); } public sealed override void RefreshProgram() { if(Application.isPlaying) { return; } RefreshProgramImpl(); SerializedProgramAsset.StoreProgram(program); if (this != null) { EditorUtility.SetDirty(this); } } protected virtual void RefreshProgramImpl() { } [PublicAPI] protected void DrawInteractionArea(UdonBehaviour udonBehaviour) { ImmutableArray exportedSymbols = program.EntryPoints.GetExportedSymbols(); if (exportedSymbols.Contains("_interact")) { EditorGUILayout.LabelField("Interaction", EditorStyles.boldLabel); EditorGUI.indentLevel++; if(udonBehaviour != null) { udonBehaviour.interactText = EditorGUILayout.TextField("Interaction Text", udonBehaviour.interactText); udonBehaviour.proximity = EditorGUILayout.Slider("Proximity", udonBehaviour.proximity, 0f, 100f); udonBehaviour.interactTextPlacement = (Transform)EditorGUILayout.ObjectField("Text Placement", udonBehaviour.interactTextPlacement, typeof(Transform), true); } else { using(new EditorGUI.DisabledScope(true)) { EditorGUILayout.TextField("Interaction Text", "Use"); EditorGUILayout.Slider("Proximity", 2.0f, 0f, 100f); EditorGUILayout.ObjectField("Text Placement", null, typeof(Transform), true); } } EditorGUI.indentLevel--; } } [PublicAPI] protected void DrawPublicVariables(UdonBehaviour udonBehaviour, ref bool dirty) { IUdonVariableTable publicVariables = null; if(udonBehaviour != null) { publicVariables = udonBehaviour.publicVariables; } EditorGUILayout.LabelField("Public Variables", EditorStyles.boldLabel); EditorGUI.indentLevel++; if(program?.SymbolTable == null) { EditorGUILayout.LabelField("No public variables."); EditorGUI.indentLevel--; return; } IUdonSymbolTable symbolTable = program.SymbolTable; // Remove non-exported public variables if(publicVariables != null) { foreach(string publicVariableSymbol in publicVariables.VariableSymbols.ToArray()) { if(!symbolTable.HasExportedSymbol(publicVariableSymbol)) { publicVariables.RemoveVariable(publicVariableSymbol); } } } ImmutableArray exportedSymbolNames = symbolTable.GetExportedSymbols(); if(exportedSymbolNames.Length <= 0) { EditorGUILayout.LabelField("No public variables."); EditorGUI.indentLevel--; return; } foreach(string exportedSymbol in exportedSymbolNames) { Type symbolType = symbolTable.GetSymbolType(exportedSymbol); if(publicVariables == null) { DrawPublicVariableField(exportedSymbol, GetPublicVariableDefaultValue(exportedSymbol, symbolType), symbolType, ref dirty, false); continue; } if(!publicVariables.TryGetVariableType(exportedSymbol, out Type declaredType) || declaredType != symbolType) { publicVariables.RemoveVariable(exportedSymbol); if(!publicVariables.TryAddVariable(CreateUdonVariable(exportedSymbol, GetPublicVariableDefaultValue(exportedSymbol, declaredType), symbolType))) { EditorGUILayout.LabelField($"Error drawing field for symbol '{exportedSymbol}'."); continue; } } if(!publicVariables.TryGetVariableValue(exportedSymbol, out object variableValue)) { variableValue = GetPublicVariableDefaultValue(exportedSymbol, declaredType); } variableValue = DrawPublicVariableField(exportedSymbol, variableValue, symbolType, ref dirty, true); if(!dirty) { continue; } Undo.RecordObject(udonBehaviour, "Modify Public Variable"); if(!publicVariables.TrySetVariableValue(exportedSymbol, variableValue)) { if(!publicVariables.TryAddVariable(CreateUdonVariable(exportedSymbol, variableValue, symbolType))) { Debug.LogError($"Failed to set public variable '{exportedSymbol}' value."); } } EditorSceneManager.MarkSceneDirty(udonBehaviour.gameObject.scene); if(PrefabUtility.IsPartOfPrefabInstance(udonBehaviour)) { PrefabUtility.RecordPrefabInstancePropertyModifications(udonBehaviour); } } EditorGUI.indentLevel--; } private static IUdonVariable CreateUdonVariable(string symbolName, object value, Type declaredType) { Type udonVariableType = typeof(UdonVariable<>).MakeGenericType(declaredType); return (IUdonVariable)Activator.CreateInstance(udonVariableType, symbolName, value); } [PublicAPI] protected virtual object GetPublicVariableDefaultValue(string symbol, Type type) { return null; } [PublicAPI] protected void DrawProgramDisassembly() { try { EditorGUILayout.LabelField("Disassembled Program", EditorStyles.boldLabel); using(new EditorGUI.DisabledScope(true)) { string[] disassembledProgram = UdonEditorManager.Instance.DisassembleProgram(program); EditorGUILayout.TextArea(string.Join("\n", disassembledProgram)); } } catch(Exception e) { Debug.LogException(e); } } [NonSerialized] private readonly Dictionary _arrayStates = new Dictionary(); protected virtual object DrawPublicVariableField(string symbol, object variableValue, Type variableType, ref bool dirty, bool enabled) { using(new EditorGUI.DisabledScope(!enabled)) { // ReSharper disable RedundantNameQualifier if(!variableType.IsInstanceOfType(variableValue)) { if(variableType.IsValueType) { variableValue = Activator.CreateInstance(variableType); } else { variableValue = null; } } EditorGUILayout.BeginHorizontal(); if(typeof(UnityEngine.Object).IsAssignableFrom(variableType)) { UnityEngine.Object unityEngineObjectValue = (UnityEngine.Object)variableValue; EditorGUI.BeginChangeCheck(); Rect fieldRect = EditorGUILayout.GetControlRect(); variableValue = EditorGUI.ObjectField(fieldRect, symbol, unityEngineObjectValue, variableType, true); if(variableValue == null && (variableType == typeof(GameObject) || variableType == typeof(Transform) || variableType == typeof(UdonBehaviour))) { EditorGUI.LabelField( fieldRect, new GUIContent(symbol), new GUIContent("Self (" + variableType.Name + ")", AssetPreview.GetMiniTypeThumbnail(variableType)), EditorStyles.objectField); } if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(string)) { string stringValue = (string)variableValue; EditorGUI.BeginChangeCheck(); variableValue = EditorGUILayout.TextField(symbol, stringValue); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(string[])) { string[] valueArray = (string[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = EditorGUILayout.TextField( $"{i}:", valueArray.Length > i ? valueArray[i] : ""); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(float)) { float floatValue = (float?)variableValue ?? default; EditorGUI.BeginChangeCheck(); variableValue = EditorGUILayout.FloatField(symbol, floatValue); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(float[])) { float[] valueArray = (float[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = EditorGUILayout.FloatField( $"{i}:", valueArray.Length > i ? valueArray[i] : 0); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(int)) { int intValue = (int?)variableValue ?? default; EditorGUI.BeginChangeCheck(); variableValue = EditorGUILayout.IntField(symbol, intValue); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(int[])) { int[] valueArray = (int[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = EditorGUILayout.IntField( $"{i}:", valueArray.Length > i ? valueArray[i] : 0); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(short)) { short intValue = (short?)variableValue ?? default; EditorGUI.BeginChangeCheck(); variableValue = (short)EditorGUILayout.IntField(symbol, intValue); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(short[])) { short[] valueArray = (short[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = (short)EditorGUILayout.IntField( $"{i}:", valueArray.Length > i ? valueArray[i] : 0); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(long)) { long intValue = (long?)variableValue ?? default; EditorGUI.BeginChangeCheck(); variableValue = (long)EditorGUILayout.IntField(symbol, (int)intValue); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(long[])) { long[] valueArray = (long[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = EditorGUILayout.IntField( $"{i}:", valueArray.Length > i ? (int)valueArray[i] : 0); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(uint)) { uint intValue = (uint?)variableValue ?? default; EditorGUI.BeginChangeCheck(); variableValue = (uint)EditorGUILayout.IntField(symbol, (int)intValue); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(uint[])) { uint[] valueArray = (uint[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = (uint)EditorGUILayout.IntField( $"{i}:", valueArray.Length > i ? (int)valueArray[i] : 0); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(ushort)) { ushort intValue = (ushort?)variableValue ?? default; EditorGUI.BeginChangeCheck(); variableValue = (ushort)EditorGUILayout.IntField(symbol, (int)intValue); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(ushort[])) { ushort[] valueArray = (ushort[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = (ushort)EditorGUILayout.IntField( $"{i}:", valueArray.Length > i ? (int)valueArray[i] : 0); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(ulong)) { ulong intValue = (ulong?)variableValue ?? default; EditorGUI.BeginChangeCheck(); variableValue = (ulong)EditorGUILayout.IntField(symbol, (int)intValue); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(ulong[])) { ulong[] valueArray = (ulong[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = (ulong)EditorGUILayout.IntField( $"{i}:", valueArray.Length > i ? (int)valueArray[i] : 0); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(byte)) { byte intValue = (byte?)variableValue ?? default; EditorGUI.BeginChangeCheck(); variableValue = (byte)EditorGUILayout.IntField(symbol, (int)intValue); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(byte[])) { byte[] valueArray = (byte[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = (byte)EditorGUILayout.IntField( $"{i}:", valueArray.Length > i ? (int)valueArray[i] : 0); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(sbyte)) { sbyte intValue = (sbyte?)variableValue ?? default; EditorGUI.BeginChangeCheck(); variableValue = (sbyte)EditorGUILayout.IntField(symbol, (int)intValue); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(sbyte[])) { sbyte[] valueArray = (sbyte[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = (sbyte)EditorGUILayout.IntField( $"{i}:", valueArray.Length > i ? (int)valueArray[i] : 0); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(double)) { double intValue = (double?)variableValue ?? default; EditorGUI.BeginChangeCheck(); variableValue = (double)EditorGUILayout.DoubleField(symbol, intValue); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(double[])) { double[] valueArray = (double[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = EditorGUILayout.DoubleField( $"{i}:", valueArray.Length > i ? valueArray[i] : 0); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(decimal)) { decimal intValue = (decimal?)variableValue ?? default; EditorGUI.BeginChangeCheck(); variableValue = (decimal)EditorGUILayout.DoubleField(symbol, (double)intValue); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(decimal[])) { decimal[] valueArray = (decimal[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = (decimal)EditorGUILayout.DoubleField( $"{i}:", valueArray.Length > i ? (double)valueArray[i] : 0); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(bool)) { bool boolValue = (bool?)variableValue ?? default; EditorGUI.BeginChangeCheck(); variableValue = EditorGUILayout.Toggle(symbol, boolValue); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(bool[])) { bool[] valueArray = (bool[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = EditorGUILayout.Toggle( $"{i}:", valueArray.Length > i ? valueArray[i] : false); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(UnityEngine.Vector2)) { Vector2 vector2Value = (Vector2?)variableValue ?? default; EditorGUI.BeginChangeCheck(); variableValue = EditorGUILayout.Vector2Field(symbol, vector2Value); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(Vector2[])) { Vector2[] valueArray = (Vector2[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = EditorGUILayout.Vector2Field( $"{i}:", valueArray.Length > i ? valueArray[i] : Vector2.zero); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(UnityEngine.Vector3)) { Vector3 vector3Value = (Vector3?)variableValue ?? default; EditorGUI.BeginChangeCheck(); variableValue = EditorGUILayout.Vector3Field(symbol, vector3Value); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(Vector3[])) { Vector3[] valueArray = (Vector3[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = EditorGUILayout.Vector3Field( $"{i}:", valueArray.Length > i ? valueArray[i] : Vector3.zero); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(UnityEngine.Vector2Int)) { Vector2Int vector2IntValue = (Vector2Int?)variableValue ?? default; EditorGUI.BeginChangeCheck(); Vector2 vector2Value = EditorGUILayout.Vector2Field(symbol, vector2IntValue); variableValue = new Vector2Int((int)vector2Value.x, (int)vector2Value.y); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(Vector2Int[])) { Vector2Int[] valueArray = (Vector2Int[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); Vector2 vector2Value = EditorGUILayout.Vector2Field( $"{i}:", valueArray.Length > i ? valueArray[i] : Vector2.zero); valueArray[i] = new Vector2Int((int)vector2Value.x, (int)vector2Value.y); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(UnityEngine.Vector3Int)) { Vector3Int vector3IntValue = (Vector3Int?)variableValue ?? default; EditorGUI.BeginChangeCheck(); Vector3 vector3Value = EditorGUILayout.Vector3Field(symbol, vector3IntValue); variableValue = new Vector3Int((int)vector3Value.x, (int)vector3Value.y, (int)vector3Value.z); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(Vector3Int[])) { Vector3Int[] valueArray = (Vector3Int[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); Vector3 vector3Value = EditorGUILayout.Vector3Field( $"{i}:", valueArray.Length > i ? valueArray[i] : Vector3.zero); valueArray[i] = new Vector3Int((int)vector3Value.x, (int)vector3Value.y, (int)vector3Value.z); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(UnityEngine.Vector4)) { Vector4 vector4Value = (Vector4?)variableValue ?? default; EditorGUI.BeginChangeCheck(); variableValue = EditorGUILayout.Vector4Field(symbol, vector4Value); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(Vector4[])) { Vector4[] valueArray = (Vector4[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = EditorGUILayout.Vector4Field( $"{i}:", valueArray.Length > i ? valueArray[i] : Vector4.zero); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(UnityEngine.Quaternion)) { Quaternion quaternionValue = (Quaternion?)variableValue ?? default; EditorGUI.BeginChangeCheck(); Vector4 quaternionVector4 = EditorGUILayout.Vector4Field(symbol, new Vector4(quaternionValue.x, quaternionValue.y, quaternionValue.z, quaternionValue.w)); variableValue = new Quaternion(quaternionVector4.x, quaternionVector4.y, quaternionVector4.z, quaternionVector4.w); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(Quaternion[])) { Quaternion[] valueArray = (Quaternion[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); Vector4 vector4 = EditorGUILayout.Vector4Field( $"{i}:", valueArray.Length > i ? new Vector4(valueArray[i].x, valueArray[i].y, valueArray[i].z, valueArray[i].w) : Vector4.zero); valueArray[i] = new Quaternion(vector4.x, vector4.y, vector4.z, vector4.w); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(Gradient)) { Gradient color2Value = variableValue as Gradient; if (color2Value == null) color2Value = new Gradient(); EditorGUI.BeginChangeCheck(); variableValue = EditorGUILayout.GradientField(symbol, color2Value); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(Gradient[])) { Gradient[] valueArray = (Gradient[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); Gradient g = valueArray.Length > i ? (valueArray[i]) : new Gradient(); if (g == null) g = new Gradient(); valueArray[i] = EditorGUILayout.GradientField($"{i}:", g); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(AnimationCurve)) { AnimationCurve curve2Value = variableValue as AnimationCurve; if (curve2Value == null) curve2Value = new AnimationCurve(); EditorGUI.BeginChangeCheck(); variableValue = EditorGUILayout.CurveField(symbol, curve2Value); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(AnimationCurve[])) { AnimationCurve[] valueArray = (AnimationCurve[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); AnimationCurve curve = valueArray.Length > i ? (valueArray[i]) : new AnimationCurve(); if (curve == null) curve = new AnimationCurve(); valueArray[i] = EditorGUILayout.CurveField($"{i}:", curve); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(UnityEngine.Color)) { Color color2Value = (Color?)variableValue ?? default; EditorGUI.BeginChangeCheck(); variableValue = EditorGUILayout.ColorField(symbol, color2Value); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(Color[])) { Color[] valueArray = (Color[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = EditorGUILayout.ColorField( $"{i}:", valueArray.Length > i ? valueArray[i] : Color.white); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(UnityEngine.Color32)) { Color32 colorValue = (Color32?)variableValue ?? default; EditorGUI.BeginChangeCheck(); variableValue = (Color32)EditorGUILayout.ColorField(symbol, colorValue); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(Color32[])) { Color32[] valueArray = (Color32[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } EditorGUILayout.BeginVertical(); EditorGUI.BeginChangeCheck(); // Show Foldout Header showArray = EditorGUILayout.Foldout(showArray, symbol, true); // Save foldout state _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = EditorGUILayout.ColorField( $"{i}:", valueArray.Length > i ? valueArray[i] : (Color32)Color.white); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(ParticleSystem.MinMaxCurve)) { ParticleSystem.MinMaxCurve minMaxCurve = (ParticleSystem.MinMaxCurve?)variableValue ?? default; EditorGUI.BeginChangeCheck(); float multiplier = minMaxCurve.curveMultiplier; AnimationCurve minCurve = minMaxCurve.curveMin; AnimationCurve maxCurve = minMaxCurve.curveMax; EditorGUILayout.BeginVertical(); EditorGUILayout.LabelField(symbol); EditorGUI.indentLevel++; multiplier = EditorGUILayout.FloatField("Multiplier", multiplier); minCurve = EditorGUILayout.CurveField("Min Curve", minCurve); maxCurve = EditorGUILayout.CurveField("Max Curve", maxCurve); EditorGUI.indentLevel--; EditorGUILayout.EndVertical(); variableValue = new ParticleSystem.MinMaxCurve(multiplier, minCurve, maxCurve); if(EditorGUI.EndChangeCheck()) { dirty = true; } } else if(variableType == typeof(ParticleSystem.MinMaxCurve[])) { EditorGUI.BeginChangeCheck(); EditorGUILayout.BeginVertical(); ParticleSystem.MinMaxCurve[] valueArray = (ParticleSystem.MinMaxCurve[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } showArray = EditorGUILayout.Foldout(showArray, symbol, true); _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); ParticleSystem.MinMaxCurve minMaxCurve = (ParticleSystem.MinMaxCurve)valueArray[i]; float multiplier = minMaxCurve.curveMultiplier; AnimationCurve minCurve = minMaxCurve.curveMin; AnimationCurve maxCurve = minMaxCurve.curveMax; EditorGUILayout.BeginVertical(); EditorGUI.indentLevel++; multiplier = EditorGUILayout.FloatField("Multiplier", multiplier); minCurve = EditorGUILayout.CurveField("Min Curve", minCurve); maxCurve = EditorGUILayout.CurveField("Max Curve", maxCurve); EditorGUI.indentLevel--; EditorGUILayout.EndVertical(); valueArray[i] = new ParticleSystem.MinMaxCurve(multiplier, minCurve, maxCurve); EditorGUILayout.Space(); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType.IsEnum) { Enum enumValue = (Enum)variableValue; GUI.SetNextControlName("NodeField"); EditorGUI.BeginChangeCheck(); variableValue = EditorGUILayout.EnumPopup(symbol, enumValue); if(EditorGUI.EndChangeCheck()) { dirty = true; } } // ReSharper disable once PossibleNullReferenceException else if(variableType.IsArray && variableType.GetElementType().IsEnum) { EditorGUI.BeginChangeCheck(); EditorGUILayout.LabelField(symbol); EditorGUILayout.BeginVertical(); Enum[] valueArray = (Enum[])variableValue; GUI.SetNextControlName("NodeField"); bool showArray = false; if(_arrayStates.ContainsKey(symbol)) { showArray = _arrayStates[symbol]; } else { _arrayStates.Add(symbol, false); } showArray = EditorGUILayout.Foldout(showArray, symbol); _arrayStates[symbol] = showArray; if(showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1 ); EditorGUILayout.Space(); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if(valueArray != null && valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = EditorGUILayout.EnumPopup( $"{i}:", valueArray[i]); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if(variableType == typeof(Type)) { Type typeValue = (Type)variableValue; EditorGUILayout.LabelField(symbol, typeValue == null ? $"Type = null" : $"Type = {typeValue.Name}"); } else if(variableType.IsArray && typeof(UnityEngine.Object).IsAssignableFrom(variableType.GetElementType())) { Type elementType = variableType.GetElementType(); Assert.IsNotNull(elementType); EditorGUI.BeginChangeCheck(); EditorGUILayout.BeginVertical(); GUI.SetNextControlName("NodeField"); bool showArray = false; if (_arrayStates.ContainsKey(symbol)) showArray = _arrayStates[symbol]; else _arrayStates.Add(symbol, false); showArray = EditorGUILayout.Foldout( showArray, symbol, true ); _arrayStates[symbol] = showArray; if(variableValue == null) { variableValue = Array.CreateInstance(elementType, 0); } UnityEngine.Object[] valueArray = (UnityEngine.Object[])variableValue; if (showArray) { EditorGUI.indentLevel++; int newSize = EditorGUILayout.IntField( "size:", valueArray.Length > 0 ? valueArray.Length : 1); Array.Resize(ref valueArray, newSize); Assert.IsNotNull(valueArray); if(valueArray.Length > 0) { for(int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); valueArray[i] = EditorGUILayout.ObjectField($"{i}:", valueArray.Length > i ? valueArray[i] : null, variableType.GetElementType(), true); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if(EditorGUI.EndChangeCheck()) { Array destinationArray = Array.CreateInstance(elementType, valueArray.Length); Array.Copy(valueArray, destinationArray, valueArray.Length); variableValue = destinationArray; dirty = true; } } else if (variableType == typeof(VRC.SDKBase.VRCUrl)) { if(variableValue == null) variableValue = new VRC.SDKBase.VRCUrl(""); VRC.SDKBase.VRCUrl url = (VRC.SDKBase.VRCUrl)variableValue; EditorGUI.BeginChangeCheck(); variableValue = new VRC.SDKBase.VRCUrl(EditorGUILayout.TextField(symbol, url.Get())); if (EditorGUI.EndChangeCheck()) { dirty = true; } } else if (variableType == typeof(VRC.SDKBase.VRCUrl[])) { EditorGUI.BeginChangeCheck(); EditorGUILayout.BeginVertical(); GUI.SetNextControlName("NodeField"); bool showArray = false; if (_arrayStates.ContainsKey(symbol)) showArray = _arrayStates[symbol]; else _arrayStates.Add(symbol, false); showArray = EditorGUILayout.Foldout( showArray, symbol, true ); _arrayStates[symbol] = showArray; VRC.SDKBase.VRCUrl[] valueArray = (VRC.SDKBase.VRCUrl[])variableValue; if (showArray) { EditorGUI.indentLevel++; EditorGUILayout.Space(); int newSize = EditorGUILayout.IntField( "size:", valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1); newSize = newSize >= 0 ? newSize : 0; Array.Resize(ref valueArray, newSize); if (valueArray != null && valueArray.Length > 0) { for (int i = 0; i < valueArray.Length; i++) { GUI.SetNextControlName("NodeField"); if (valueArray[i] == null) valueArray[i] = new VRC.SDKBase.VRCUrl(""); valueArray[i] = new VRC.SDKBase.VRCUrl( EditorGUILayout.TextField( $"{i}:", valueArray.Length > i ? valueArray[i].Get() : "")); } } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); if (EditorGUI.EndChangeCheck()) { variableValue = valueArray; dirty = true; } } else if (variableType == typeof(LayerMask)) { LayerMask maskValue = (LayerMask)variableValue; GUI.SetNextControlName("NodeField"); EditorGUI.BeginChangeCheck(); EditorGUILayout.LabelField(symbol); // Using workaround from http://answers.unity.com/answers/1387522/view.html LayerMask tempMask = EditorGUILayout.MaskField(InternalEditorUtility.LayerMaskToConcatenatedLayersMask(maskValue), InternalEditorUtility.layers); variableValue = InternalEditorUtility.ConcatenatedLayersMaskToLayerMask(tempMask); if (EditorGUI.EndChangeCheck()) { dirty = true; } } else { EditorGUILayout.LabelField(symbol + " no defined editor for type of " + variableType); } // ReSharper restore RedundantNameQualifier IUdonSyncMetadata sync = program.SyncMetadataTable.GetSyncMetadataFromSymbol(symbol); if(sync != null) { GUILayout.Label($"sync{sync.Properties[0].InterpolationAlgorithm.ToString()}", GUILayout.Width(80)); } } EditorGUILayout.EndHorizontal(); return variableValue; } #region Serialization Methods void ISerializationCallbackReceiver.OnAfterDeserialize() { OnAfterDeserialize(); } void ISerializationCallbackReceiver.OnBeforeSerialize() { OnBeforeSerialize(); } [PublicAPI] protected virtual void OnAfterDeserialize() { } [PublicAPI] protected virtual void OnBeforeSerialize() { } #endregion } }