See comments in repro code below for more details. You should be able to copy/paste/run that code to see it for yourself.
I’ve also been trying to work around the issue by using an IMGUI wrapper, but in my actual use case I’m trying to draw a SerializedProperty and I don’t yet see a way to do that without being able to pass a reference to the property (or at least a path to the property) into the wrapper “Action”.
Please let me know if you know a workaround for this.
using System;
using UnityEditor.UIElements;
using UnityEditor;
public class Entity_inspector : EditorWindow
{
EntityFlags flags = EntityFlags.isHumanoid;
[MenuItem("Window/ enum repro")]
public static void OpenWindow()
{
GetWindow<Entity_inspector>().Show();
}
public void OnEnable()
{
rootVisualElement.Add(new EnumFlagsField(flags));
}
[Flags]
public enum EntityFlags : Int32
{
// These stop all other options from showing properly, comment out to allow others to show
NONE = 0,
ALL = ~0,
dynamicPosition = 1 << 0,
hasRotation = 1 << 1,
playerConrolled = 1 << 2,
aiControlled = 1 << 3,
isHumanoid = 1 << 4,
canJump = 1 << 5,
canAttack = 1 << 6,
canBeAttacked = 1 << 7,
canMelee = 1 << 8,
invulnerable = 1 << 9,
isBuilding = 1 << 10,
hasPhysicsBody = 1 << 11,
collectable = 1 << 12,
resource = 1 << 13,
// multiple flags with the same value don't show (I don't personally care about these, just testing for repro)
interactable = 1 << 14,
interactable1 = 1 << 14,
interactable2 = 1 << 14,
interactable3 = 1 << 14,
// These are grouped into "MIXED", I DO care about these
PRESET_RESOURCE_DROP = collectable | resource,
PRESET_HUMANOID = isHumanoid | dynamicPosition | canBeAttacked | EntityFlags.hasPhysicsBody,
PRESET_HUMANOID_NPC = PRESET_HUMANOID | aiControlled,
PRESET_HUMANOID_NPC_SOLDIER = PRESET_HUMANOID_NPC | canAttack,
PRESET_PLAYER = PRESET_HUMANOID | canJump | playerConrolled | hasPhysicsBody,
PRESET_BUILDING = isBuilding,
PRESET_NAV = aiControlled | dynamicPosition,
PRESET_CAN_RAID = isHumanoid | dynamicPosition | canBeAttacked | canAttack | aiControlled,
}
}
As a workaround, you could use an EnumField or a custom PopupField instead, assuming you want only one of the options to be selected at all time since you have presets. With that, you could probably do the conversion manually on the value changed callback.
Unfortunately I do need to set multiple flags. Any other ideas?
Edit: I guess, to your point, I might be able to make do with single options + creating presets in the code for now. That removes some of the value in having them in the inspector in the first place, but it might be workable for now. Thanks.
Yes that’s what I mean, since you probably use one preset at the time, you could have an enum with the preset names, and use that to apply the different attributes afterwards.
You could use an IMGUIContainer like you mentioned originally. EditorGUILayout.EnumFlagsField seems to have better support at the moment for these types of flag enum.
Here’s what it would look like with a serialized property.
var property = serializedObject.FindProperty("flags");
var imguiContainer = new IMGUIContainer(() =>
{
EditorGUI.BeginChangeCheck();
property.intValue = (int)((EntityFlags)EditorGUILayout.EnumFlagsField("Label", (EntityFlags)property.intValue));
if (EditorGUI.EndChangeCheck())
serializedObject.ApplyModifiedProperties();
});
root.Add(imguiContainer);
Or the remaining option would be a completely custom control for this.
To clarify, the “presets” are not meant for the editor workflow, they have another purpose. I do not want to be limited to presets, but I can use them in that capacity for now as a workaround.
I’d still be interested in the imgui option, but the problem (I think) is that the “property” variable will not be in scope when the callback is called.
Maybe that’s not actually an issue and I’m mis-understanding, or maybe I can work around that further by storing the property path on the IMGUIContainer element somehow, assuming I have access to the IMGUIContainer instance from within the callback(?). I’ll give it a shot anyway.
There will be a closure allocation to ensure the property variable still exist in the callback. If that doesn’t really work in your case (say this is done in a for loop for multiple properties), you could create a wrapper class over IMGUIContainer that keeps a reference to the property in a variable.
Ok I’ve got the field drawing properly with an imgui wrapper, but there’s one more piece of the puzzle: How can I raise an event on the element when the value is changed? I have a SerializedPropertyChangeEvent registered on the IMGUIContainer element, but it’s not being triggered on its own and I haven’t been able to find a way to do it manually. So far I have this but ain’t working.
public class ImguiPropWrapper
{
public SerializedObject serializedObject;
public SerializedProperty prop;
public IMGUIContainer element;
public void DrawProp()
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(prop);
if (EditorGUI.EndChangeCheck())
{
serializedObject.ApplyModifiedProperties();
element.SendEvent(UnityEditor.UIElements.SerializedPropertyChangeEvent.GetPooled(prop));
}
}
}