#c# #unity3d #range #clamp
#c# #unity3d #диапазон #зажим
Вопрос:
У меня есть следующий код:
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. Я думаю, вам нужно будет отслеживать предыдущие min и max, затем вы можете проверить, какой из них изменился, и выполнить проверку только для этого. В противном случае, когда max равно 1, а min равно 2, вы не знаете, был ли min перемещен выше max, и он должен быть установлен на 1 или max, как перемещенный ниже min, и он должен быть установлен на 2.
2. На самом деле имеет смысл!
3. Ваша первая строка в OnValidate перезаписывает RangeMin , поэтому во второй строке будет использоваться неправильное значение. Сначала присваивайте локальные переменные и только в конце присваивайте полям локальные переменные.
Ответ №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;
}
}
Другой альтернативой, также уже упомянутой в комментариях, является использование локальных переменных для хранения закрепленных значений, но подождите с присвоением, пока все значения не будут завершены, например,
private void OnValidate()
{
var newMin = Mathf.Min(RangeMin, RangeMax);
var newMax = Mathf.Max(RangeMin, RangeMax);
RangeMin = newMin;
RangeMax = newMax;
}
В качестве альтернативы вернемся к
Примечание, пожалуйста, не предлагайте использовать EditorGUI.MinMaxSlider, поскольку он не отображает значения.
Я думаю, вы могли бы просто сделать это так, как это уже было сделано непослушными атрибутами
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 доступен в хранилище ресурсов Unity бесплатно и содержит гораздо больше приятных улучшений для редактора ( ReorderableList
, Button
, ShowIf
, и т.д.) 😉