Перейти к основному содержимому

Туториал 6. Custom Binder

Разбор сэмпла Path 6. Custom Binder — биндер для компонента, о котором StarterKit не знает.

Предполагается знание: TodoList.


Что строим

Hero
[████████████░░░░░░░░] 70%
[ Hit ] [ Heal ]

HealthBar — обычный UI-компонент проекта: Image с заливкой, подпись и градиент цвета. Задача — привязать его Value к Health во ViewModel.

Файлы: Samples~/06. CustomBinder/.


Компонент без MVVM

public sealed class HealthBar : MonoBehaviour
{
[SerializeField] private Image _fill;
[SerializeField] private TMP_Text _label;
[SerializeField] private Gradient _gradient = new();

public float Value
{
get => _value;
set
{
_value = Mathf.Clamp01(value);
_fill.fillAmount = _value;
_fill.color = _gradient.Evaluate(_value);
_label.text = $"{Mathf.RoundToInt(_value * 100f)}%";
}
}
}

Компонент ничего не знает о фреймворке. Это важно: биндер — адаптер снаружи, а не зависимость внутри.


Биндер

[GenerateSerializableBinder]
[AddBinderContextMenu(typeof(HealthBar))]
[AddComponentMenu("Aspid/MVVM/Binders/Samples/Health Bar Binder – Value")]
public class HealthBarValueMonoBinder : ComponentFloatMonoBinder<HealthBar>
{
protected sealed override float Property
{
get => CachedComponent.Value;
set => CachedComponent.Value = this.SafeClamp01(value);
}
}

Всё, что нужно написать, — свойство Property. Остальное даёт база и атрибуты:

ЭлементЧто даёт
ComponentFloatMonoBinder<T>IBinder<float> плюс int, long, double, слот конвертера, кеш компонента, OneWayToSource
[GenerateSerializableBinder]генератор создаёт HealthBarValueBinder — сериализуемый двойник для полей [View]-классов без компонента
[AddBinderContextMenu(typeof(HealthBar))]пункт «Add Binder» в контекстном меню компонента HealthBar
[AddComponentMenu]место в меню Add Component
this.SafeClamp01(value)значение вне 0..1 не проглатывается молча: в консоль уходит ошибка, применяется ближайшее допустимое

Класс не sealed: сгенерированный двойник и проектные наследники должны иметь возможность расширить его.

Какую базу выбрать

Тип свойстваБаза
float / intComponentFloatMonoBinder<T> / ComponentIntMonoBinder<T>
UnityEngine.Object (Sprite, Material, …)ComponentObjectMonoBinder<T, TObject>
всё остальноеComponentMonoBinder<T, TValue>
значение нужно преобразовать и передать дальшеCasterMonoBinder<TFrom, TTo>

Сцена

В сцене нет [View]-класса: обычный MonoView со списком биндеров по именам (Health, HitCommand, HealCommand) и ViewInitializer с [Serializable] HeroViewModel, который редактируется прямо в Inspector. Так один экран не требует ни одного скрипта View.


Резюме

КонцепцияГде
Биндер = Property над CachedComponentHealthBarValueMonoBinder
Сериализуемый двойник из генератора[GenerateSerializableBinder]
Контекстное меню компонента[AddBinderContextMenu]
Валидация без молчанияSafeClamp01
MonoView + ViewInitializer без кодасцена Custom Binder

Полные правила написания биндеров — в Биндерах.