Custom extension support for EditorExtension #61
Replies: 3 comments 6 replies
-
Originally I didn't exactly know how other packages handled groups like this and I am trying to avoid having to rewrite the entire inspector and completely change the workflow of creating editor windows so I came up with this workaround which kinda forces you to region your variables rather then placing group attributes all over your script, and also centralizes the options of the group (like labels, drawInBox) in one place. For example here is a SO script of mine: [CreateAssetMenu(fileName = "PlayerControllerOptions", menuName = "Player Controller Options")]
public class PlayerControllerOptions : ScriptableObject
{
[FoldoutGroup("Physics", nameof(GravityScale), nameof(TerminalVelocity))]
[SerializeField] private Void physicsFoldout;
[field: SerializeField, HideProperty] public float GravityScale { get; private set; }
[field: SerializeField, HideProperty] public float TerminalVelocity { get; private set; }
[FoldoutGroup("Movement", nameof(DefaultPlayerSpeed), nameof(SprintSpeedMultiplier), nameof(JumpForce), nameof(MaxStepHeight), nameof(MaxSlopeAngle))]
[SerializeField] private Void movementFoldout;
[field: SerializeField, HideProperty] public GameplayValue DefaultPlayerSpeed { get; private set; }
[field: SerializeField, HideProperty] public GameplayValue SprintSpeedMultiplier { get; private set; }
[field: SerializeField, HideProperty] public GameplayValue JumpForce { get; private set; }
[field: SerializeField, HideProperty] public float MaxStepHeight { get; private set; }
[field: SerializeField, HideProperty, Clamp(0f, 180f)] public float MaxSlopeAngle { get; private set; }
[FoldoutGroup("GroundCheck", nameof(GroundCheckRadius), nameof(GroundCheckOffset), nameof(GroundMask))]
[SerializeField] private Void groundCheckFoldout;
[field: SerializeField, HideProperty] public float GroundCheckRadius { get; private set; }
[field: SerializeField, HideProperty] public Vector3 GroundCheckOffset { get; private set; }
[field: SerializeField, HideProperty] public LayerMask GroundMask { get; private set; }
[field: SerializeField, ToggleGroup("Wall Climbing", nameof(DefaultClimbSpeed), nameof(WallClimbRotationSpeed), nameof(WallJumpForce), nameof(WallCheckDistance), nameof(WallCheckOffset), nameof(ClimbableTag))]
public bool CanWallClimb { get; private set; }
[field: SerializeField, HideProperty] public GameplayValue DefaultClimbSpeed { get; private set; }
[field: SerializeField, HideProperty] public float WallClimbRotationSpeed { get; private set; }
[field: SerializeField, HideProperty] public float WallJumpForce { get; private set; }
[field: SerializeField, HideProperty] public float WallCheckDistance { get; private set; }
[field: SerializeField, HideProperty] public Vector3 WallCheckOffset { get; private set; }
[field: SerializeField, HideProperty, TagDropdown] public string ClimbableTag { get; private set; }
[field: SerializeField, ToggleGroup("Flying", nameof(FlyingMode), nameof(DefaultFlightSpeed), nameof(FlyingForce), nameof(GlideVerticalSpeed), nameof(GlideRotationSpeed))]
public bool CanFly { get; private set; }
[field: SerializeField, HideProperty] public FlyMode FlyingMode { get; private set; }
[field: SerializeField, HideProperty] public GameplayValue DefaultFlightSpeed { get; private set; }
[field: SerializeField, HideProperty] public float FlyingForce { get; private set; }
[field: SerializeField, HideProperty, ShowField(nameof(FlyingMode), FlyMode.Glide)]
public float GlideVerticalSpeed { get; private set; }
[field: SerializeField, HideProperty, ShowField(nameof(FlyingMode), FlyMode.Glide)]
public float GlideRotationSpeed { get; private set; }
[field: SerializeField, ToggleGroup("Swimming", nameof(DefaultSwimSpeed), nameof(SwimForce), nameof(SwimRotationSpeed), nameof(WaterCheckRadius), nameof(UnderwaterCheckRadius), nameof(WaterCheckOffset), nameof(UnderwaterCheckOffset), nameof(CanSink))]
public bool CanSwim { get; private set; }
[field: SerializeField, HideProperty] public GameplayValue DefaultSwimSpeed { get; private set; }
[field: SerializeField, HideProperty] public float SwimForce { get; private set; }
[field: SerializeField, HideProperty] public float SwimRotationSpeed { get; private set; }
[field: SerializeField, HideProperty] public float WaterCheckRadius { get; private set; }
[field: SerializeField, HideProperty] public float UnderwaterCheckRadius { get; private set; }
[field: SerializeField, HideProperty] public Vector3 WaterCheckOffset { get; private set; }
[field: SerializeField, HideProperty] public Vector3 UnderwaterCheckOffset { get; private set; }
[field: SerializeField, HideProperty] public bool CanSink { get; private set; }
}And I just got used to it, no one complained it was all good and dandy. Right now I think is too late to change it without breaking everything (unless people don't get annoyed), but if I were to change it I think I would still use the property drawer. I would have the current group attributes act the same as they do right now, but instead of referencing the properties in the group attribute I would add a group ID parameter and a new attribute called GroupProperty or something like that, which takes in a group ID and also hides the original field so you don't have to spam the HideProperty attribute. Then the root group will just look for all properties with the GroupProperty Attribute filter them by the Id inputted and draw those properties inside the group. On the root you can also have the option to draw the attached property if you want to start with a variable and don't want to use a holder. Your idea for the EditorExtension is good, but I am trying to avoid having to change the workflow of creating editors. If you want to create your custom editor that overrides all object editors there is nothing wrong with just commenting or removing the CustomEditor line and adding it to yours while still inheriting. |
Beta Was this translation helpful? Give feedback.
-
|
Hi, thank you for explaining the design process behind For example:
So overall, I fully understand why changing the current approach is not desirable. That said, the main point I wanted to share is slightly different: I believe adding a lightweight interface-based extension point to
|
Beta Was this translation helpful? Give feedback.
-
|
Additionally, I recommend using Scripting Define Symbols as a switch for the For example: #if !EDITORATTRIBUTES_ATTRIBUTES_DISABLED
[CanEditMultipleObjects, CustomEditor(typeof(Object), true)]
#endif
public class EditorExtension : UnityEditor.Editor
{
...
}This ensures that:
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hello, I’ve been using EditorAttributes for about two years now, and the experience has always been excellent. Recently, however, I’ve been thinking about a few things:
The way EditorAttributes provides Group
[https://editorattributesdocs.readthedocs.io/en/latest/Attributes/GroupingAttributes/foldoutgroup.html]
seems somewhat verbose—it requires declaring an “irrelevant” field and modifying the attributes of every affected field in order to achieve grouping. Other editor tools only require
[Group("A/B")], which feels much more concise:I noticed that this is because most of EditorAttributes’ functionality is implemented in
PropertyDrawerrather thanUnityEditor.Editor. SincePropertyDrawerlacks a “global view” and cannot be aware of other fields during drawing, Group has to work in this somewhat cumbersome way.Based on this, I’d like to ask:
Was this a trade-off made for architectural consistency? If I wanted to implement a
[Group("A/B")]-style path syntax, what non-intrusive extension approach would you most recommend?I noticed that you already provide inheritance support in
EditorExtension, but if I inherit fromEditorExtensionand also use[CustomEditor(typeof(Object), true)], it will conflict with the parent class declaration, and Unity’s selection becomes non-deterministic. I also don’t want to use[CustomEditor(typeof(MonoBehaviour), true)], nor do I want to create a base class for all scripts and pointCustomEditorto it, as that would bloat the project.With that in mind, I came up with a potentially viable approach:
IInspectorExtension)EditorExtensionquery and invoke registered extensions inCreateInspectorGUIBy extending only two files and adding just a few lines of code to
EditorExtension, developers could then customize anyUnityEditor.Editorfunctionality they want in the future, add it intoEditorExtensionin a non-intrusive way, and automatically benefit from[CustomEditor(typeof(Object), true)]as well as all the features provided by EditorAttributes. Rough example code below:What do you think about this approach? The above is just a question and a suggestion, and my understanding may be somewhat one-sided—please excuse any shortcomings.
Beta Was this translation helpful? Give feedback.
All reactions