dbrizov / NaughtyAttributes

Attribute Extensions for Unity
MIT License
4.54k stars 464 forks source link

[Issue] Dropdowns don't retain values in structs #255

Open JonLevin25 opened 3 years ago

JonLevin25 commented 3 years ago

Hi, first off thanks for all the hard work, This is one of the first things I add to a new project to save tons of time!

As the title states - when selecting a value from a dropdown in a struct (classes work fine) - the value is lost and default value is returned. To repro- put the test component below on a GO, and try selecting a value on both the class and struct.

    public class TestNaughtyDropdownBug : MonoBehaviour
    {
        [SerializeField] private TestClass testClass;
        [SerializeField] private TestStruct testStruct;
    }

    public static class TestStatic
    {
        public static string[] TestDropdown = {
            "Default",
            "Value A",
            "Value B",
        };
    }

    [Serializable]
    public class TestClass
    {
        [SerializeField, Dropdown(nameof(drop))] private string test;
        public string[] drop => TestStatic.TestDropdown;
    }

    [Serializable]
    public struct TestStruct
    {
        [SerializeField, Dropdown(nameof(drop))] private string test;
        public string[] drop => TestStatic.TestDropdown;
    }
dbrizov commented 3 years ago

I know about this issue. You can read more about it here. I don't know how to fix it though :( The workaround is to use a class

JonLevin25 commented 3 years ago

I see. thanks for the swift reply! I did change to a class, but think I found another bug - I think assets aren't marked dirty when updating a dropdown. Is this a known issue? Don't have time to test now but can open a ticket tomorrow

dbrizov commented 3 years ago

Yeah, report a bug if you think something is not right. I've had issues regarding EditorUtility.SetDirty in the past, I might've missed something.

arimger commented 3 years ago

If I can share a small tip about updating structs through reflection, it should work properly - you can iterate backward in the objects hierarchy looking for the first non-struct parent and assigning values in the loop, remember that you have to handle manually arrays/lists elements.

axelorca commented 1 year ago

Another thing to add in here; [Dropdown] does work with classes, but it can only recognize public variables, not private ones. Sounds logical since this is using reflection.

Just leavin this here so ppl who encountered the same issue know 👍

TylerTemp commented 9 months ago

OK I know this is old.

The trick is to use pure PropertyDrawer, then the boxed struct will be a plain field, and use reflection for valuedType. As this project hasn't be maintained for a long time, here is a solution you can use together with NaughtyAttributes which works for the script provided.

public class TestNaughtyDropdownBug : MonoBehaviour
{
    [SerializeField] private TestClass testClass;
    [SerializeField] private TestStruct testStruct;
}

public static class TestStatic
{
    public static DropdownList<string> TestDropdown = new DropdownList<string>{
        { "Default", "Default" },
        { "Value A", "Value A" },
        { "Value B", "Value B" },
    };
}

[Serializable]
public class TestClass
{
    [SerializeField, Dropdown(nameof(drop))] private string test;
    public DropdownList<string> drop => TestStatic.TestDropdown;
}

[Serializable]
public struct TestStruct
{
    [SerializeField, Dropdown(nameof(drop))] private string test;
    public DropdownList<string> drop => TestStatic.TestDropdown;
}

https://github.com/dbrizov/NaughtyAttributes/assets/6391063/49a74137-1f0f-48d2-a52d-4516ead67ad1