Skip to content

Instantly share code, notes, and snippets.

@CustomPhase
Created July 28, 2019 16:31

Revisions

  1. CustomPhase created this gist Jul 28, 2019.
    22 changes: 22 additions & 0 deletions CustomUnityAction.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    [System.Serializable]
    public class CustomUnityAction {

    [SerializeField]
    public List<SerializableAction> actions;

    public void Invoke()
    {
    if (actions != null)
    {
    for (int i = 0; i < actions.Count; i++)
    {
    actions[i].Invoke();
    }
    }
    }

    }
    58 changes: 58 additions & 0 deletions CustomUnityActionDrawer.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,58 @@
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;

    [CustomPropertyDrawer(typeof(CustomUnityAction))]
    public class CustomUnityActionDrawer : PropertyDrawer {

    public override float GetPropertyHeight(SerializedProperty property, UnityEngine.GUIContent label)
    {
    return getList(property.FindPropertyRelative("actions")).GetHeight()+8;
    }

    private UnityEditorInternal.ReorderableList list;
    private string propName;

    private UnityEditorInternal.ReorderableList getList(SerializedProperty property)
    {
    if (list == null)
    {
    list = new UnityEditorInternal.ReorderableList(property.serializedObject, property, true, true, true, true);
    list.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
    {
    rect.width -= 5;
    rect.x += 5;
    EditorGUI.PropertyField(rect, property.GetArrayElementAtIndex(index), true);
    };
    list.drawHeaderCallback += DrawHeader;
    }
    return list;
    }

    private void DrawHeader(Rect rect)
    {
    GUI.Label(rect, propName, EditorStyles.boldLabel);
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
    // Using BeginProperty / EndProperty on the parent property means that
    // prefab override logic works on the entire property.
    EditorGUI.BeginProperty(position, label, property);
    propName = ObjectNames.NicifyVariableName(property.name);
    var listProperty = property.FindPropertyRelative("actions");
    var list = getList(listProperty);
    var height = 0f;
    for (var i = 0; i < listProperty.arraySize; i++)
    {
    height = Mathf.Max(height, EditorGUI.GetPropertyHeight(listProperty.GetArrayElementAtIndex(i)));
    }
    list.elementHeight = height;
    position.x += 4;
    list.DoList(position);

    EditorGUI.EndProperty();
    }

    }
    84 changes: 84 additions & 0 deletions SerializableAction.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,84 @@
    using System;
    using System.IO;
    using System.Runtime.Serialization.Formatters.Binary;
    using UnityEngine;

    [Serializable]
    public class SerializableAction
    {
    [SerializeField]
    private UnityEngine.Object _target;
    [SerializeField]
    private string _methodName = "";
    [SerializeField]
    private byte[] _serialData = { };

    [SerializeField]
    private GameObject go;

    Action _action;

    public void Invoke()
    {
    if (_action == null)
    {
    _action = CreateDelegate();
    }
    _action.Invoke();
    }

    public void SetDelegate(Action action)
    {
    if (action == null)
    {
    _target = null;
    _methodName = "";
    _serialData = new byte[] { };
    return;
    }

    var delAction = action as Delegate;
    if (delAction == null)
    {
    throw new InvalidOperationException(typeof(Action).Name + " is not a delegate type.");
    }


    _target = delAction.Target as UnityEngine.Object;

    if (_target != null)
    {
    _methodName = delAction.Method.Name;
    _serialData = null;
    }
    else
    {
    //Serialize the data to a binary stream
    using (var stream = new MemoryStream())
    {
    (new BinaryFormatter()).Serialize(stream, action);
    stream.Flush();
    _serialData = stream.ToArray();
    }
    _methodName = null;
    }
    }

    public Action CreateDelegate()
    {
    if (_serialData.Length == 0 && _methodName == "")
    {
    return null;
    }

    if (_target != null)
    {
    return Delegate.CreateDelegate(typeof(Action), _target, _methodName) as Action;
    }

    using (var stream = new MemoryStream(_serialData))
    {
    return (new BinaryFormatter()).Deserialize(stream) as Action;
    }
    }
    }
    93 changes: 93 additions & 0 deletions SerializableActionDrawer.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,93 @@
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    using System.Reflection;
    using System.Linq;

    [CustomPropertyDrawer(typeof(SerializableAction))]
    public class SerializableActionDrawer : PropertyDrawer {

    int selection;

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
    return 26;
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
    // Using BeginProperty / EndProperty on the parent property means that
    // prefab override logic works on the entire property.
    EditorGUI.BeginProperty(position, label, property);

    // Draw label
    //position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);

    // Don't make child fields be indented
    var indent = EditorGUI.indentLevel;
    EditorGUI.indentLevel = 0;

    // Calculate rects
    Rect objectRect = new Rect(position.x, position.y+5, 90, position.height-10);
    var selectRect = new Rect(position.x + 96, position.y+5, position.width - 96, position.height-10);

    bool changedObject = false;
    EditorGUI.BeginChangeCheck();
    EditorGUI.ObjectField(objectRect, property.FindPropertyRelative("go"), typeof(GameObject), GUIContent.none);
    changedObject = EditorGUI.EndChangeCheck();
    //EditorGUI.PropertyField(objectRect, property.FindPropertyRelative("go"), GUIContent.none);

    if (property.FindPropertyRelative("go").objectReferenceValue != null)
    {
    selection = 0;
    int count = 0;
    GameObject go = property.FindPropertyRelative("go").objectReferenceValue as GameObject;
    Component[] components = go.GetComponents<Component>();
    List<string> methods = new List<string>();
    Dictionary<int, KeyValuePair<Object, string>> data = new Dictionary<int, KeyValuePair<Object, string>>();
    for (int i = 0; i<components.Length; i++)
    {
    if (components[i].GetType() != typeof(Transform)) {
    MethodInfo[] _meth = components[i].GetType().GetMethods();
    for (int m = 0; m < _meth.Length; m++)
    {
    if (_meth[m].IsPublic && !_meth[m].IsSpecialName)
    {
    methods.Add(components[i].GetType().Name + "/" + _meth[m].Name);
    if (components[i]==property.FindPropertyRelative("_target").objectReferenceValue &&
    _meth[m].Name==property.FindPropertyRelative("_methodName").stringValue )
    {
    selection = count;
    }
    data.Add(count, new KeyValuePair<Object, string>(components[i], _meth[m].Name));
    count++;
    }
    }
    }
    }
    EditorGUI.BeginChangeCheck();
    selection = EditorGUI.Popup(selectRect, selection, methods.ToArray());
    if (EditorGUI.EndChangeCheck() || changedObject)
    {
    property.FindPropertyRelative("_target").objectReferenceValue = data[selection].Key;
    property.FindPropertyRelative("_methodName").stringValue = data[selection].Value;
    }
    } else
    {
    GUI.enabled = false;
    EditorGUI.Popup(selectRect, selection, new string[] { "Select object first" });
    GUI.enabled = true;
    }

    // Draw fields - passs GUIContent.none to each so they are drawn without labels
    //EditorGUI.PropertyField(amountRect, property.FindPropertyRelative("amount"), GUIContent.none);
    //EditorGUI.PropertyField(unitRect, property.FindPropertyRelative("unit"), GUIContent.none);
    //EditorGUI.PropertyField(nameRect, property.FindPropertyRelative("name"), GUIContent.none);

    // Set indent back to what it was
    EditorGUI.indentLevel = indent;

    EditorGUI.EndProperty();
    }
    }