范围滑块的逻辑:min(min, max) 有效但 max(min, max) 无效
Logic for range sliders: min(min, max) works but max(min, max) doesn't
我有以下代码:
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,因为它不显示值:
你的问题是顺序:
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;
}
}
评论中已经提到的另一种选择是使用局部变量来存储钳位值,但等待赋值,直到所有值都完成钳位,例如
private void OnValidate()
{
var newMin = Mathf.Min(RangeMin, RangeMax);
var newMax = Mathf.Max(RangeMin, RangeMax);
RangeMin = newMin;
RangeMax = newMax;
}
或者回到
Note, please don't suggest using EditorGUI.MinMaxSlider as it doesn't show the values.
我猜你可以简单地让它像 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 中免费提供,并且为编辑器提供了更多不错的增强功能(ReorderableList
、Button
、 ShowIf
, 等等);)
我有以下代码:
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,因为它不显示值:
你的问题是顺序:
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;
}
}
评论中已经提到的另一种选择是使用局部变量来存储钳位值,但等待赋值,直到所有值都完成钳位,例如
private void OnValidate()
{
var newMin = Mathf.Min(RangeMin, RangeMax);
var newMax = Mathf.Max(RangeMin, RangeMax);
RangeMin = newMin;
RangeMax = newMax;
}
或者回到
Note, please don't suggest using EditorGUI.MinMaxSlider as it doesn't show the values.
我猜你可以简单地让它像 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 中免费提供,并且为编辑器提供了更多不错的增强功能(ReorderableList
、Button
、 ShowIf
, 等等);)