TylerTemp / SaintsField

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

IMGUI 下的 [Infobox] 跟随[枚举]属性时绘制属性高度错误 #85

Open Lx34r opened 3 days ago

Lx34r commented 3 days ago

IMGUI 下,[Infobox] 无论是否是 below,绘制属性后均会有一段与 [Infobox] 同样高度的额外空白行,且不会正常绘制 [Header]

[IMGUI 高度错误,没有绘制 Header] image

[UIToolkit 高度正确] image

[Code] image

TylerTemp commented 2 days ago

方便分享以下你的 Unity 版本吗?

首先,我的测试结果:2019.4上,正常(2019本身不支持UI Toolkit)

image

2022.2上,手动ban掉UIToolkit后,IMGUI出现吃掉 Header 的情况,但没有出现空间间隔情况(截图可以看到对象是由IMGUI绘制的无误)。

image

实际上,Unity 在 2022.1 起,修改了内部 PropertyField fallback drawer 的绘制逻辑,导致一直 fallback 时会将 decorator drawer 反复重绘。因此 SaintsField 的 SaintsProperyDrawer 有以下代码处理 decorator 重绘问题:

#if UNITY_2022_1_OR_NEWER
Type dec = fieldInfo.GetCustomAttributes<PropertyAttribute>(true)
    .Select(propertyAttribute =>
    {
        // Debug.Log(propertyAttribute.GetType());
        Type results = _propertyAttributeToDecoratorDrawers.TryGetValue(propertyAttribute.GetType(),
            out IReadOnlyList<Type> eachDrawers)
            ? eachDrawers[0]
            : null;

        // Debug.Log($"Found {results}");

        return results;
    })
    .FirstOrDefault(each => each?.IsSubclassOf(typeof(DecoratorDrawer)) ?? false);

if (dec != null && ImGuiRemoveDecDraw(position, property, label))
{
    return;
}
#endif

我先查一下 IMGUI 吃 Header 的问题。空白间隔的问题,需要你提供以下Unity版本号(请给完整的版本号带f,比如2022.2.0f1,如果是中国特供版,会以c1结尾: 2022.2.0f1c1


Update: 狗了…2022+上,UIToolkit需要用反射手动移除 decorator 否则会多次绘制;IMGUI则不应移除否则不会绘制…我他妈…

算了,也不是第一次被 Unity 坑了… 吃 decorator 的问题我想想办法…

Lx34r commented 2 days ago

感谢在台风天假期的耐心回复 :)

EditorVersion:2022.3.46f1 (8e9b8558c41a)

哈哈哈,能深深感受到你的无奈,毕竟这世界就是个巨大的草台班子。Unity团队不仅解决 bug report 慢,而且拖半年多 fixed 后告知 bugfixes are backported,尤其是引入 Job 后处理 Mesh 的各种问题,说的就是破破烂烂的2022+。

Lx34r commented 2 days ago

实际上,Unity 在 2022.1 起,修改了内部 PropertyField fallback drawer 的绘制逻辑,导致一直 fallback 时会将 decorator drawer 反复重绘。因此 SaintsField 的 SaintsProperyDrawer 有以下代码处理 decorator 重绘问题:

#if UNITY_2022_1_OR_NEWER
Type dec = fieldInfo.GetCustomAttributes<PropertyAttribute>(true)
    .Select(propertyAttribute =>
    {
        // Debug.Log(propertyAttribute.GetType());
        Type results = _propertyAttributeToDecoratorDrawers.TryGetValue(propertyAttribute.GetType(),
            out IReadOnlyList<Type> eachDrawers)
            ? eachDrawers[0]
            : null;

        // Debug.Log($"Found {results}");

        return results;
    })
    .FirstOrDefault(each => each?.IsSubclassOf(typeof(DecoratorDrawer)) ?? false);

if (dec != null && ImGuiRemoveDecDraw(position, property, label))
{
    return;
}
#endif

关于 Infobox 这类装饰器属性的绘制,我看到#71是使用 IPlayaAttributeAbsRenderer 里进行额外处理的,为什么不考虑包装一个继承自 DecoratorDrawer 的类(比如 SaintsDecoratorDrawer)替代 SaintsPropertyDrawer 来绘制 Infobox 避免将其反复应用到数组/列表的内容上,是有什么考量么?

TylerTemp commented 11 hours ago

继承自 DecoratorDrawer 的类(比如 SaintsDecoratorDrawer)替代 SaintsPropertyDrawer 来绘制 Infobox 避免将其反复应用到数组/列表的内容上

拆分几个问题:

为什么不继承自 DecoratorDrawer

DecoratorDrawer 类,无法获取到 被装饰对象 的任何信息,导致所有 callback 都无法进行。这也是为什么 NaughtyAttribute无法支持 Conditional InfoBox 了,动态 InfoBox Content 也没法支持了。

这也是为什么开始有一个 SepTitle(基于 DecoratorDrawer),后面又做了一个 Separator(基于 PropertyDrawer),即使两者的功能非常相似。

Unity的PropertyDrawer带有 fieldInfo,而DecoratorDrawer并没有这个属性

避免将其反复应用到数组/列表的内容上的考量

实际上:

这两者的考量,同样是因为 callback 的原因。 InfoBox 基于单个元素,因此可以基于元素值给出反馈:

[InfoBox("$" + nameof(ElementValidate), EMessageType.Error)] public string[] myValues;
// 这个特性我好像在文档中没好好写…
private string ElementValidate(string v, int index) => string.IsNullOrEmpty(v)? $"不允许空字符串,index位置: {index}": null;

image

PlayaInfoBox 的 callback,针对的是整个列表, callback 中获取的也是整个信息。

[PlayaInfoBox("$" + nameof(ElementValidateList), EMessageType.Error)] public string[] myArray;
private string ElementValidateList(string[] v) => v.Length < 5? $"数组长度必须>=5,当前长度: {v.Length}": null;

image

因此两者的应用场景虽然大部分相同,但对 array/list 时则完全不同了。

Lx34r commented 10 hours ago

非常感谢耐心答疑,确实如此,我之前没有考虑到 callback 要拿列表的信息。

TylerTemp commented 8 hours ago

你好,方便的话请尝试以下 dev 分支,应该有解决这个问题。具体版本我…明儿再发 🥹

Lx34r commented 8 hours ago

你好,方便的话请尝试以下 dev 分支,应该有解决这个问题。具体版本我…明儿再发 🥹

ok,等我拉一下测试看看。


更新:https://github.com/TylerTemp/SaintsField/commit/734818f2311a51206600608df841f67822ab2f1e 2022.3.46f1 (8e9b8558c41a) IMGUI 下已解决上述问题,等发版可以 close 这个 issue。