Pull to refresh

Comments 60

PinnedPinned comments

Много людей судя по просмотрам частей рефакторинга составили свое представление на основании только первой части. Да, мой недосмотр, что я не успеваю сделать таймкоды. Я сделал их ко второй части. В первой части делался грубый рефакторинг и было мало объяснений и не всегда людям с другим опытом легко вникнуть. Вторая часть даст больше пояснений и проводя рефакторинг дальше станет более очевидно правильность направления улучшения этого проекта.

Конечно рефакторинг затянулся. Но еще раз посмотрев, что получилось после 6 части, которая я думал будет последней - я понял, что есть работа как минимум для еще одной. Поэтому если вам это интересно - дайте как то знать ... для себя я уже натренировался, не оставляет только чувство, что не доделал до конца ... Мне важна ваша поддержка.

Наверное, лучше бы написали свой мини-курс по созданию TowerDefence. По времени вышло бы намного меньше, чем разбор чужого кода.

Возможно, вы не всегда отдаете себе в этом отчет, но это же все равно, что использовать глобальные переменные, которые вот уже несколько десятков лет так не используют.

Вы знаете, краткий ликбез для совсем "не отстреливающих" типа меня тут бы всё-таки не повредил. В код того Tower Defence я не смотрел, потому не рискну пример на его основе брать. Попробую отсебятину.

Допустим, я хочу сделать клон Game & Watch. Осовременить старого доброго "волка с яйцами", например.

Я хочу не заморачиваться по детальному обсчёту логики в каждом чёртовом инстансе объекта, а запихать по максимуму в глобальные переменные.

Одна для текущей позиции волка, по одной для каждого жёлоба для яиц, одна для зайца, счётчик очков, счётчик жизней, переключатель паузы (с положениями "игра идёт" - "пользователь взял паузу" - "пауза на анимацию разбитого яйца" - "пауза по окончании игры").

По одному контроллеру на анимацию волка (со считыванием последней нажатой управляющей клавиши, может быть, в отдельной переменной), зайца и яиц, контроллер учёта очков (и по совместительству обсчёта разбития яйца / потери жизни / окончания игры).

Вроде, ничего не забыл.

Чем плохо использование глобальных переменных в таком случае?

Ошибка в какой-либо части программы может иметь далеко идущие последствия, так как область данных открыта всем подпрограммам. В больших системах трудно гарантировать целостность данных при внесении изменений в какую-либо часть системы. В процессе эксплуатации уже через короткое время возникает путаница из-за большого количества перекрестных связей между подпрограммами, запутанных схем управления, неясного смысла данных, что угрожает надежности системы и определенно снижает ясность программы.

:) я думаю, автора цитаты указывать не нужно

Теория без практики мертва. Слепо следовать "чистому коду", solid-у и прочему менеджерскому хайпу с цитированием 80-летних дедов с совершенно другим техническим окружением, провозмогая и просто потому что так надо - не стоит. Если данный подход решает проблему - нет смысла оверинженерить.

Серьезно, теперь наступило такое время, что не признаются даже основы? А ну тогда ладно ...

Ну это же геймдев, ну. Тут все не как в кровавом ынтерпрайзе, каждый пилит так, чтобы просто заработало. Как показывает изучение кода последних популярных игр - лучше их не изучать, а просто играть.

З.Ы. Удивлен, что не было ни слова, что все надо хранить в субд и вообще ее упоминания - необычно. :)

Да что вы со своими кровавыми интерпрайзами. Подход у нас такой же, каждый пилит так чтобы работало. Отличается только тем что php с логикой около global, он как 20 лет назад жил, так и сейчас живет, иди вон попробуй туда фичу запилить. Пол гига легаси на Java 7? Изи! 50% кода это дубликаты, но потому что живет оно по 20 лет+, вплоть до того что фичу запилили 15 дет назад, 10 лет назад фичу с view убрали про фичу забыли, Новому индусу задачу поставили точно такую же фичу запилить год назад, он человек простой, сказали сделал.

Дядя Боб уже давно не основа, тем более в геймдеве. Он морально устарел и так до сих пор и не предоставил внятного примера хотя бы того же SRP, вокруг которого бы не порождались бесконечные флеймы. Но денег на тренингах хорошо собирает, это да.

Какая разница, возникла ошибка из-за избытка перекрёстных связей или из-за каскада неучтённых взаимодействий при мелкой правке где-то наверху иерархии?

Да, я слишком неопытен, чтобы докопаться до истины. Пятнадцать лет в этот барьер мышления бьюсь. Не выходит.

Разница в частоте возникновения данных событий. И скорости исправления таких ошибок.

Это всё объясняет. Но всё-таки не помешало бы уточнение.

Вы предлагаете частоту возникновения проблем обменять на сложность дебага этих проблем? Правильно понимаю?

Нет, в ООП уменьшается частота и упрощается сложность дебага. Но наверно даже поверхностное изучение ООП должно было рассказать о преимуществах инкапсуляции.

И все же, я позволю себе еще одну цитату классиков, покамест труды прочих неучей не вошли в сборники

значительным достижением является введение иерархии классов в процессе проектирования. Именно это называется OOD и именно здесь преимущества C++ демонстрируются наилучшим образом ... это означает не только уменьшение объема кода программ, но и удешевление проекта за счет использования предыдущих разработок, что дает выигрыш в стоимости и времени.

Но мне уже право не ловко, проводить обучение и рассказывать о преимуществах ООП онлайн ... статья подразумевает знание сих прописных истин ..

когда я писал ДЛЯ СЕБЯ простую утилиту на полтора экрана кода, я сначала решил, что прям-прям вот сейчас как наООПэшу по полной программе и начал её писать. После третьего экрана описания только структуры объекта и без начала написания самой программы я всё же вспомнил свой разговор с преподавателем еще далекого 95-го года об ООП и собственно там речь была об увесистом ПО или о библиотеках , то есть как раз об удобстве разработки больших проектов и о хорошей переносимости кода - если же эти условия не выполняются, то писать ООПэшный код вообще не имеет смысла. Я к чертям удалил всё и написал процедурный код в одном блоке Main().

Конечно, же в вашем негативном опыте оказался виноват ООП, а не ваше понимание задачи? ;)

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

Во-первых, я написал о положительном опыте.

Во-вторых, я показал пример, где ООП не будет уместным применять, поскольку не выполняются критерии, необходимые для его применения.

В-третьих, любая задача подразумевает выбор инструментов, согласитесь что для посадки двух одуванчиков глупо составлять смету, проектную документацию, проводить изыскательские работы и т.д. Проще копнуть ямку и посадить.

Собственно мой комментарий именно вам поэтому и был написан, потому что вы, явно, исповедуете принцип безоглядного ООП везде и всюду, что - неправильно.

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

Я уже достаточно давно исповедую принцип ООП, но далеко не безоглядно, и когда нашел соответствующий пример (его и только его), и когда он не настолько банален по принципу "мне лень, не хочу делать через ООП" я о нем честно пишу.

Прошу, тогда уж ознакомьтесь:

тут попроще - о проблемах ООП

+

Усовершенствование паттерна Flyweight в биовычислениях

Прошу, тогда уж ознакомьтесь:

тут попроще - о проблемах ООП

Как ни странно, но полная статья понятнее чем блок "попроще".

Но даже в обсуждении "попроще" вам комментаторы уместно возражают и хардкорном ООП везде и всюду. То что вы это не приемлете - не значит что это константа для всех.

Я спорить не вижу смысла, но к примеру ваша же отсылка к 1000 объектов и мой самый первый пример где всего 1 объект и десяток свойств как бы несравнимые вещи, вы указываете на сложности в использовании ООП в конкретно взятом проекте, а я пишу о нецелесообразности его использования с маленьких проектах, то есть примеры говорят о разных вещах. Так же мои слова в ваш адрес, о том что вы ярый сторонник ООП использующий этот подход везде и всюду являются правдой которую вы подтверждаете сами в "попроще".

Так что получается что я правильные выводы сделал и о вас и о вашем подходе к вопросу, а вот вы меня так и не поняли.

Понимание базовых истин и принципов приходит с опытом, через боль и страдания.

Новичкам естественно проще и понятнее более простая процедурная архитектура - они ещё не сталкивались с отладкой чего-то сложнее калькулятора и неуловимыми ошибками. Донести до них опыт можно, но вот понять его многие из них не смогут, только запомнить. Исключения - те, кто сталкивались с чем-то похожим на прошлом опыте.

Когда человек убьет день на глупый досадный баг, который можно было бы легко избежать, следуя очевидным истинам и простым принципам, он постарается в будущем не повторять такой ошибки.

В идеале такое надо сразу вносить в программу обучения, чтобы сразу на личном опыте новички ощутили не только плюсы, но и минусы того или иного подхода.

К сожалению в большинстве случаев этого не делается, возможно из соображений упрощения и снижения нагрузки: если новичкам с первых шагов расставлять грабли, путь сможет пройти меньше людей, тех кто сдался поприбавится. Как минимум той модели курсов, что эксплуатируется сейчас, выгодна именно массовость, а не качество.

В любом случае, по наблюдениям, первые 6-18 месяцев человек упорно пытается придерживаться процедурного подхода, генерируя страшный код. Потом обычно начинается прогресс - начинает приходить понимание, накапливается опыт. Но не у всех, к сожалению.

Серьезный бустер тут - обмен опытом с более опытными коллегами: ревью и рефакторинг чужого кода, обсуждения. Что-то постигается самостоятельно, что-то подсматривается в коде более опытных коллег, что-то выносится из обсуждений/проблем, свидетелем или участником которых был. Этого к сожалению типичные курсы тоже не дают - даже если есть группы, они одного уровня.

Стоит отметить что это проект для обучения!. В проекте прослеживается логика, SO расширяемость и замена преставления не вникая в подробности, реализовал, прокинул в SO, и не разбираешься что делает остальная часть кода. Своеобразная точка входа, удобство ставить задачи для группы студентов.

Статические переменные не чем не плохи в простых проектах. Я думаю преподаватель в вузе сможет это прокомментировать устно и этого будет достаточно.

MVC от части дает способ разбиение задач. Договорившись каким интерфейсом обмениваться, множество студентов могут работать над одной задачей. Один над вью, другой над моделью итд.

Минимизация использования Monobeh. Я думаю что многие компании стремятся не сильно зависеть от внешних движков и используют юнити в качестве фронта, в простом случае кор они оставляют прямо в юнити, но у них всегда есть относительно быстрый способ перейти на что-то другое.

Про архитектуру я промолчу, с вами категорически не согласен в вашем рефакторинге.

MVC от части дает способ разбиение задач. Договорившись каким интерфейсом обмениваться, множество студентов могут работать над одной задачей. Один над вью, другой над моделью итд.

Вы заблуждаетесь. Если для проектов WPF/WindowsForms/ASP.NET MVC хоть что-то дает, а именно позволяет отделить визуализацию от бизнес-логики. То в случае с Unity - в этом нет никакой необходимости. Над "View" работает 3d-моделлер или дизайнер уровней. Как такового кода UI в юнити просто нет. Есть бизнес-логика отображения, и её отделять от другой бизнес-логики совершенно не нужно. И как вы можете видеть на всем протяжении рефакторинга - проект существенно упрощался, бизнес-логика искусственно разделенная на части совмещалась, становилась в разы меньше, убиралось дублирование и бесполезный обмен запутывающими ссылками.

Если же для разделения работ, требуется написать архитектуру содержащую в 3-5 раз больше сущностей и перекрестных связей, то не кажется ли вам, что эта архитектура уже своим наличием превышает работу 3-х программистов, которую один мог бы выполнить без всякого разделения?

И конечно же, учить студентов создавать архитектуры, которые требуют многократно больших трудозатрат это интересная задача. Но совершенно не оправданная ничем. Вместо этого, нужно давать понимание, что каждая бесполезно введенная сущность, каждая без необходимости организованная связь между классами - ухудшает проект. И создаются эти дополнительные связи и разделения на методы и классы лишь для одной единственной цели. Для повторного использования. Но на всем протяжении рефакторинга не было ни одного повторного использования кода.

Про MVC думаю я не прав. Но ваша архитектура ничем не лучше архитектуры, которая была, вам просто так удобно, по своему. Но для обучения проект годный, да тут не ecs , mvvm, mvp-rx или ecs-rx, но основу получить можно, студентам заинтересованным и не очень.

Не стоит думать, что в этом примере, я как то по особенному организовал "свою" архитектуру от которой "мне удобнее". Я всего лишь, убрал все не нужное в проекте и его оказалось чудовищно много. Если бы Junior писал бы такие вот проекты - я был бы рад, в итоге получается проект "без архитектуры". И его можно минимальными средствами доводить "до ума, чтобы было удобно". Такой же проект, как он был в изначальном состоянии проще выбросить.

Еще раз для обучения это нормальный проект, скорее всего его написал преподаватель в свободное время. Есть задел на будущее, можно каждый семестр не писать одно и тоже, а давать задачи на расширение, 20 студентов в группе смогут взять себе по минитаску и довести его до ума, преподу в кайф, на его глазах расширяется проект.

имхо, для обучения полезнее посмотреть рефакторинг :) вы посмотрите полнее, там много чего интересного по ходу дела .. по первому стриму если только судить, можно получить немного превратное мнение, постепенно там становится все более и более безапелляционно. Но это требует времени, времени которое должен тратить ведущий программист на горе junior`ов, которых обучил молодой преподаватель "по кайфу"

Как такового кода UI в юнити просто нет.

Я тут влезу немножко и сморожу очередную глупость.

Относительно кода как сущности понятия UI не существует.
Понятие UI существует только в разрезе собственно логики, в отрыве от кода.

Разделение логики и визуализации - это да, нужно-важно-неоспоримо.
Но что делать, если сам инструмент активно противится чёткому разделению И созданию удачной архитектуры?

Более того. Что делать, если отсутствие разделения на начальном этапе - неизбежно?
Между фазой "каждая сущность - объект! боже, сколько же их..." и фазой "здесь объект не нужен, здесь объект не нужен, там тоже не нужен... вуаля! лаконичный поддерживаемый код!" - огромная куча промежуточных шагов. В т. ч. шагов по перестройке образа мышления.

Каждый встречный конструктор игр начинает ознакомление будущего программиста с объектов "игрок", "пуля" и "противник" не от хорошей жизни.

Принцип "Если мне это не надо, значит никому не надо" в действии.

ScriptableObject - это префаб, только без трансформа. Делать из него игровые сущности так себе затея, но это отличный механизм для хранения настроек. Хранимый на сцене GameObject с параметрами может быть просто уничтожен, его трудно редактировать и переиспользовать.

MonoBehaviour по сравнению с POCO - оверхед на менеджмент GO, компонентов, времени жизни, плюс всякие подводные камни типа сравнения с null. Ну и иногда просто не хочется быть привязанным к UnityEngine.

Кстати, замена private SerializedField на public - плохая практика, если над проектом работает больше одного человека, чтобы всякие умники не меняли значения в рантайме.

MV* позволяет не держать в своей голове сразу всё. Можно собрать всю игру в одном классе, в этом нет ничего плохого. Но в какой-то момент игра разрастается до такого состояния, что менеджмент UI, настроек, сети, файлов хочется все-таки разделить на более контролируемые части.

Немного оверинжиниринга для учебного проекта необходимо, чтобы показать, как это вообще работает в реальных проектах. Ещё бы это объяснялось, почему так надо или не надо делать, и какие есть ещё варианты.

По моему мнению, здесь рефакторинг скорее не удался, чем удался. Согласен лишь с тем, что глобальный доступ к ивентам и настройкам - плохая идея.

Ещё лет 20 назад я услышал фразу, которая хорошо характеризует ситуацию: "кто не умеет работать - тот учит".

Работать умею, спасибо)
Но хотелось попробовать себя и в преподавании. Спойлер: совмещать работу и преподавание очень сложно.

И ООП нас учит, что нужно так декомпозировать объекты реального мира на классы, чтобы они содержали бы в себе и состояние, и поведение вместе. В этом по сути базис ООП.

А еще в принципах ООП указано Наследование. Однако уже давно определено, что наследование часто бывает вредным.

Само заявление, что класс должны содержать в себе и состояние (если здесь идет речь именно про динамическое состояние, а не readonly зависимости при DI) и поведение вместе, довольно спорное. Оно актуально для некоторых паттернов, таких, как Builder. Но для слоя бизнес-логики подобный подход стоит использовать с осторожностью.

Четвертое, я периодически сталкиваюсь с мнением, что в Юнити нужно минимизировать использование MonoBehaviour? Но, почему?

Для начала, MonoBehaviour - это достаточно дорого для производительности.

Второе - это проблема с отслеживанием зависимостей и взаимодействия различных систем между собой.

Объект имеет состояние, обладает некоторым хорошо определенным поведением и уникальной идентичностью

Если и это утверждение для кого-то спорное, то может просто и не стоит ООП применять? Видимо мы сейчас увидим, как неучи создадут новую парадигму и мы вернемся к использованию глобальных переменных, с процедурами, операторами goto и спагети коду ... зачем, что-то учить и соблюдать, если и так заработает ... повальная безграмотность

ООП сформировалось полвека назад, за это время оно прошло серьезную эволюцию. Более того, те принципы, которые вы проповедуете, были описаны много позже самого появления понятия ООП.

Если для вас неучи - это те, кто не застрял в семидесятых, то ок.

Я являюсь автором этого курса.

Во-первых, спасибо большое за статью, очень интересно было услышать мнение о курсе от кого-то кроме студентов. Я постараюсь объяснить мой подход при создании архитектуры игры на юнити, который возможно объяснит зачем все делать так сложно.

Я вижу своей задачей создать конструктор для геймдизайнера, в котором он сможет создать собственно игру. Без геймдизайнера мой проект выглядит больше как конструктор, в котором пока еще не во что играть. И пока не увидишь работу геймдизайнера, вся эта сложность и гибкость может показаться излишней. Но когда ему придется настроить 20 юнитов, у которых чуть отличаются абилки, параметры и тд и тп, они будут вам очень благодарны за такую гибкость. Говорю это как человек, который два года проработал над очень крупной РПГ. Мне хотелось подготовить студентов к большим проектам, поэтому я пытался привить им такой подход. Не утверждаю, что у меня это получилось хорошо (первый раз пробовал преподавать), но по крайней мере цель была такая.

1) Мне кажется, что SO гораздо более гибкие для настройки игровых сущностей и не имеют лишних полей (типа Transform), лишних функций префаба, что дает меньше возможности запутаться. Проверено опять же на крупном проекте.

2) Использование static полей. Использование их налево и направо действительно чревато, но если создать одну точку входа в виде Game, то я не вижу особых проблем в этом.

3) MVC. На данном этапе игры это кажется излишним, но на самом деле это задел под сериализацию, мультиплеер и сохранение состояния игры в файл (сейвы проще говоря). В этом случае мы сохраняем/передаем по сети только Data. Это кроме того, что так на мой взгляд код гораздо лучше декомпозируется. Плюсы от этого легче ощутить, работая над большим проектом в команде.

4) Как уже кто-то написал выше, частое использование монобехов ведет к сложностям с отслеживанием зависимостей. Я очень много раз на это натыкался на крупном проекте (особенно в UI). Что-то где-то произошло, а ты не понимаешь почему. А оказывается там в каком-то мелком объекте выставлена криво ссылка. Дебажить это почти нереально. А вот если все из кода, проблема становится сильно проще. Но тут опять же надо ловить грань: что является ответственностью программиста, а что является ответственностью дизайнера. И надо стараться не давать дизайнеру ничего лишнего, потому что они рано или поздно ошибутся, а баг вам искать)

Я понимаю откуда возникает диссонанс от этого проекта. С одной стороны игра выглядит простой, делается в одиночку, но при этом содержит излишнюю гибкость. Я же хотел подготовить студентов к будущим проектам, на которых будет требоваться масштабируемость кодовой базы, возможность разработки в команде, удобство для гейм/левел и прочих дизайнеров. Если бы я готовил студентов к хакатонам, то подход конечно был бы совсем другой.

Надеюсь без обид, надеюсь говорить, что я с вами не согласен - излишнее. Именно для больших проектов так делать еще хуже. То, что вы хотели и что в итоге получилось - разные вещи. Нельзя на примере, где нет в этом необходимости, показывать то, что хочется. Я планировал, закончить эту демонстрацию показав, где, какое и главное когда возникает необходимость в MVC. Но количество рефакторинга меня накрыло ... но я думаю я сделаю этот итоговый ролик. Подписывайтесь на канал :)

Обид конечно нет. Я основывался на своем опыте работы в крупной студии. Если у вас другой опыт, могу понять.

Если такого рода архитектуры действительно делают в Owlcat Games - у меня к ним много вопросов. Но кто я такой, чтобы они консультировались бы у простого смертного :)

Ага, только там все раз в 10 все сложнее. И на мой взгляд все оправданно. Когда стоит задача создать игру с таким масштабом и с такой гибкостью, работая в команде 50+ человек, другого пути я не вижу. И тут даже вопрос не в теории. Попробуйте перевести все SO на монобехи (боюсь соврать, но по моим ощущениям там 100к+), юнити просто колом встанет и все тут) Она итак еле живая была с таким количеством данных.

Ну и как адекватно сохранять такое большое дерево данных со всеми абилками со всеми стейтами игровых сущностей без MVC я не представляю.

Вы не понимаете главного - в правильной архитектуре ничего переводить не нужно, оно изначально правильно. Ничего там не встанет, и это прямо следует из вашего проекта. Создавая SO, вы не уменьшили нисколько кол-во монобехов. Все что я сделал, я удалил лишние SO, ничего не добавив. И ничего не произошло проект по прежнему выполнял свою функциональность, не стал чем-то хуже, медленнее или что там еще?

И прятаться за "объем" - такое себе, что работает плохо на малом объеме, еще хуже работает на большом ...

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

Выбирать стоит наиболее подходящее решение для конкретных задач (понятное дело, думая о будущем) и когда вопрос стоит в обучении студентов то заинтересовать рабочим результатом, одна из важнейших задач. А кому зайдет с опытом уже придут к чистоплотности

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

Я правильно понял, что с вашей точки зрения, легче всего разбить на сущности в соответствии с изменившимися требованиями единственный класс Game с многотысячестрочным методом main(), содержащим всю игровую логику, чем эту же логику разбитую на любые классы?

Уважаемый, свои инсинуации оставьте при себе, а не приплетайте меня к ним. Смотрите мой канал, бесплатно обучайтесь на нем, и возможно скоро это вам поможет стать программистом. Но для этого нужно внимательно слушать и стараться понять, о чем там говорится.

А и порадую, видимо, моего старого знакомого, правда в гримме я его не узнаю @Leopotam

Отвечая на вопрос

как адекватно сохранять такое большое дерево данных

Надо вспомнить, так между делом, что для таких вещей цивилизация придумало такое понятие как базы данных и 3-ю нормальную форму, которая позволяет максимально все оптимизировать.

Нормализацию придумали не для «максимально все оптимизировать», иначе не пришлось бы придумывать денормализацию.
И не бывает «правильной» архитектуры, а бывают архитектуры удовлетворяющие (или не удовлетворяющие) определенному набору требований.

Денормализацию не придумывали, а ради исключения шли на неё, когда удобство хранения данных было важнее её оптимальности.

Чьей – «её» оптимальности? Как на нее могли пойти, предварительно не придумав? Вы заглядывали на определение денормализации хотя бы там на википедии?

Уважаемый, как раз вам и рекомендую изучить этот вопрос, чувствуется что вы о нем знаете очень мало. А чтобы нарушить нормализацию - ума много не надо, и придумывать нечего.

Денормализация – это не когда «нормализацию не осилили», как можно подумать на основе ваших комментариев.

БД это ответ на вопрос где хранить сейвы. А MVC отвечает на вопрос как данные должны быть структурированы в игре, чтобы их было максимально удобно далее сериализовать/десериализовать. Например с помощью NewtonJSON можно одним вызовом сериализовать класс со всем его деревом полей. Поэтому если у вас все данные выделены в отдельную область (Data или Model), то их одним вызовом можно перенести в json и обратно. А если этого нет, то придется каждое поле руками сериализовать. И это не номер пройденного уровня, а весь игровой стейт. Все позиции персонажей и мобов, все абилки, весь инвентарь, все статусы, весь лут на карте, все дерево прогрессии персонажа. А если вам нужно просто номер уровня сохранить, то подойдут внутренние механизмы юнити, типа PlayerPrefs.

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

И приходилось ли вам вообще писать систему сейвов где есть весь игровой стейт?

Конечно, мне приходилось реализовывать сохранения, причем во всех перечисленных вариантах - и в БД на сервере, и в БД у игрока, и в файл у игрока ...

И конечно, если вы уже насоздавали кучу ненужных SO, то хотя бы их для сейвов поиспользовать можно. Но, вот только не надо представлять это как "придется каждое поле руками". Да придётся, но не сравнивайте это с созданием для этого SO и опять же переприсваиванием из него и в него. Это много больше.

Вот кстати:

DirectConvertor - прямые конвертации альтернатива сериализации [сохранение ваших игр]

И маленькая ремарка, это был курс по выбору. Его обязательно пройти, если ты его выбрал, но выбирать его не обязательно.

Много людей судя по просмотрам частей рефакторинга составили свое представление на основании только первой части. Да, мой недосмотр, что я не успеваю сделать таймкоды. Я сделал их ко второй части. В первой части делался грубый рефакторинг и было мало объяснений и не всегда людям с другим опытом легко вникнуть. Вторая часть даст больше пояснений и проводя рефакторинг дальше станет более очевидно правильность направления улучшения этого проекта.

Я, например, «составлял свое представление» на основе ваших комментариев, так как ютоб слабо подходит для кодревью. Вне зависимости от наличия таймкодов.

Я не согласен касательно MVC и разделения сущностей.

Объекты имеет смысл делить, даже если повторное использование не требуется. При разработке стоит проблема сложности логики, высокая сложность усложняет поддержку кода и повышает количество багов. Когда мы делим классы или методы на несколько маленьких (без фанатизма и с умом), то сложность снижается и код становится гибче.

Логика "меньше сущностей - лучше" выглядит как попытка свести всё к большим объектам по 500-1000 строк кода, хотя я думаю автор статьи и не имел это в виду, но чем больше проект, тем вероятнее такое будет происходить, если не делить код на сущности и не создавать между сущностями связи. Такие вещи как low coupling и high cohesion не просто так были придуманы, как и правила по количеству строк в классе/методе. Всё это напрямую связано со сложностью кода, поддержке, рефакторингу, расширению и т.д.

Если у меня большая игра, то мне так или иначе придётся делать логику на модель и визуализацию. Даже если и то и другое будет лежать в компонентах на монобехах. Когда люди делают монобех с логикой, а дальше извне подвязывают звук, анимации и эффекты, то это тот же самый принцип с разбиением, что и в MVC, и делать так желательно.

Ну и касательно создания абстракций над Unity и вынос модели в отвязанный от Unity код. Это тоже имеет смысл. Часто не хочется чтобы цикл жизни каких-то объектов был привязан к визуальным объектам на сцене. Если у меня есть режим игры, либо игровая валюта игрока и т.п., то мне абсолютно не нужен объект на сцене на такие вещи. Я хочу поставить какой-то главный объект на сцену, который внутри себя создаст то, что нужно будет для работы любой игровой сцены, либо меню.

Пожалуй, я с вами даже соглашусь. Я уже записал и скоро будет на канале - я сделал ровно обратный пример, когда MVC нужна. По сути, все эти "упражнения с рефакторингом" были для того, чтобы показать когда же на самом деле нужен MVC, и как его делать, чтобы он не "уничтожил" ваш проект.

Только почему то одни - восприняли мою статью с тезисами, как разговор об сфере применения ООП. А она таковой не является, хотя такую я писал лет 10 назад здесь ...

На самом же деле - эта статья и рефакторинг о наглядном, плачевном состоянии умов ... когда нет понимания, когда нужен MVC. И делают абы было, да еще и не правильно. Вот о чем речь ... Это совсем не отменяет того, что MVC может быть полезна и о том, что её можно правильно реализовать. Но лучше без MVC если она не нужна или не выполняет свою роль.

Именно, тогда и только тогда, я говорю учат неправильно.

Единственно, у вас не верный критерий:

Когда мы делим классы или методы на несколько маленьких (без фанатизма и с умом), то сложность снижается и код становится гибче.

Логика "меньше сущностей - лучше" выглядит как попытка свести всё к большим объектам по 500-1000 строк кода

Совершенно без разницы сколько строк кода составляет класс или метод. Да это обычно коррелирует с тем, что там может быть что-то не так. Да, у Фаулера - это чуть ли не первый признак "кода с душком". Но вы то хоть видели код над которым я проводил рефакторинг - там пустые методы! И даже если бы они имели тяжелую логику, не это является признаком.

Когда вы начнете следовать ООП и правильно декомпозировать объекты, вы с удивлением увидите, что "больших" классов/методов у вас не будет. Это произойдет само собой, и в этом и есть преимущество ООП. Почему так произойдет? Потому что вы будете основываться на связности и зацеплености объектов. А не на искусственном разделении "большого" . А это и означает - повторное использование. Поэтому я с вами соглашусь, хотя вы пока еще не поняли что "(без фанатизма и с умом) " это и есть "когда возникает необходимость в повторном использовании".

как не залезешь на github - проект из 15-30 файлов .cs, каждый на 7-15 строчек кода, программа уровня "hello world!", без поллитра не разобраться, без примера вообще не ясно что и как работает, прыгаешь туда-сюда из файла в файл - сомнительное какое-то удобство.

нет, я понимаю что я тот еще валенок и не проецирую свою тупость на всех, я в целом о характере кодинга, когда читая asm файл для 6502 я понимаю всё куда лучше и быстрее.

что "больших" классов/методов у вас не будет

Я делал обработчик файла, там одна процедура была на 400-500 строк. При желании можно было в паре мест что-то выделить в отдельные процедуры, ну стало бы 350 строк. Не совсем ясно какая связь ООП и количества команд. Если 350 команд раскидать по 10-ти методам то кроме ухудшения чтения кода ничего не получится. Тут же и проблема с оптимизацией кода, когда что-то уходит в отдельный метод, то нужно использовать или глобальные данные или данные копировать или передавать ссылку на данные, то есть выделение кода в отдельный метод становится еще одной задачей, которой не было до этого.

когда же на самом деле нужен MVC

Тут важно понимать что MVC не привязан к ООП.

Каждый понимает ООП и шаблоны проектирования как хочет.

1 – Насчет SO я в принципе согласен. Если первая мысль при решении проблемы – использовать СОшки, значит есть другое решение. Но и полностью отрицать СО нельзя. Их можно использовать для создании данных для уровней , использовать пустые СО в виде ID, использовать в качестве конфига игры, который будет пылиться в DI или Service Locator.  

 

2 – Сатик поля для доступа к объектам. Ну да, не хорошо, я бы так не делал и другим бы не позволил, но в качестве альтернативы ServiceLocator или простого DI для студентов подойдет. Я бы лично для небольшого проекта, да и для любого использовал ServiceLocator такого вот вида. Есть пить не просит, с рефлексий не балуется - ну и пускай существует.

public class Services<T>
    {
        private T _service;

        private static Services<T> _i;
        public static Services<T> S=>_i??=new Services<T>();

        public T Get() => _service;

        public T Set(T service) => _service = service;
    }

Почему нужен Di\ServiceLocator? У тебя будет как минимум Controller сцены, который обязан её инициализировать при её загрузке: спавн уровня, спавно игрока, иницилизация всех других сущностей теми, которых он только что заспавнил. А ещё точно будет Mediator между игрой и всем UI. Вот уже как минимум две сущности, к которым захочется достучаться в любое время и им как раз самое место в DI, ServiceLocator или на худой конец в статики.

 

3 – Про MVC. Вот тут чистое имхо, ногами не бить. MVC в юнити с вероятностью 99 процентов не нужен, но если его все же использовать то модель должна быть точно такой же компонентной.  То есть мы имеем какой-нибудь DataObject с CURD интерфейсом. А это за собой тянет следующие вещи:

  1. Интеграцию с юнити инспектором

  2. Отдельный редактор если надо посмотреть базу вне юнити

  3. Система сохранение этих штук в отдельный файл

  4. Если файл большой, то придется использовать кокой нибудь b-tree, чтобы просто этот объем не хранить в оперативки, а иметь к нему доступ, как мы имеем доступ к ассетам в редакторе через AssetDatabase.

  5. А ещё может понадобиться язык сценариев, например JINT (реализация JS). Это заместо UnityAction или UltEvent, которых не будет в базе.

В итого это мини DataBase. Я такое искал – не нашел, если у кого-то есть ссылка, то милейше прошу поделиться. Иначе придется писать свой, использую BplusDotNet.

4 – тут исключительно согласен. Больше компонентов богу компонентов. А ещё если сверху приправить Odin и новым UI Toolkit с котором писать свои редакторы стало в разы проще, то работа с юнити становится просто наслаждением.

5 – Тоже согласен. Только я бы хотел добавить, что не надо бояться создавать пустые MonoBeh или SO. Такие пустые классы могут служить марками для других классов, которые будут работать с теми game object, на которых навешена пустышка.

 

Конечно рефакторинг затянулся. Но еще раз посмотрев, что получилось после 6 части, которая я думал будет последней - я понял, что есть работа как минимум для еще одной. Поэтому если вам это интересно - дайте как то знать ... для себя я уже натренировался, не оставляет только чувство, что не доделал до конца ... Мне важна ваша поддержка.

Sign up to leave a comment.

Articles