TylerTemp / SaintsField

A Unity Inspector extension tool focusing on script fields inspector enhancement
MIT License
148 stars 9 forks source link

Examples work, once in a new script, doesn't work #51

Closed KamilTheDev closed 1 month ago

KamilTheDev commented 1 month ago

All the examples work fine, but once I try in my own script, Layout Group doesn't work. Unity 2022.3.27f1

Screenshot 2024-07-14 194814

LayoutGroupExample.cs:

using SaintsField.Playa;

namespace SaintsField.Samples.Scripts.SaintsEditor
{
    public class LayoutGroupExample : SaintsMonoBehavior
    {
        [SepTitle("Break By End", EColor.Gray)]
        // end group
        public string beforeGroup;

        [LayoutGroup("Group", ELayout.Background | ELayout.TitleOut)]
        public string group1;
        public string group2;  // starts from this will be automatically grouped into "Group"
        public string group3;

        [LayoutEnd("Group")]  // this will end the "Group"
        public string afterGroup;

        [SepTitle("Break By Group", EColor.Gray)]

        // break group
        public string breakBefore;

        [LayoutGroup("break", ELayout.Background | ELayout.TitleOut)]
        public string breakGroup1;
        public string breakGroup2;

        // this group will stop the grouping of "break"
        [LayoutGroup("breakIn", ELayout.Background | ELayout.TitleOut)]
        public string breakIn1;
        public string breakIn2;

        [LayoutGroup("break")]  // this will be grouped into "break", and also end the "breakIn" group
        public string breakGroup3;
        public string breakGroup4;

        [LayoutEnd("break")]  // end, it will not be grouped
        public string breakAfter;

        [SepTitle("Break By Last Group", EColor.Gray)]
        public string beforeGroupLast;

        [LayoutGroup("GroupLast")]
        public string groupLast1;
        public string groupLast2;
        public string groupLast3;
        [Layout("GroupLast", ELayout.Background | ELayout.TitleOut)]  // close this group, but be included
        public string groupLast4;

        public string afterGroupLast;
    }
}

TestMonoScript.cs (copied from above example):

using SaintsField;
using SaintsField.Playa;
using UnityEngine;

public class TestMonoScript : MonoBehaviour
{
    [SepTitle("Break By End", EColor.Gray)]
    // end group
    public string beforeGroup;

    [LayoutGroup("Group", ELayout.Background | ELayout.TitleOut)]
    public string group1;
    public string group2;  // starts from this will be automatically grouped into "Group"
    public string group3;

    [LayoutEnd("Group")]  // this will end the "Group"
    public string afterGroup;

    [SepTitle("Break By Group", EColor.Gray)]

    // break group
    public string breakBefore;

    [LayoutGroup("break", ELayout.Background | ELayout.TitleOut)]
    public string breakGroup1;
    public string breakGroup2;

    // this group will stop the grouping of "break"
    [LayoutGroup("breakIn", ELayout.Background | ELayout.TitleOut)]
    public string breakIn1;
    public string breakIn2;

    [LayoutGroup("break")]  // this will be grouped into "break", and also end the "breakIn" group
    public string breakGroup3;
    public string breakGroup4;

    [LayoutEnd("break")]  // end, it will not be grouped
    public string breakAfter;

    [SepTitle("Break By Last Group", EColor.Gray)]
    public string beforeGroupLast;

    [LayoutGroup("GroupLast")]
    public string groupLast1;
    public string groupLast2;
    public string groupLast3;
    [Layout("GroupLast", ELayout.Background | ELayout.TitleOut)]  // close this group, but be included
    public string groupLast4;

    public string afterGroupLast;
}
TylerTemp commented 1 month ago

Hi

Layout need SaintsEditor enabled, which is not enabled by default to avoid conflict.

The most easy way is Window - Saints - Apply SaintsEditor. For more information about config please check the related document

KamilTheDev commented 1 month ago

I have indeed already pressed Apply SaintsEditor before creating the issue. image

In my post, you can see adding the pre-provided example script works fine, but copy & pasting it into my own script has the Layout Element not working.

TylerTemp commented 1 month ago

OK, understood.

In this case, I'm afraid that your project has a script that also highjack the UnityEditor.Editor, which has a higher priority than other 3rd-party plugins.

A very easy way to verify that:

First, under any Editor folder, create the following script:

using SaintsField.Editor;

[CanEditMultipleObjects]
[CustomEditor(typeof(TestMonoScript), true)]
public class ApplySaintsEditor : SaintsEditor
{
}

If it works now, that means there are scripts that override CustomEditor which is way too high priority. If it does not work, then sorry buddy there is indeed some problem with your project that I can not think of.

If this script works, there is a (very annoying) way to find out which hijake it:

foreach (Assembly asb in AppDomain.CurrentDomain.GetAssemblies())
{
    Type[] allTypes = asb.GetTypes();
    List<Type> allEditors = allTypes
        .Where(type => type.IsSubclassOf(typeof(UnityEditor.Editor)))
        .ToList();

    foreach (Type eachEditorType in allEditors)
    {
        foreach (CustomEditor customEditor in eachEditorType.GetCustomAttributes<CustomEditor>(true))
        {
            var v = typeof(CustomEditor)
                .GetField("m_InspectedType",
                    BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic |
                    BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.FlattenHierarchy)
                ?.GetValue(customEditor);
            Debug.Log($"Found editor: {eachEditorType} -> {customEditor}: {v}");
        }
    }
}

It'll print all the editor for it's corresponding types. Carefully check overrides like UnityEngine.Object, MonoBehavior, Component, which should be the one that blocked other inspectors.


Update:

Another way, not garrented, is Window - UI Toolkit - Debugger. by debugging the target filed, most 3rd editor will leave some evidence by the name of the containing element(s), or the class name of them.


Update 2:

Another way, as most script only inject UnityEngine.Object, you can try put this under an Editor folder:

using UnityEditor;

[CanEditMultipleObjects]
[CustomEditor(typeof(UnityEngine.MonoBehaviour), true)]
public class ApplySaintsMonoBehaviorEditor : SaintsEditor
{
}

[CanEditMultipleObjects]
[CustomEditor(typeof(UnityEngine.ScriptableObject), true)]
public class ApplySaintsScriptableObjectEditor : SaintsEditor
{
}
KamilTheDev commented 1 month ago

Thank you, the first and last script did restore the correct functionality.

From the debug script, I found this: Found editor: ExternalPropertyAttributes.Editor.ExternalCustomInspector -> UnityEditor.CustomEditor: UnityEngine.Object

This is from Quibli, a toon shader asset. I imported Quibli into a new project and confirmed that is the cause of the issue.

I need to keep Quibli and its editor tools in my project, as I'm using it. Is it okay to just continuing to use the last script you provided to avoid having to make modifications to Quibli asset?

TylerTemp commented 1 month ago

Yes. because most of the time, we only need to deal with MonoBehavior and ScriptableObject. The Component type only comes into handy when you inherent, for example, Button, SpriteRenderer etc, which is usually not the case. MonoBehavior & ScriptableObject should cover most of the game development. And you can always add CustomEditor manually when the target type is not included.