Pull to refresh

Comments 34

Я может не увидел по тексту… но зачем синглтон скрещивать с компонентом (MonoBehavior)?
«Мухи отдельно, а котлеты отдельно.»
Чем вам чем вас обычный мультипоточный синглтон не угодил? Положили его в отдельный каталог (напр., в Helpers, как я это сделал), добавили пространство имен в скрипт и используйте на здоровье.
>> Singleton -> Минусы
Добавьте тогда уж, что будет, если этот скрипт через редактор навесят на несколько объектов.
Только не говорите нечто типа «Юзер так делать не будет». Это человеческий фактор.
Этот факт можно легко отследить добавив в функцию инициализации синглтона проверку на то не был ли он инициализирован ранее и выводить предупреждение в дебаг, к примеру. Таким образом можно немного обезопасить себя от ошибок невнимательности.
Я неуверен, что там под капотом у Unity3D с многопоточностью, но возможно, что MonoBehavior инициализируются асинхронно (быть може на пулле потоков). Тогда есть вероятность, что такой вот синглтон проинициализируется дважды, трижды, стопиццотжды…

>> Этот факт можно легко отследить добавив в функцию инициализации синглтона проверку на то не был ли он инициализирован ранее и выводить предупреждение в дебаг

Вопрос мой, собственно, в том и состоит зачем тогда его вообще наследовать от MonoBehavior? Чтобы потом вот такие костыли наворачивать? Я, видимо, профит некий упускаю из внимания.
Я неуверен, что там под капотом у Unity3D с многопоточностью, но возможно, что MonoBehavior инициализируются асинхронно (быть може на пулле потоков). Тогда есть вероятность, что такой вот синглтон проинициализируется дважды, трижды, стопиццотжды…

Нет, жизненый цикл MonoBehaviour гарантирует, что методы Awake и Start вызываются в конкретное время и один раз. С жизненым циклом строго и на нем логика игры строится.

Вопрос мой, собственно, в том и состоит зачем тогда его вообще наследовать от MonoBehavior? Чтобы потом вот такие костыли наворачивать? Я, видимо, профит некий упускаю из внимания.

Потому что изначально у нас есть MonoBehaviour. Если мы не будем наследовать класс от него, то класс не будет являться скриптом и не сможет быть добавлен в объект на сцену.
>Нет, жизненый цикл MonoBehaviour гарантирует, что методы Awake и Start вызываются в конкретное время и один раз. С жизненым циклом строго и на нем логика игры строится.

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

> Потому что изначально у нас есть MonoBehaviour. Если мы не будем наследовать класс от него, то класс не будет являться скриптом и не сможет быть добавлен в объект на сцену.

Я не говорю, что не нужны системы сообщений, синглтоны. Это не нужно в Юнити. Идеология другая. Будете писать движок, — тогда эти вещи пригодятся. Да и Юнити не для больших проектов.
new НекоторыйКласс, не? Вполне работает и не нужно быть для этого наследником MonoBehaviour. Для синглтона в самый раз.

По поводу эфиров сообщений, синглтонов.

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

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

SomeClass().Instance().var — вот только в Юнити этого не надо, да и нигде не надо. Если уж не обойтись без синглтона, то прячьте его за статической функцией. Не нужно никому знать, что объект синглтон.

Зачем система сообщений, на отладку которой вы убьете кучу времени, которая будет делать кучу вызовов каждый фрейм, когда количество объектов, которым нужно знать о событии ограничено. Это не win32api. Да и с учетом байткодовости всех таких систем, они будут не особо эффективны, в отличии от того же win32api.

Юнити решил все задачи за Вас и дал Вам среду, где максимально быстро и красиво можно решить задачу. Вся низкоуровневая работа сделана разработчиками Юнити на C++.
Может Вы еще будете из текстовых файлов грузить конфиги? У меня были когда-то такие мысли, но убедился, что ту же конфигурацию намного быстрее забить в объект на сцене или префаб в удобной среде, чем редактировать файлы.

Пользуйтесь тем, что Вам дал Юнити, и не давайте вредных советов. Если Вашу статью кто-то сочтет авторитетной, то он пойдет по ложному пути. Вы сами это осознаете, и людей за собой поведете.
Я прочитал ваш комментарий, но не хочу отвечать на такой тон.
Ступайте с миром!
Извините меня. Я хотел бы выслушать ваши аргументы. Просто вижу те же самые грабли, которые встретил сам, наступил много раз, и пошел дальше. Но я не пытался свои заблуждения выставлять в роль руководства.
Агрументы по поводу чего?
Утверждение, что синглтоны дурной звучит для меня странно и как-то категорично. Подозреваю тут дело вкуса, но вкус обсужать не хочу, да и статье не про синглтоны.

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

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

Я вот сейчас только начинаю разбираться с Unity и столкнулся с уже упоминавшейся в комментариях проблемой — подавляющее большинство уроков, туториалов и книг идут по одной схеме — вот мы нарисовали почву, вот мы натыкали деревья и покрасили травку (в книгах главы две могут этим заниматься), вот камера от первого лица… А у меня, например, в мыслях прототип изометрии с непрямым управлением, и мне надо как-то хранить информацию о массивах тайлов пола и стен, отслеживать клики по ним, вести список задач, давать юнитам забирать какую-то задачу себе и при этом не давать другим юнитам в параллели брать задачу, которую кто-то уже занял, но не успел обновить статус задачи. И я пока не могу найти нормального описания реализации подобного взаимодействия.
Начните с более простых проектов, которые точно удастся завершить. По ходу дела станет понятно, какие возможности дает Unity, и что Вы можете организовать на их основе. Сможете поиграться с различными вариантами ваших задумок.

Большинство проектов, как известно, не доходят до конца. Да и с каждым проектом будет больше опыта. Сразу не бросайтесь на сложные задачи. Их решение займет много времени. Заодно выработаете свои персональные привычки организации работы в Юнити. Разберите код готовых проектов опытных программистов. Потом через некоторое время будете с удивлением смотреть на свои велосипеды.

А в остальном — обычное программирование. Применимы паттерны и все проверенные временем подходы. Тут уже к среде нет привязки. C# — такой же C# как у MS и не все должно быть MonoBehaviour'ом. Тем более, если некоторым объектам не нужно находиться в мировом пространстве, то им необязательно быть потомками MonoBehaviour'а и получать Update каждый кадр.

Легко можете применить DI, организовать систему сообщений. Слабое связывание позволит переносить наработки между проектами. Задачи повторяются.

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

Если ваше мышление не связывает конкретный язык программирования, то ответ надо искать в других местах. Где конкретно тема обсуждается. Можете взглянуть на проекты Soomla, которые, возможно, удовлетворят ваши потребности и легко интегрируются в Unity. Имеют открытый код.

Выбирая между «правильным программированием» и законченным проектом, стоит не упускать из виду возможности Unity. Например, новый UI, позволяет очень быстро на сцену набросать GUI и HUD. Отличный проект и законченный проект не всегда одно и то же. Лучше иметь в магазинах игру, которая приносит деньги, чем не иметь.
SomeClass().Instance().var — вот только в Юнити этого не надо, да и нигде не надо. Если уж не обойтись без синглтона, то прячьте его за статической функцией. Не нужно никому знать, что объект синглтон.


Статические функции увеличивают связность, а передача объектов — уменьшает. В идеале («если уж не обойтись без синглтона»), зависимость от синглтона должна пробрасываться в параметрах конструктора или функций настройки. То есть клиентский код использует синглтон как `_instance.SomeMethod()`, не зная, что _instance — синглтон. Об этом будет знать только внешний код, где осуществляется настройка: `var x = new Client(SomeClass.Instance)`. При необходимости, можно абстрагировать синглтон за интерфейсом, тогда появится возможность протаскивать в клиенты в поле _instance не сам синглтон (логгер, подсистему звуковых эффектов, etc.), а его моки или обёртки. А зависимость от статической функции (а-ля service locator) жёсткая, тяжело отторгается.
а почему 32api? что в 64х разрядной уже ушли от очереди сообшений? Челове всё правильно правильно описал, в более мение крупном проекте система событий будет хорошим решением.
Неужели нет инструмента от разработчиков Unity3D, VS или ReSharper, который позволяет при переименовании/перемещении файла скрипта или его перемещении сохранить связи этого скрипта с объектами, которым он назначен? Ведь весь секрет в том, чтобы при переименовании/перемещении скрипта переименовать/переместить соответственно и одноименный *.meta файл, что создается в проекте вместе с самим скриптом. А если такой инструмент есть, то при рефакторинге приложения не должна рушиться его консистентность, а значит и не нужны такие костыли, что описал автор. Откоментируйте меня, если я не прав )
Я не знаю такого инструмента. Было бы здорово, если он появится, решилось бы много проблем.
Но наличие такого инструмента не решает проблемы, описываемые в статье. Например, не устраняет жесткую связанность между разными частями приложения.
Так как сохранность состояния сцены для меня один из больших приоритетов, то я провел исследование и нашел это плагин:
www.visualstudio.com/en-us/features/unitytools-vs.aspx

В связке VisualStudio+UnityPlugin+Resharper я смог наладить рабочий процесс, в котором не приходится постоянно следить за хвостами. Думаю, на сегодняшний день это самое оптимальное рабочее окружение для разработки.

Буду рад, если вам это поможет.
Спасибо! Отличный плагин!
Я может не увидел по тексту… но зачем синглтон скрещивать с компонентом (MonoBehavior)?

Потому что в статье рассказывается о способак взаимодействия именно скриптов Unity (наследников MonoBehviour), которые живут в жизненном цикле сцены и могут использовать в своей логике факты вызовов таких функций как (Awake, Start, Update и множество других).
Вы хотите сказать, что скрестили синглтон с MonoBehavior потому, что автор выбрал именно такой заголовок в статье?
Взаимодействие между скриптами можно было реализовать и без наследования от MonoBehavior.

Вариант с месседжами снимается
Можно вообще воспользоваться стандартными средствами Unity3D — пробросом сообщений (напр., SendMessageUpward)
А чем плохи встроенные в юнити события (мессаджи: SendMessage и BroadcastMessage)? Документация клянется, что это работает очень быстро.
Работает только с MonoBehavior, «магические строчки» вместо имени метода не поддающиеся рефакторингу, и ограничение в один параметр. Ну и работает явно медленнее стандартных евентов.
Врёт. У нас были фризы в какие-то моменты времени, искали долго в чём проблема, я потом подумал на SendMessage, переписали без SendMessage — фризы исчезли. Сча очень редко их пользуем.
Помимо вышеперечисленного хардкода имен методов, ограничения по параметрам и производительности, возникает неоднозначность в случаях, когда объект, у которого вызывается SendMessage содержит в себе много разных скриптов. Нужно думать, контролировать, а вдруг еще какая-то компонента, кроме предпологаемого получателя содержит в себе метод с таким же именем, что будет в таком случае? А если наш объект содержит несколько копий одного скрипта на себе, но с разными параметрами, что будет в таком случае и как контролировать такие вызовы?
Спасибо всем, кто отписался, очень полезно!
Спасибо за статью, про третий метод не знал, буду впредь использовать.
Спасибо за статью, как раз таких статей по юнити и не хватает, как прикрутить физику, модельки, UI полно, а вот как заставить это все работать вместе — очень мало. А могли бы вы выложить, например на gist, пример реализации пула евентов.
Паттерны проектирования.
Наблюдатель
Собственно, проблемы связывания всех компонентов и кода в Unity нет. Проблема в головах тех, кто пытается делать что-то в этом редакторе, не понимая базовых вещей. Отсюда и обвинения в сторону редактора. Отсюда и «дошёл до азов в процессе эволюции». Теория — не ваш враг. Она ваш помощник. И её знание — не трата времени, а сильный инструмент в ваших руках и голове.
Извините, но я не понял, чем ваша реализация событий лучше стандартных событий C#? Вы делаете в принципе то же самое, но в вашей реализации приходиться дополнительно реализовывать Event Aggregator, постоянно пополняя его новыми событиями, а при использовании стандартных событий он не нужен.
Оставлю, пожалуй, ссылку на официальный урок по событиям от сайта Unity.
Принципиальное отличие от стандартных событий C# в том, что стандартное событие выполняется сразу, а в моей реализации выполнение откладывается до момента, когда MonoBehaviour выполняет свой Update.
Ну а что мешает дополнить событийную модель особенными ивентами которые стартуют команды и\или последовательности команд? то как бы удобное решение проблемы когда надо сделать цепочку действий.
Скажите а где инстанцируется UnitDiedEvent?
Sign up to leave a comment.

Articles