Найти в Дзене
Лавка Разработчика

Поделюсь еще одной схемкой

Поделюсь еще одной схемкой. Более непонятно, но уже ближе к проекту #пилимигру, что мы с вами делаем

В посте выше, я уже рассказывал, как устроены взаимосвязи в MVVM. Но то были взаимосвязи "по вертикали", то есть кто кого видит, и за что отвечает. Теперь же нам нужно бы разобраться, как строятся горизонтальные связи. Как условное оружие, которое можно модернизировать отображается и в геймплее и в UI окошке с крафтом, например? Используют ли они одну вьюмодель?

Прежде чем читать далее, небольшой дисклеймер:

1. На схеме выше, а также в проекте #пилимигру мы делаем не бест оф зе бест реализацию MVVM. Потому что идеальная реализация подразумевает то, что баиндеры - это маленькие монобехи цепляющиеся непосредственно к свойствам классов вьюмодели. То есть в редакторе вешаешь баиндер (монобех) на строку, выбираешь вьюмодель к которой цепляешься и реактивное свойство со строкой. Таким образом такой баиндер можно использовать повсюду. Баиндеры могут быть и на спрайты, и на звуки, и ковертеры вроде bool => Sprite, чтобы в зависимости от реактивного свойтсва подставлять нужную картинку и т.д. Этого у нас не будет, но у меня однажды была попытка сделать подобное в ассете Lukomor. Наверное, надо бы его обновить, глядишь, полезным окажется

2. Вьюмодели нужны не для всех объектов в игре. В зависимости от игры, для мировых объектов вьюмодели могут не применяться. Пример: игры вроде Lethal Company, или R.E.P.O. Все объекты с которыми можно взаимодействовать не нуждаются во вьюмоделях, т.к. не содержат необходимости быть соединенными с моделью. Игрок играет сессию, собирает/рушит/перемещает предметы в игре, но это никак не влияет на модель (сами предметы в модели никак не отображаются). Предметы могут нанести урон игроку - но это происходит за счет игрока, соединенного с моделью, а не предметов. Так что не нужно пихать вьюмодели там, где они не нужны.

В остальном, мы получаем некое дерево зависимостей, с входом через 2 точки: для UI и для World, т.е. для мира. Так разделено, потому что UI обычно создается из префаба, а World (то бишь сцена) имеет какие-то заготовки помимо создаваемых объектов. Фабрики вьюмоделей регистрируются в DI контейнере, поэтому в момент входа на сцену после регистрации, мы можем запросить нам выдать корневые вьюмодели - для UI и для World. Тут же в точке входа мы можем вытащить UIRootBinder и WorldRootBinder и выполнить методы Bind(uiRootViewModel) и Bind(worldRootViewModel) на обоих. Каждая вьюмодель может иметь внутри себя ссылки или списки ссылок на другие вьюмодели. Они даже могут пересекаться (несколько вьюмоделей иметь ссылку на одну и ту же модель, см. ViewModel D). Игрок имеет инвентарь, инвентарь имеет ячейку, ячейка имеет предмет - вложенность, я думаю понятна.

Получается каждый баиндер получает свою модель. И (в нашем проекте) каждый баиндер имеет ссылку на вложенные баиндеры. То есть условный баиндер инвентаря имеет ссылку на баиндеры ячеек инвентаря. Баиндер инвентаря получает вьюмодель инвентаря в которой лежит список вьюмоделей ячеек инвентаря. Баиндер инвентаря проходится по этому списку и запихивает каждую вьюмодель ячейки в баиндер ячейки. И так далее. Получается дерево.

Интересный момент, что при такой структуре разные баиндеры могут получать одну и ту же модель и отображать ее по разному. Вспомним пример с крафтовым оружием из начала поста: вьюмодель оружия содержит информацию обо всех "обвесах", мировой баиндер оружия их визуализирует, а UI баиндер показывает иконками, какой обвес в какой слот на оружии добавлен. Меняя обвес в UI, он автоматически меняется и в мире, т.к. биндеры подписаны на одну и ту же модель

Как-то так. Похоже, скоро всё соберется в кучку и выдавится в видеоролик.. Вопросы!

-2
-3