One extension is to draw static fields such that they can be edited by any instance and changed for all of them. I’m working in two parts:
Part 1: A custom attribute with no code.
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
public class DrawGetterAttribute : PropertyAttribute {}
Part 2: A custom editor for all Unity game objects, based on t0chas’ work,
[UnityEditor.CustomEditor(typeof(UnityEngine.Object), true, isFallback = false)]
public class EditorFPS : CustomEditorBase
Example usage: [DrawStatic] public static int testStaticInt = 42
I used System.Type.GetFields() to pull the static fields marked with [DrawStatic], but now I’m not sure how to plug them back into EditorGuiLayout.PropertyField(). Since I’m pulling it from the C# type, it’s not packaged in a SerializedProperty that can easily be passed back in.
You are not going to be able to use PropertyField since that relies on SerializedObjects, something that you can’t make using a bunch of FieldInfos. You will essentially need to reimplement the functionality of PropertyField yourself by checking the types of the fields you’ve gathered.
Yep. And it requires quite a bit of work to get full feature parity with EditorGUILayout.PropertyField. This is one of those things where the ninety-ninety rule is in full effect.
Here are some things to consider when implementing this:
You need to draw the right kind of field based on the field type, which could be anything (EditorGUILayout.IntField for int fields etc.). If you only need to support primitive types this is very doable, but if you want any types of datasets to be supported, it gets more complicated.
You need to get and set the field value using reflection. This is simple enough in most cases, but if the field exists inside a struct, setting the value gets more complicated (not sure if this is even a possible scenario with the custom editor based approach though).
If you want multi-target editing to work, you need to implement that manually.
If you want undo to work you’ll need to implement that manually.
If you want OnValidate to get called for the component, you need to implement that separately. It’s debatable whether this makes sense with static fields or not, but I would say it does.
Note sure if I forgot some gotchas, but handling all of these should get you pretty close to feature parity
It’s also worth pointing out that after all that this would be a purely runtime (or editor) debugging tool, since your changes won’t be serialized or saved.
If you want to have a central repository of editable but global data, look into creating a ScriptableObject asset to hold it.
I felt like that would be the case, but I figured I should ask just in case I missed any undocumented methods or something. Thanks for the answers, everybody!
Yeah, I already have several SOs as pseudo-global data objects, but I’m trying to figure out a way to make this a drop-in solution. You know how sometimes a class has a single static field, and you really don’t want to put in the time overhead for making a SO class for just that one thing? That’s the use case I’m targeting.
That said, I can just use the value pulled from System.Type.GetFields().Where(pi => pi.GetCustomAttributes(false).Any(attr => attr is DrawStaticAttribute) and put it in an EditorGUILayout.LabelField(). That way I can get most of the way there, the designers can let me know if they want a value to change, and then I can time shift the evaluation of whether we need a new SO until after someone says something.
I don’t remember where it is, but I that there’s a package that basically does this – it creates shared fields that are sourced from a ScriptableObject.