Event Bus, Паттерны на практике, Unity, C#

แชร์
ฝัง
  • เผยแพร่เมื่อ 4 ก.ค. 2024
  • Ссылка на гитхаб игры:
    github.com/Haywaar/VerticalSc...
    Ссылка на гитхаб классной но сложной реализации EventBus
    github.com/PeturDarri/Generic...
    Автору на кофе и шаурму
    4276 5500 5792 8742 - карта Сбербанка
    Если будут вопросы
    мой тг @wargy
    моя почта kazancev.s215@gmail.com
    Тайминги:
    00:00 Введение
    00:26 Проблема зависимостей классов
    02:00 Определение
    03:13 Event bus как решение проблемы зависимостей классов
    03:59 Техническая реализация Event Bus: введение
    04:25 Event Bus: Kolhoznik Edition
    05:24 Event Bus: Middle Edition
    07:08 Kolhoznik vs Middle
    08:57 Event Bus: Priority Events Edition
    10:21 Другие разновидности Event Bus
    11:21 Недостатки сигнальной шины
    13:52 Service Locator + Signal Bus
    14:42 Финал
  • เกม

ความคิดเห็น • 79

  • @fairytale_black_cat
    @fairytale_black_cat 28 วันที่ผ่านมา +2

    Делал на этом паттерне большой проект, на прошлом месте работы. Там по сетевому протоколу приходили команды, а куча объектов в сцене должны были на них реагировать и менять свое состояни. Чтобы не плодить классы с событиями, обработчики регистрировались по имени события, а сами обработчики должны были принимать на входе один объект событие. Внутри события была коллекция параметров в словарике имя/значение. Поддерживался приоритет подписки и возможность рассылки событий глобально и внутри иерархии сцены. Не очень по ООП, но главное что работало и позволяло избегать зависимостей между объектами. Все что объекты в сцене знали, это один статик объект для работы с событиями.

  • @user-rj1bo4gw9o
    @user-rj1bo4gw9o ปีที่แล้ว +6

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

  • @user-yn6np8xi7k
    @user-yn6np8xi7k ปีที่แล้ว +2

    Сергей, спасибо тебе за очередной вдумчивый и очень информативный урок!

  • @alaleksandr.
    @alaleksandr. 8 หลายเดือนก่อน

    Короткое и информативное объяснение с примерами кода

  • @PinkPanteRus
    @PinkPanteRus ปีที่แล้ว

    Спасибо! Разъяснил от простого к сложному!

  • @AzDzelo
    @AzDzelo ปีที่แล้ว

    Очень круто рассказал! Благодарствую!
    Продолжай делать новые видео, здорово получается!

  • @jasim1798
    @jasim1798 ปีที่แล้ว

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

  • @pashafilenko1567
    @pashafilenko1567 ปีที่แล้ว

    Спасибо за видео!

  • @MrG12g
    @MrG12g ปีที่แล้ว

    очень крутое видео! молодец!

  • @user-de1wo4xd4j
    @user-de1wo4xd4j 7 หลายเดือนก่อน

    как же я орнул с базированного выражения (13:20): Нормально делай - нормально будет!

  • @user-cb7dk1ow3h
    @user-cb7dk1ow3h 10 หลายเดือนก่อน +1

    Серега лучший!

    • @sergeykazantsev1655
      @sergeykazantsev1655  10 หลายเดือนก่อน

      Вовка привет! Рад тебя видеть, как с гуся вода)

  • @alexsorokin8373
    @alexsorokin8373 ปีที่แล้ว

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

    • @sergeykazantsev1655
      @sergeykazantsev1655  ปีที่แล้ว +1

      ну да, тут надо балансировать, но если аккуратно писать, соблюдать SRP, может вполне хорошо получиться)

    • @gwynbleinn
      @gwynbleinn 5 หลายเดือนก่อน

      Согласен. Колхозная реализация мне пришла в голову на втором или третьем проекте. Как и некоторые другие паттерны.
      Но вот качество их исполнения стоит улучшать

  • @gwynbleinn
    @gwynbleinn 5 หลายเดือนก่อน

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

  • @forcesoftheevil9252
    @forcesoftheevil9252 ปีที่แล้ว

    Спасибо за видео! Мне не нравится Event bus в двух версиях, так что я откажусь от подписок :D
    Но если серьёзно, почему бы шину событий разбивать на классы? Какие-нибудь PlayerEventBus, GameEventBus и прочее

    • @sergeykazantsev1655
      @sergeykazantsev1655  ปีที่แล้ว

      Я бы лучше ивенты тогда разбивал. Типа Eventbus.PlayerEvents.PlayerDamaged += DoSomething()
      А насчёт подписок, она есть во всех трех версиях, просто в первой она спрятана)

  • @olza4967
    @olza4967 ปีที่แล้ว

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

    • @sergeykazantsev1655
      @sergeykazantsev1655  ปีที่แล้ว

      Ну я вот этот скроллер на гитхаб выложил, можете посмотреть) Как раз рубрика "паттерны на практике" для этого и делалась) Юнити какие-то демки выкладывают, но я честно говоря не смотрел
      Хотя я у них на сайте нашёл бесплатную классную книжку, где сами Юнити рассказывают про паттерны, приводят примеры и тд resources.unity.com/games/level-up-your-code-with-game-programming-patterns

  • @Diyozen
    @Diyozen ปีที่แล้ว

    Хочется всё-таки сделать этот EventBus сервисом и зарегистрировать в сервис локаторе. А то чего он такой одинокий (хех) синглтон.

    • @sergeykazantsev1655
      @sergeykazantsev1655  ปีที่แล้ว

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

  • @kitoglav22
    @kitoglav22 4 หลายเดือนก่อน

    Привет! Объясните, пожалуйста, неофиту что происходит в КолхозникЭдишн при объявлении класса? Вижу приватный конструктор, вижу геттер статический, но не понимаю для чего оно нужно. Чтобы видеть всех подписчиков? Почему мы не можем сделать статическими все Action и весь класс в целом?

    • @sergeykazantsev1655
      @sergeykazantsev1655  4 หลายเดือนก่อน

      Шина колхозника основана на паттерне(если его можно так назвать) синглтон, на 4:33 можете справа посмотреть как я триггерю ивенты и как я на них подписываюсь.
      Статический геттер нужен чтобы мы к классу сигнальной шины могли получить доступ из любого места.

  • @gamedev_renaissance1075
    @gamedev_renaissance1075 10 หลายเดือนก่อน

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

    • @sergeykazantsev1655
      @sergeykazantsev1655  10 หลายเดือนก่อน

      Насчёт приведения к object, скажу честно, я не нашёл другого решения. Тут же к object приводится для того, чтобы сохранить все Action в одном Dictionary, так, чтобы они хранились вне зависимости от того какие у них входные параметры. Если есть более хорошее решение - я с удовольствием с ним ознакомлюсь и переделаю код, потому что каст к object мне тоже не нравится.
      Насчёт выстрела Invoke - это можно пофиксить если сделать какой-нибудь пустой интерфейс ISignal и всем сигналам от него отнаследоваться. Впрочем, например в том же зенжекте никого это не парит и вы можете Invok-ать в тело что угодно.

    • @gamedev_renaissance1075
      @gamedev_renaissance1075 10 หลายเดือนก่อน

      @@sergeykazantsev1655 Да, просто я перебираю ваши "наивные" реализации и модифицирую под себя (респект за материал). Из-за замечательной контрвариантности Action сделать CallbackWithPriority у меня не вышло, а вот к object кастить - пожалуйста. Поэтому решил спросить.

    • @sergeykazantsev1655
      @sergeykazantsev1655  10 หลายเดือนก่อน

      Во-во и я другого решения не нашёл)

  • @user-sn6xn1zx1v
    @user-sn6xn1zx1v 4 หลายเดือนก่อน

    Внимание, вопрос! :) Попытался внедрить шину сыбытий в своём проекте и столкнулся с проблемой. Как у тебя в проекте объекты с монобехами успеваются подписываться на сигналы. Поясню. Если учитываем, что код срабатывает последовательно, то может возникнуть ситуация, когда в одном скрипте создается сигнал, а во втором Start еще не отработал и подписка на сигнал не состоялась. Как быть?
    Или я что-то упустил? Пока я выкручиваюсь тем, что привязывают все нужные ситсемы друг к другу через Сервис Локатор и инициализирую в одном котроллере. То есть, использую Init вместо Start.

    • @sergeykazantsev1655
      @sergeykazantsev1655  4 หลายเดือนก่อน +1

      В завершающем видео по вертикальному скроллеру в конце(11:40) как раз с этим сталкивался.
      Вижу несколько вариантов:
      1. Просто следовать правилам. Создать сигнальную шину в Awake, подписаться всеми классами в Start, стрелять сигналам строго после Start. Что собственно и осталось в этом проекте. Можно это рихтовать Script Execution Order или если есть Entry Point
      2. Написать более сложную реализацию сигнальной шины с очередью, чтобы при подписке система проверяла не стрелял ли кто сигналами вот только что. Я такого решения не использовал но уверен что он есть.

  • @chillcompany1028
    @chillcompany1028 ปีที่แล้ว +1

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

    • @sergeykazantsev1655
      @sergeykazantsev1655  ปีที่แล้ว +1

      Скорее моя оговорка. Объекты слушают и подписываются.

    • @esteticachannel4604
      @esteticachannel4604 ปีที่แล้ว

      класс это и есть объект если что, это же ооп

    • @sergeykazantsev1655
      @sergeykazantsev1655  10 หลายเดือนก่อน

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

  • @esteticachannel4604
    @esteticachannel4604 ปีที่แล้ว +1

    вывод прост, используйте ecs

    • @sergeykazantsev1655
      @sergeykazantsev1655  ปีที่แล้ว +1

      С моей точки зрения ecs не является универсальным решением на все случаи жизни) с ним много своих проблем)

    • @esteticachannel4604
      @esteticachannel4604 ปีที่แล้ว

      @@sergeykazantsev1655 к примеру каких проблем?)

    • @sergeykazantsev1655
      @sergeykazantsev1655  10 หลายเดือนก่อน +2

      С моей точки зрения ECS предназначен только для игр/проектов где крайне важна оптимизация, скорость и производительность. Например игры с огромным количеством игровых сущностей(под миллион различных активных объектов на сцене), сетевые шутаны или те же MMORPG игры. Там DOTS даёт существенный бонус по производительности.
      Насчёт проблем - вот что знаю: сериализация данных и ресурсов, подгрузка бандлов и ресурсов. Недавно как раз смотрел конференцию игроделов где обсуждался ECS и каждая вторая фирма писала свой, а не использовала готовые решения так как сталкивалась с той самой сериализацией и подгрузкой ресурсов.
      А также излишне массивный код и непонятная парадигма на первое время для большинства начинающих разработчиков. Тот же match3 или вертикальный скроллер можно написать без ECS - пользователь разницы не увидит, но код будет написан быстрее и условному джуну/мидлу поддерживать его будет легче.

    • @esteticachannel4604
      @esteticachannel4604 10 หลายเดือนก่อน

      @@sergeykazantsev1655 странное мнение, оптимизация это лишь сайд эффект ецс, ецс же является первоначально архитектурным паттерном) дабы избавится от чрезмерной связности в коде и обеспечить гибкость, матч3 и скроллеры это что-то из разряда ГК где вообще архитектуру не соблюдают в принципе. DOTS это вообще не ецс, он там пришит сбоку. Погугли что значит ецс для начала и для чего он предназначен) подрузка бандлов и ресурсов, а также сериализация обеспечивается сервисами как раз таки со стороны ооп, это не проблема, гибридный подход в юнити пока не избежен, но всяко лучше чем потом спаггети легаси разгребать, а для подключения модуля нового какого 100500 зависимостей пробрасывать и думать какой бы паттерн тут бы всё зарешал

    • @sergeykazantsev1655
      @sergeykazantsev1655  10 หลายเดือนก่อน +1

      Могу я узнать на чём основаны такие заявления?
      Ты разрабатываешь игры на ECS? Если да, то для какого жанра, какой проект, как долго, какой фреймворк используешь(LeoEcs, Entitas или что-то другое)
      А если фреймворк свой - на чём он основан? Можешь ли ты скинуть статьи с хабра или откуда-то ещё про ECS которые конкретно ты считаешь базой и основой основ?

  • @user-de1wo4xd4j
    @user-de1wo4xd4j 7 หลายเดือนก่อน

    В конце не совсем понял, а в чем принципиально лучше данные с игрока получать, а не с события?

    • @sergeykazantsev1655
      @sergeykazantsev1655  7 หลายเดือนก่อน

      А я что-то такое разве говорил? Оо

    • @user-de1wo4xd4j
      @user-de1wo4xd4j 7 หลายเดือนก่อน

      @@sergeykazantsev1655 14:31 :)

    • @sergeykazantsev1655
      @sergeykazantsev1655  7 หลายเดือนก่อน +1

      А, так в том конкретном случае нам надо было показать текущий и предыдущий счёт после события levelFinished. Можно конечно ивент с этими данными отправлять, но какой смысл если это нужно только в одном месте)

  • @user-wq2cl7hl1o
    @user-wq2cl7hl1o ปีที่แล้ว

    что значит выдаст enr? 1:21

  • @a.danilenko
    @a.danilenko 11 หลายเดือนก่อน +1

    1. В проекте используется ServiceLocator - это антипатерн. ServiceLocator это вариант DI с кучей проблем и недостатков. Рекомендую к изучению книгу Марка Симана "Внедрение зависимостей в .Net". Эта книга познакомит с лучшими практиками внедрения зависимостей. Может быть в каких-то простых проектах ServiceLocator допустим, но не в профессиональной командной разработке. Даже учитывая эти оговорки, что ServiceLocator это некая альтернатива DI, я не могу не указать на то, что в совокупности недостатков у ServiceLocator гораздо больше, чем достоинств. ServiceLocator это не альтернатива DI это и есть DI, но в одной из самых плохих реализаций.
    2. EventBus это слишком "жирный" класс с точки зрения предоставляемого API - любой объект может подписаться на любое событие и вызвать любое событие. Это антипатерн. Такие решения тянут за собой много проблем, которые сразу видны опытному профессиональному разработчику. Интерфейс (в широком смысле слова) или API класса должен быть строго ограничен потребностями класса. Тут в одном абзаце всю проблематику не изложить. Если совсем кратко: нарушение принципов ISP и DI.
    3. Подписки и отписки от событий неконтролируемы, соответственно могут происходить на любом этапе работы приложения. Отсюда следует опасность потенциальной проблемы, т.к. операции подписки и отписки не являются потокобезопасными. Соответственно, если требуется потокобезопасность при подписке и отписке, то следует использовать синхронизацию при доступе разделяемым ресурсам.
    3. Часто встречаются мелкие недочёты в качестве кода: пренебрежение ключевым словом readonly, использование публичных полей вместо свойств и т.д.
    В общем, есть куда расти в качестве кода.

    • @sergeykazantsev1655
      @sergeykazantsev1655  11 หลายเดือนก่อน +1

      По поводу сервис локатора. На моём канале есть отдельное видео про сервис локатор где я говорю, что этот паттерн имеет свои недостатки и DI таки получше будет. В том же видео я говорю что SL может стать антипаттерном. А также есть подведение итогов, на котором я также говорю что сервис локатор многими дядями считается антипаттерном и у него есть проблема вседоступности. Критиковать сервис локатор в видео про сигнальную шину - крайне странно.
      Насчёт того что EventBus слишком "жирный" класс соглашусь, но как я понимаю тот же Zenject такая реализация вполне устраивает. Я согласен что на больших проектах будут проблемы, но без такой логики EventBus это не EventBus. Мне кажется что вседоступность ивентов это не такая большая беда, что как вседоступность классов как в том же SL. Но опять же говорю что да, наверное на больших проектах может вылезти
      Тайминги подписки и отписки и потокобезопасность да, в финальном видео по данной игре - я опять же говорил о том, что в некоторых случаях приходится это контролировать и с этим возникает головная боль.
      Очень интересно - где у меня публичные поля вместо свойств используются, покажите пожалуйста, мог реально промахнуться
      А расти можно бесконечно - я никогда не заявлял что в коде я преисполнился, что я Бог кода и тд

    • @a.danilenko
      @a.danilenko 11 หลายเดือนก่อน

      @@sergeykazantsev1655
      Я не смотрел видео про ServiceLocator.
      ServiceLocator это и есть DI. Наверное, ты путаешь DI с DI-контейнером. DI и DI-контейнер это разные понятия.
      Публичные поля: классы CallbackWithPriority, ScoreChangedSignal, AddScoreSignal, SelectShipSignal и др.

    • @sergeykazantsev1655
      @sergeykazantsev1655  11 หลายเดือนก่อน

      Я знаю разницу между сервис локатором и di контейнером, и в том же видео я про это говорил
      За публичные поля спасибо

    • @a.danilenko
      @a.danilenko 11 หลายเดือนก่อน +1

      @@sergeykazantsev1655
      Когда ты говоришь, что ServiceLocator это альтернатива DI, то ты имеешь виду, видимо, DI-контейнер.
      Вот я о чём. DI и DI-конейнер это не одно и тоже. Не следует их путать.
      ServiceLocator это тоже DI. Это тоже способ внедрения зависимостей. DI-контейнер и ServiceLocator это инструмент (вариант исполнения) используемый при DI.
      Крестовая отвертка не может быть альтернативой отвертке, быть лучше или хуже отвертки, поэтому что это тоже отвертка - вот такая аналогия, для понимания ситуации.

    • @sergeykazantsev1655
      @sergeykazantsev1655  11 หลายเดือนก่อน

      Под DI в этом случае я говорю в целом использование его в архитектуре, без DI-контейнера реализовать DI в проекте наверное можно, но зачем? Тот же Мартин Фаулер кстати в статье про SL сравнивает его именно с DI , но я думаю он тоже имеет DI как общую парадигму
      martinfowler.com/articles/injection.html