- Home /
Question by
idbrii · Feb 21, 2018 at 11:18 PM ·
editor-scriptingpropertydrawer
Create an Attribute that references another property
I want to make a variable with a custom editor that's dependent on the value of another variable.
Simplified example:
public class IndexInStringBehaviour : MonoBehaviour
{
[IndexInString("Content")] public int Index;
public string Content;
}
Enumerate the letters in Content in a dropdown, let the user pick one from a list, and then be able to extract it at runtime (I don't expect Content to change at runtime).
This pattern would be useful for a list of child transforms of a GameObject or other data within my data.
Comment
Answer by idbrii · Feb 21, 2018 at 11:18 PM
With the following code, you can extract the selected letter like so:
var c = IndexInStringAttribute.GetLetter(Content, Index);
Put this script in a file outside of Editor (it's referenced by your runtime code via the attribute).
using UnityEngine;
#if UNITY_EDITOR
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Reflection;
using System;
using UnityEditor;
#endif
namespace Me.Inspector {
public class IndexInStringAttribute : PropertyAttribute {
// Name of the container to reference.
public string ContentVar;
public IndexInStringAttribute(string ContentVar) {
this.ContentVar = ContentVar;
}
// Convert from the int decorated with IndexInString to the
// selected child.
public static char GetLetter(string container, int letter_index) {
return container[letter_index];
}
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(IndexInStringAttribute))]
public class IndexInStringDrawer : PropertyDrawer {
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
var content_var = (attribute as IndexInStringAttribute).ContentVar;
EditorGUI.BeginProperty(position, label, property);
{
// Try to find container as sibling variable.
SerializedProperty container_prop = property.serializedObject.FindProperty(content_var);
if (container_prop == null) {
// serializedObject might be an array. Swap our name with
// container and try again.
var container_path = property.propertyPath.Replace(property.name, content_var);
container_prop = property.serializedObject.FindProperty(container_path);
}
if (container_prop != null) {
// TODO: handle container_prop.hasMultipleDifferentValues
var container = container_prop.stringValue; // use objectReferenceValue for Serializable objects
// Here you'd do whatever you want with the container
// value. Let's make a popup of its contents.
var letters = new string[] {};
if (container != null) {
// Popup entries must be unique so include index
letters = container.ToCharArray().Select((c, i) => string.Format("{0} - {1}", i, c)).ToArray();
}
property.intValue = EditorGUI.Popup(position, property.displayName, property.intValue, letters);
}
else
{
// Fallback when we can't find container property.
EditorGUI.PropertyField(position, property);
}
}
EditorGUI.EndProperty();
}
}
#endif
}