【问题标题】:Logic for range sliders: min(min, max) works but max(min, max) doesn't范围滑块的逻辑: min(min, max) 有效,但 max(min, max) 无效
【发布时间】:2021-02-15 14:05:32
【问题描述】:

我有以下代码:

public class Test : UnityEngine.MonoBehaviour
{
    [Range(0.0f, 1.0f)] // draws a slider restricted to 0.0 <-> 1.0 range in UI
    public float RangeMin = 0.0f;

    [Range(0.0f, 1.0f)] // draws a slider restricted to 0.0 <-> 1.0 range in UI
    public float RangeMax = 1.0f;

    private void OnValidate() // called at every update in UI to validate/coerce
    {
        RangeMin = math.min(RangeMin, RangeMax);
        RangeMax = math.max(RangeMin, RangeMax);
    }
}

目前,它执行以下操作:

更改最小值不会影响最大值:(期望的行为)

更改最大值确实会影响最小值:(不需要的行为)

那段非常简单的代码适用于最小滑块,但不适用于最大滑块。

注意,请不要建议使用EditorGUI.MinMaxSlider,因为它不显示值:

【问题讨论】:

  • 我认为您必须跟踪之前的最小值和最大值,然后您可以检查哪个更改了,并且只对那个进行验证。否则,当最大值为 1 且最小值为 2 时,您不知道最小值是否移动到最大值之上并且应该设置为 1 或最大值移动到最小值之下并且应该设置为 2。
  • 实际上非常有意义!
  • 您在 OnValidate 中的第一行会覆盖 RangeMin,因此第二行将使用错误的值。先赋值给局部变量,最后才将局部变量赋值给字段。

标签: c# unity3d range clamp


【解决方案1】:

你的问题是顺序:

private void OnValidate() // called at every update in UI to validate/coerce
{
    RangeMin = math.min(RangeMin, RangeMax);
    RangeMax = math.max(RangeMin, RangeMax);
}

它对RangeMin“有效”,因为您在更改它的那一刻立即检查并限制它。

但是,在更改RangeMax 时,您已经立即影响RangeMin之前它有机会限制RangeMax

按照建议,您应该检查您当前正在更改的两个值中的哪一个,例如喜欢

[HideInInspector] private float lastMin;
[HideInInspector] private float lastMax;

private void OnValidate() 
{
    if(!Mathf.Approximately(lastMin, RangeMin))
    {
        RangeMin = Mathf.Min(RangeMin, RangeMax);
        lastMin = RangeMin;
    }

    if(!Mathf.Approximately(lastMax, RangeMax))
    {
        RangeMax = Mathf.Max(RangeMin, RangeMax);
        lastMax = RangeMax;
    }
}

cmets 中已经提到的另一种替代方法是使用局部变量来存储钳位值,但等待赋值,直到所有值都完成钳位,例如

private void OnValidate() 
{     
    var newMin = Mathf.Min(RangeMin, RangeMax);        
    var newMax = Mathf.Max(RangeMin, RangeMax);

    RangeMin = newMin;
    RangeMax = newMax;
}

或者回到

注意,请不要建议使用 EditorGUI.MinMaxSlider,因为它不显示值。

我想你可以简单地让它像 Naughty Attributes 已经完成的那样

namespace NaughtyAttributes
{
    [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
    public class MinMaxSliderAttribute : DrawerAttribute
    {
        public float MinValue { get; private set; }
        public float MaxValue { get; private set; }

        public MinMaxSliderAttribute(float minValue, float maxValue)
        {
            MinValue = minValue;
            MaxValue = maxValue;
        }
    }
}

还有抽屉

namespace NaughtyAttributes.Editor
{
    [CustomPropertyDrawer(typeof(MinMaxSliderAttribute))]
    public class MinMaxSliderPropertyDrawer : PropertyDrawerBase
    {
        protected override float GetPropertyHeight_Internal(SerializedProperty property, GUIContent label)
        {
            return (property.propertyType == SerializedPropertyType.Vector2)
                ? GetPropertyHeight(property)
                : GetPropertyHeight(property) + GetHelpBoxHeight();
        }

        protected override void OnGUI_Internal(Rect rect, SerializedProperty property, GUIContent label)
        {
            EditorGUI.BeginProperty(rect, label, property);

            MinMaxSliderAttribute minMaxSliderAttribute = (MinMaxSliderAttribute)attribute;

            if (property.propertyType == SerializedPropertyType.Vector2)
            {
                EditorGUI.BeginProperty(rect, label, property);

                float indentLength = NaughtyEditorGUI.GetIndentLength(rect);
                float labelWidth = EditorGUIUtility.labelWidth + NaughtyEditorGUI.HorizontalSpacing;
                float floatFieldWidth = EditorGUIUtility.fieldWidth;
                float sliderWidth = rect.width - labelWidth - 2.0f * floatFieldWidth;
                float sliderPadding = 5.0f;

                Rect labelRect = new Rect(
                    rect.x,
                    rect.y,
                    labelWidth,
                    rect.height);

                Rect sliderRect = new Rect(
                    rect.x + labelWidth + floatFieldWidth + sliderPadding - indentLength,
                    rect.y,
                    sliderWidth - 2.0f * sliderPadding + indentLength,
                    rect.height);

                Rect minFloatFieldRect = new Rect(
                    rect.x + labelWidth - indentLength,
                    rect.y,
                    floatFieldWidth + indentLength,
                    rect.height);

                Rect maxFloatFieldRect = new Rect(
                    rect.x + labelWidth + floatFieldWidth + sliderWidth - indentLength,
                    rect.y,
                    floatFieldWidth + indentLength,
                    rect.height);

                // Draw the label
                EditorGUI.LabelField(labelRect, label.text);

                // Draw the slider
                EditorGUI.BeginChangeCheck();

                Vector2 sliderValue = property.vector2Value;
                EditorGUI.MinMaxSlider(sliderRect, ref sliderValue.x, ref sliderValue.y, minMaxSliderAttribute.MinValue, minMaxSliderAttribute.MaxValue);

                sliderValue.x = EditorGUI.FloatField(minFloatFieldRect, sliderValue.x);
                sliderValue.x = Mathf.Clamp(sliderValue.x, minMaxSliderAttribute.MinValue, Mathf.Min(minMaxSliderAttribute.MaxValue, sliderValue.y));

                sliderValue.y = EditorGUI.FloatField(maxFloatFieldRect, sliderValue.y);
                sliderValue.y = Mathf.Clamp(sliderValue.y, Mathf.Max(minMaxSliderAttribute.MinValue, sliderValue.x), minMaxSliderAttribute.MaxValue);

                if (EditorGUI.EndChangeCheck())
                {
                    property.vector2Value = sliderValue;
                }

                EditorGUI.EndProperty();
            }
            else
            {
                string message = minMaxSliderAttribute.GetType().Name + " can be used only on Vector2 fields";
                DrawDefaultPropertyAndHelpBox(rect, property, message, MessageType.Warning);
            }

            EditorGUI.EndProperty();
        }
    }
}

到底是什么样子的

[SerializeField] [MinMaxSlider(0f; 100f)] private float _minMaxSlider;

现在在复制该代码之前,请注意 Naughty Attributes Package 可在 Unity Asset Store 中免费获得,并且为编辑器提供了许多更好的增强功能(ReorderableListButtonShowIf 等);)

【讨论】:

    猜你喜欢
    • 2016-11-17
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-07
    • 2012-08-26
    • 1970-01-01
    • 2017-06-10
    相关资源
    最近更新 更多