Данный код не сработает: "Input.GetKeyDown(KeyCode.Space) += Jump;" Статью случайно не GPT писал?)
Что показывает "Пример использования событий и делегатов" в разделе "оптимизация скриптов"? Что там оптимизируется?
"Избегайте использования коллекций, которые часто изменяют свой размер, таких как List<T>. Вместо этого используйте массивы или Queue<T> с заранее заданным размером." Странная формулировка, почему не List с заранее заданным размером? В примере с ObjectPool это можно было бы кстати учесть.
Наверное мелочь, но GeometryUtility.CalculateFrustumPlanes можно было бы закешировать в переменную перед циклом, а во-вторых использовать версию принимающую массив, а не создающую каждый раз новый. Понимаю, что суть примера была не в этом, но статья про оптимизацию всё-таки.
За 3 года опыта побывал на 5 проектах, бывали плотные дедлайны, кранчи были пару раз, оба в течении недели, один хорошо оплатили. Чтобы меня кто-то под дулом пистолета заставлял перерабатывать, не помню, не можешь - не работаешь. Это конечно анекдот, а не статистика, но "кранчат ли у вас" это не вопрос, который я задаю на собесах, поэтому хватит пугать новичков.
Насчет поддержки не знаю, возможно соглашусь. Автор цитаты которых я приводил, не согласились бы, Майк Актон говорил, что логика становится заметно проще, 0 абстракций, прозрачный алгоритм, но это ладно, я не настолько опытен, чтобы судить.
Касательно Unreal, умные указатели там есть, но из того, что я видел, работая с ним, часто используются сырые, потому что в движке используется сборщик мусора. В Unigine в плюсовой апишке тоже сырые, и погуглив тоже нашёл информацию про сборщик мусора. В новом O3DE всякие Entity/Component тоже по сырым (вроде без GC). Умные указатели видел только в недавнем UltraEngine. Так что все флагманы стараются от умных указателей уйти, пусть даже в сборку мусора.
Я по работе пишу на Unity и C#, с низкоуровневой разработкой игр и движков знаком, но не сильно, только учусь (язык Odin). Но смотря разные видео-лекции, от того же Кейси Муратори, Майка Актона (знаменитый доклад по DOD), или Джонатана Блоу. Все они топят против умных указателей и против массивов указателей в целом. Мол делай плотно упакованные массивы данных, вместо указателей используй индексы, смотри не на отдельные объекты, а на множества объектов, используй кастомные аллокаторы вместо new, аллоцируй и переиспользуй большие блоки памяти, и твой код будет работать в 5-10 раз быстрее, при тех же усилиях, а умные указатели отпадут за ненадобностью. Что думаете про подобный подход? Писать как говорят джентельмены выше на практике слишком сложно? Или если оверхед небольшой, то и ладно?
Unity по цене меняется только в самой свежей версии, а то что хуже и хуже, то чем же хуже? Я пробовал Unreal, инструмент конечно мощный, но в плане удобства и скорости разработки он Unity сильно проигрывает, поэтому я бы не стал однозначно топить за какой-либо из движков.
Спасибо за статью. Хотя я увидел, что статья не затрагивает конкретные реализации, без них статья ощущается какой-то неполной.
Какие примеры библиотек реализуют DOD ECS, а какие нет? Наивный код с итерацией по всем сущностям и проверкой компонентов на лету я увидел, но в реальности такого не встречал, такое разве что в каких-то самопальных решениях есть.
Компоненты по ссылкам видел только в Entitas (C#). LeoEcs, Morpeh, DOTS, Flecs (C++), Bevy (Rust) и тонна других решений независимо от языка данные хранят плотно, имеют запросы/фильтры и т.д. Значит ли это, что все популярные решения являются DOD ECS по умолчанию? Значит ли это, что используя их мы уже пришли к DOD?
Я ожидал например увидеть сравнение архиетипных и спарс-сетных ECS, потому что это плоскость в которой разные реализации как раз таки сильно отличаются - скоростью разных операций, организацией данных под кеш, сложностью, гибкостью и удобством, что в целом можно обозначить как "больше/меньше заточен под DOD", или как то, что некоторые библиотеки намеренно продают какую-то часть своей дата-ориентированности в угоду простоте и удобству.
Подход проверен на пет-проекте и в бою не использовался, зато идёт реклама "преимуществ". В таком случае я бы сказал, что статья имеет сугубо развлекательный характер, но тогда непонятно зачем подход так пиарится. Учитывая, что статью может прочитать новичок, который не умеет отличать хорошее от плохого, было бы более этично в самом начале написать пометку "использовать для реальных проектов на свой страх и риск, либо не использовать вовсе".
Если говорить про сам подход, то он мало в чём выигрывает у ECS, я бы даже сказал, что он во многом проигрывает, даже обычному ООП.
Подход берёт у ECS несколько частей, кроме самых важных, например фильтры, которые фактически бесплатно дают возможность работать с множествами сущностей по набору компонентов.
Так же подход имеет заведомо медленную и наивную реализацию для хранения данных. Я даже не буду говорить про скорость ECS и заточку под кеш, это не главный плюс ECS, но классический ООП вариант без индирекшена на Dictionary и тот будет быстрее, зачем продвигать использование неоптимальных решений для системы, которая является ядром всей игры, я не знаю. С таким подходом фразы а-ля "статический метод можно сделать burst, поэтому использую quaternion" звучат как "дом без фундамента пошёл трещинами, будем использовать скотч".
Раз уж поиск компонентов сделан не по типу, а под каждый тип выделен int, то проще не использовать Dictionary, а например внутри каждой сущности сущности сделать массив object[MaxComponentId + 1], где null это отсутствие компонента (раз уж сделали ссылочную обёртку на примитивы, давайте использовать). Вот так мы изобрели хранение компонентов, как это делает Entitas, массив быстрее создаётся, быстрее работает, а ещё мы либо не мучаем GC на ресайзе Dictionary, либо, если он имеет capacity с запасом, тупо экономим на памяти в несколько раз, и даже логику менять не пришлось, кроме кода отрисовки в редакторе.
Неудобно это смотря с чем сравнивать. SO в любом случае гораздо удобнее, чем хранить данные в монобехах и каждый раз залезать в префаб, чтобы найти нужные данные.
Вот это кстати интересная особенность, что тесты в анриле отрабатывают за минимально возможное время. В юнити на плеймод тестах с запуском сцены такое придётся делать вручную через большой timeScale, что наверное может приводить к багам. Тесты на мультиплеерную игру в юнити вообще не сделаешь, разве что писать свой туллинг поверх какого-нибудь ParrelSync, потому что юнити не умеет запускать второй клиент в рамках одного редактора.
Нужно хотя-бы знать название формул или примерно понимать задачу, тогда уже более менее понятно что искать. Со многими вещами, пока не узнаешь об их существовании, применять их для решения задач не получится. И пока один гуглит через расплывчатые формулировки или ждёт несколько часов негарантированных ответов на форумах, другой уже решил задачу.
Многие фичи в играх требуют хоть какого-то знания математики, и если на каждую фичу искать библиотеку, то максимум что ты сможешь это собрать из готовых ассетов клон клона какой-то игры, а устроиться в студию со своим хоть сколько-то сложным продуктом вряд-ли, делать игру без помощи тех-лида тоже не сможешь, как и сам стать техлидом.
Про 90% это перегиб. Если под навыком имеется в виду владение каким-то языком, умение писать код, знание практик, умение решать проблемы, а не знание конкретных API или конкретных библиотек, то общего там гораздо больше. Я сомневаюсь, что свитч между бэком/фронтом/мобайл сильно проще.
Про зарплаты, да, разница есть, но зато ты делаешь игры, а в других областях ты игры не делаешь.
Имхо, плохая идея. Я как программист не чувствую себя достаточно компетентным в вопросе геймдизайна, у меня другие обязанности, к тому же как и другая часть команды я могу не быть целевой аудиторией проекта, чтобы предлагать хорошие идеи. Хоть мы и работаем на благо проекта, мои интересы как программиста могут разниться с интересами геймдизайнера. Не зря мы часто слышим шутки о том, что приходит геймдизайнер и программисты начинают всё переделывать из-за новых требований.
По-моему последнее решение в данном вопросе должно приниматься одним человеком, геймдизайнером, потому что он отвечает за удачные и неудачные идеи, а не вся команда и все голосовавшие. Выслушать, прислушаться или аргументированно отказать, это хорошая практика, но перекладывать ответственность на всю команду идея так себе, и все должны это понимать, а не обижаться.
Спасибо за очень интересную и познавательную статью. В чём-то укрепил своё мнение касательно Rust. Ещё Джонатан Блоу на своих стримах говорил, что Rust будет сильно мешать на этапе прототипирования, и что если бы он писал Braid и Witness на Rust, то из-за потери в скорости итерации на 10-15% его игры просто не дожили бы до релиза. Данная статья подобное рассуждение по сути подтверждает.
Отличается очень многим, как минимум своей структурой и способом выполнения. FSM - граф состояний и переходов. В момент времени состояние (стейт) одно. BT - дерево условий и действий. В минимальном наборе идёт Sequence (И) и Selector (ИЛИ), а также действия (через них же сделаны условия), который возвращают статусы (Success, Failure, Continue). Выполняется сверху вниз слева направо до первого Failure/Success статуса.
С машиной состояний у тебя будут условные стейты Chase, Attack, Wander, где внутри будут проверки, последовательности действий и прерывания. Дальше внутри или отдельно от стейтов будет логика переходов между ними.
С деревом у тебя будет рут с Selector от которого идёт Sequence с условиями и действиями атаки, потом Sequence с условиями и действиями погони, потом Sequence блуждания. То есть логика будет формально та же, ты можешь выразить те же самые состояния, но дерево будет каждый кадр пересчитываться, действия могут возвращать Continue и выполняться в течении времени, могут прерываться по каким-то условиями и переходить к менее или более приоритетным действиям, могут выполняться параллельно. Выглядит и работает это как более читаемое нагромождение if-else с маленькими функциями.
Скорее особенность самого Godot, хотя подход от Unity и Unreal в целом не сильно отличается.
Я по большей части работал в Unity и тут есть сцена, объекты, префаб (объект заготовка), и компоненты на объектах. В Unreal есть миры, акторы, компоненты акторов, блюпринты (префабы). В Godot все объекты на сцене является нодой, сама сцена это коллекция нод, префабы это по сути тоже сцена, объекты это ноды, и компоненты это тоже ноды.
В подходе Godot наверное есть плюс, потому что в Unity часто нет большой разницы между тем чтобы загрузить дополнительную сцену с уровнем, или же просто собрать уровень в префабе и создать его, хотя workflow с ними отличается и сцена и префабы это вещи не взаимозаменяемые, как и объекты с компонентами. А в Godot получается что всё единообразно, то есть у тебя не несколько видов кирпичей, а один универсальный.
Причём тут Rule-based? Это просто самописная компонентая модель, к тому же медленная и неоптимальная, с единственной разницей, что обновляется один компонент (Rule) за тик. Можно переименовать это в Action/Decision/Behaviour/Component-based AI и смысл вообще никак не изменится. По запросу Rule-based выдаёт множество статей про machine learning, по играм инфы мало, а если и есть, то там совсем про другое. Тут либо что-то недопоняли и переврали, либо придумали что-то своё, но дали этому название вводящие в заблуждение, так ещё и с реализацией накосячили.
Мне во интересно, статья про "лучше движки". Откуда тогда здесь Unreal, и если есть Unreal, то почему тут нет Unity? По-моему брать Unreal под 2D игры это оверкилл в принципе, потому что это не 2D движок изначально, но раз уж добавили, то чем Unity так отстаёт? Даже если вся статья строится только на личном опыте, то тогда откуда такой заголовок?
Если у меня в игре много "потребителей" инпута, то я бы сделал интерфейс IInputListener и сервис InputListenerProvider, всё. Зачем мне делать 4 доп. класса не знаю. Получилось бы очень похоже, есть контроллер и логика, и есть третья сущность, про которую контроллер и логика знают, и которая обеспечивает между ними связь. Только в данном случае эта третья сущность не EventBus, а маленький провайдер, отдающий интерфейс, и я могу по имплементациям данного интерфейса сразу оказаться в методе обработчике, а не искать по usageам где у меня используется мой MoveMessage и где на него идёт подписка. Вышло длиннее? Не думаю. Потому что в примере из статьи не разобран главный кейс для которого нам в принципе нужна абстракция, а именно ситуация когда потребителей инпута может быть много. В таком случае при смене обработчика нам надо одним классом отписаться, затем другим классом подписаться, вместо простого вызова InputListenerProvider.ReplaceListener(this) в классе, который хочет слушать инпут. Ну да, теперь какой-то слой логики знает про InputListenerProvider, но от того, что этот же слой будет знать про MoveMessage фактическая зависимость от инпута никуда не уйдёт, просто станет неявной.
Касательно паттернов. Из "промежуточных классов" я увидел только "адаптер", и может какой-нибудь "мост", но у этих паттернов есть своё применение. Причём все эти паттерны работают по прямому вызову методов, то есть моя логика знает и зависит от "стратегии" или от той же "фабрики", поэтому размывания фактически нет. Размыть зависимости может неправильное использование паттерна "наблюдатель", из которого EventBus по факту и появился, а всё остальное может размыть логику только если очень постараться, например если писать +4 класса на какую-то простую логику. Я же не против умеренного декаплинга, абстракций, переиспользования логики, умеренного соблюдения SRP, или тех же ивентов, да я даже не против самого EventBus, но имхо использовать его как в статье я бы не стал и считаю ошибочным.
В том же видео вообще пример с множеством подписчиков и айдишниками, что неоптимальный и крайне странный подход, но я так понимаю это будет в следующей статье, поэтому ладно уж. Про скорость передачи сообщений через EventBus, сравнение строк и мусор при отписках я тоже говорить не буду, это меньшая из проблем.
И при этом ни разу не "размыли логику" или не "спрятали зависимости"
Почему не размыли и не спрятали, если сделали и то и другое по факту? Какой код тогда имеет размытые зависимости, если с этим кодом всё в порядке?
И второй момент, раз EventBus настолько классный, что через него предлагается дробить игровую логику на некие шаги, то появляется вопрос, а насколько это легко дебажить, искать ошибки при отсутствующих подписчиках например, и в целом производить навигацию по коду?
Данный код не сработает: "
Input.GetKeyDown(KeyCode.Space) += Jump;
" Статью случайно не GPT писал?)Что показывает "Пример использования событий и делегатов" в разделе "оптимизация скриптов"? Что там оптимизируется?
"Избегайте использования коллекций, которые часто изменяют свой размер, таких как
List<T>
. Вместо этого используйте массивы илиQueue<T>
с заранее заданным размером." Странная формулировка, почему не List с заранее заданным размером? В примере с ObjectPool это можно было бы кстати учесть.Наверное мелочь, но
GeometryUtility.CalculateFrustumPlanes
можно было бы закешировать в переменную перед циклом, а во-вторых использовать версию принимающую массив, а не создающую каждый раз новый. Понимаю, что суть примера была не в этом, но статья про оптимизацию всё-таки.За 3 года опыта побывал на 5 проектах, бывали плотные дедлайны, кранчи были пару раз, оба в течении недели, один хорошо оплатили. Чтобы меня кто-то под дулом пистолета заставлял перерабатывать, не помню, не можешь - не работаешь.
Это конечно анекдот, а не статистика, но "кранчат ли у вас" это не вопрос, который я задаю на собесах, поэтому хватит пугать новичков.
Насчет поддержки не знаю, возможно соглашусь. Автор цитаты которых я приводил, не согласились бы, Майк Актон говорил, что логика становится заметно проще, 0 абстракций, прозрачный алгоритм, но это ладно, я не настолько опытен, чтобы судить.
Касательно Unreal, умные указатели там есть, но из того, что я видел, работая с ним, часто используются сырые, потому что в движке используется сборщик мусора. В Unigine в плюсовой апишке тоже сырые, и погуглив тоже нашёл информацию про сборщик мусора. В новом O3DE всякие Entity/Component тоже по сырым (вроде без GC). Умные указатели видел только в недавнем UltraEngine. Так что все флагманы стараются от умных указателей уйти, пусть даже в сборку мусора.
Я по работе пишу на Unity и C#, с низкоуровневой разработкой игр и движков знаком, но не сильно, только учусь (язык Odin). Но смотря разные видео-лекции, от того же Кейси Муратори, Майка Актона (знаменитый доклад по DOD), или Джонатана Блоу. Все они топят против умных указателей и против массивов указателей в целом.
Мол делай плотно упакованные массивы данных, вместо указателей используй индексы, смотри не на отдельные объекты, а на множества объектов, используй кастомные аллокаторы вместо new, аллоцируй и переиспользуй большие блоки памяти, и твой код будет работать в 5-10 раз быстрее, при тех же усилиях, а умные указатели отпадут за ненадобностью.
Что думаете про подобный подход? Писать как говорят джентельмены выше на практике слишком сложно? Или если оверхед небольшой, то и ладно?
Unity по цене меняется только в самой свежей версии, а то что хуже и хуже, то чем же хуже? Я пробовал Unreal, инструмент конечно мощный, но в плане удобства и скорости разработки он Unity сильно проигрывает, поэтому я бы не стал однозначно топить за какой-либо из движков.
Спасибо за статью.
Хотя я увидел, что статья не затрагивает конкретные реализации, без них статья ощущается какой-то неполной.
Какие примеры библиотек реализуют DOD ECS, а какие нет?
Наивный код с итерацией по всем сущностям и проверкой компонентов на лету я увидел, но в реальности такого не встречал, такое разве что в каких-то самопальных решениях есть.
Компоненты по ссылкам видел только в Entitas (C#). LeoEcs, Morpeh, DOTS, Flecs (C++), Bevy (Rust) и тонна других решений независимо от языка данные хранят плотно, имеют запросы/фильтры и т.д.
Значит ли это, что все популярные решения являются DOD ECS по умолчанию? Значит ли это, что используя их мы уже пришли к DOD?
Я ожидал например увидеть сравнение архиетипных и спарс-сетных ECS, потому что это плоскость в которой разные реализации как раз таки сильно отличаются - скоростью разных операций, организацией данных под кеш, сложностью, гибкостью и удобством, что в целом можно обозначить как "больше/меньше заточен под DOD", или как то, что некоторые библиотеки намеренно продают какую-то часть своей дата-ориентированности в угоду простоте и удобству.
Подход проверен на пет-проекте и в бою не использовался, зато идёт реклама "преимуществ". В таком случае я бы сказал, что статья имеет сугубо развлекательный характер, но тогда непонятно зачем подход так пиарится.
Учитывая, что статью может прочитать новичок, который не умеет отличать хорошее от плохого, было бы более этично в самом начале написать пометку "использовать для реальных проектов на свой страх и риск, либо не использовать вовсе".
Если говорить про сам подход, то он мало в чём выигрывает у ECS, я бы даже сказал, что он во многом проигрывает, даже обычному ООП.
Подход берёт у ECS несколько частей, кроме самых важных, например фильтры, которые фактически бесплатно дают возможность работать с множествами сущностей по набору компонентов.
Так же подход имеет заведомо медленную и наивную реализацию для хранения данных. Я даже не буду говорить про скорость ECS и заточку под кеш, это не главный плюс ECS, но классический ООП вариант без индирекшена на Dictionary и тот будет быстрее, зачем продвигать использование неоптимальных решений для системы, которая является ядром всей игры, я не знаю.
С таким подходом фразы а-ля "статический метод можно сделать burst, поэтому использую quaternion" звучат как "дом без фундамента пошёл трещинами, будем использовать скотч".
Раз уж поиск компонентов сделан не по типу, а под каждый тип выделен int, то проще не использовать Dictionary, а например внутри каждой сущности сущности сделать массив object[MaxComponentId + 1], где null это отсутствие компонента (раз уж сделали ссылочную обёртку на примитивы, давайте использовать).
Вот так мы изобрели хранение компонентов, как это делает Entitas, массив быстрее создаётся, быстрее работает, а ещё мы либо не мучаем GC на ресайзе Dictionary, либо, если он имеет capacity с запасом, тупо экономим на памяти в несколько раз, и даже логику менять не пришлось, кроме кода отрисовки в редакторе.
Неудобно это смотря с чем сравнивать. SO в любом случае гораздо удобнее, чем хранить данные в монобехах и каждый раз залезать в префаб, чтобы найти нужные данные.
Вот это кстати интересная особенность, что тесты в анриле отрабатывают за минимально возможное время.
В юнити на плеймод тестах с запуском сцены такое придётся делать вручную через большой timeScale, что наверное может приводить к багам.
Тесты на мультиплеерную игру в юнити вообще не сделаешь, разве что писать свой туллинг поверх какого-нибудь ParrelSync, потому что юнити не умеет запускать второй клиент в рамках одного редактора.
Нужно хотя-бы знать название формул или примерно понимать задачу, тогда уже более менее понятно что искать.
Со многими вещами, пока не узнаешь об их существовании, применять их для решения задач не получится.
И пока один гуглит через расплывчатые формулировки или ждёт несколько часов негарантированных ответов на форумах, другой уже решил задачу.
Многие фичи в играх требуют хоть какого-то знания математики, и если на каждую фичу искать библиотеку, то максимум что ты сможешь это собрать из готовых ассетов клон клона какой-то игры, а устроиться в студию со своим хоть сколько-то сложным продуктом вряд-ли, делать игру без помощи тех-лида тоже не сможешь, как и сам стать техлидом.
Про 90% это перегиб. Если под навыком имеется в виду владение каким-то языком, умение писать код, знание практик, умение решать проблемы, а не знание конкретных API или конкретных библиотек, то общего там гораздо больше. Я сомневаюсь, что свитч между бэком/фронтом/мобайл сильно проще.
Про зарплаты, да, разница есть, но зато ты делаешь игры, а в других областях ты игры не делаешь.
Имхо, плохая идея.
Я как программист не чувствую себя достаточно компетентным в вопросе геймдизайна, у меня другие обязанности, к тому же как и другая часть команды я могу не быть целевой аудиторией проекта, чтобы предлагать хорошие идеи.
Хоть мы и работаем на благо проекта, мои интересы как программиста могут разниться с интересами геймдизайнера. Не зря мы часто слышим шутки о том, что приходит геймдизайнер и программисты начинают всё переделывать из-за новых требований.
По-моему последнее решение в данном вопросе должно приниматься одним человеком, геймдизайнером, потому что он отвечает за удачные и неудачные идеи, а не вся команда и все голосовавшие.
Выслушать, прислушаться или аргументированно отказать, это хорошая практика, но перекладывать ответственность на всю команду идея так себе, и все должны это понимать, а не обижаться.
Спасибо за очень интересную и познавательную статью.
В чём-то укрепил своё мнение касательно Rust.
Ещё Джонатан Блоу на своих стримах говорил, что Rust будет сильно мешать на этапе прототипирования, и что если бы он писал Braid и Witness на Rust, то из-за потери в скорости итерации на 10-15% его игры просто не дожили бы до релиза.
Данная статья подобное рассуждение по сути подтверждает.
Отличается очень многим, как минимум своей структурой и способом выполнения.
FSM - граф состояний и переходов. В момент времени состояние (стейт) одно.
BT - дерево условий и действий. В минимальном наборе идёт Sequence (И) и Selector (ИЛИ), а также действия (через них же сделаны условия), который возвращают статусы (Success, Failure, Continue). Выполняется сверху вниз слева направо до первого Failure/Success статуса.
С машиной состояний у тебя будут условные стейты Chase, Attack, Wander, где внутри будут проверки, последовательности действий и прерывания. Дальше внутри или отдельно от стейтов будет логика переходов между ними.
С деревом у тебя будет рут с Selector от которого идёт Sequence с условиями и действиями атаки, потом Sequence с условиями и действиями погони, потом Sequence блуждания.
То есть логика будет формально та же, ты можешь выразить те же самые состояния, но дерево будет каждый кадр пересчитываться, действия могут возвращать Continue и выполняться в течении времени, могут прерываться по каким-то условиями и переходить к менее или более приоритетным действиям, могут выполняться параллельно.
Выглядит и работает это как более читаемое нагромождение if-else с маленькими функциями.
Скорее особенность самого Godot, хотя подход от Unity и Unreal в целом не сильно отличается.
Я по большей части работал в Unity и тут есть сцена, объекты, префаб (объект заготовка), и компоненты на объектах.
В Unreal есть миры, акторы, компоненты акторов, блюпринты (префабы).
В Godot все объекты на сцене является нодой, сама сцена это коллекция нод, префабы это по сути тоже сцена, объекты это ноды, и компоненты это тоже ноды.
В подходе Godot наверное есть плюс, потому что в Unity часто нет большой разницы между тем чтобы загрузить дополнительную сцену с уровнем, или же просто собрать уровень в префабе и создать его, хотя workflow с ними отличается и сцена и префабы это вещи не взаимозаменяемые, как и объекты с компонентами. А в Godot получается что всё единообразно, то есть у тебя не несколько видов кирпичей, а один универсальный.
Причём тут Rule-based? Это просто самописная компонентая модель, к тому же медленная и неоптимальная, с единственной разницей, что обновляется один компонент (Rule) за тик. Можно переименовать это в Action/Decision/Behaviour/Component-based AI и смысл вообще никак не изменится. По запросу Rule-based выдаёт множество статей про machine learning, по играм инфы мало, а если и есть, то там совсем про другое. Тут либо что-то недопоняли и переврали, либо придумали что-то своё, но дали этому название вводящие в заблуждение, так ещё и с реализацией накосячили.
Мне во интересно, статья про "лучше движки". Откуда тогда здесь Unreal, и если есть Unreal, то почему тут нет Unity? По-моему брать Unreal под 2D игры это оверкилл в принципе, потому что это не 2D движок изначально, но раз уж добавили, то чем Unity так отстаёт? Даже если вся статья строится только на личном опыте, то тогда откуда такой заголовок?
Если у меня в игре много "потребителей" инпута, то я бы сделал интерфейс IInputListener и сервис InputListenerProvider, всё. Зачем мне делать 4 доп. класса не знаю.
Получилось бы очень похоже, есть контроллер и логика, и есть третья сущность, про которую контроллер и логика знают, и которая обеспечивает между ними связь.
Только в данном случае эта третья сущность не EventBus, а маленький провайдер, отдающий интерфейс, и я могу по имплементациям данного интерфейса сразу оказаться в методе обработчике, а не искать по usageам где у меня используется мой MoveMessage и где на него идёт подписка.
Вышло длиннее? Не думаю. Потому что в примере из статьи не разобран главный кейс для которого нам в принципе нужна абстракция, а именно ситуация когда потребителей инпута может быть много. В таком случае при смене обработчика нам надо одним классом отписаться, затем другим классом подписаться, вместо простого вызова InputListenerProvider.ReplaceListener(this) в классе, который хочет слушать инпут.
Ну да, теперь какой-то слой логики знает про InputListenerProvider, но от того, что этот же слой будет знать про MoveMessage фактическая зависимость от инпута никуда не уйдёт, просто станет неявной.
Касательно паттернов. Из "промежуточных классов" я увидел только "адаптер", и может какой-нибудь "мост", но у этих паттернов есть своё применение.
Причём все эти паттерны работают по прямому вызову методов, то есть моя логика знает и зависит от "стратегии" или от той же "фабрики", поэтому размывания фактически нет.
Размыть зависимости может неправильное использование паттерна "наблюдатель", из которого EventBus по факту и появился, а всё остальное может размыть логику только если очень постараться, например если писать +4 класса на какую-то простую логику.
Я же не против умеренного декаплинга, абстракций, переиспользования логики, умеренного соблюдения SRP, или тех же ивентов, да я даже не против самого EventBus, но имхо использовать его как в статье я бы не стал и считаю ошибочным.
В том же видео вообще пример с множеством подписчиков и айдишниками, что неоптимальный и крайне странный подход, но я так понимаю это будет в следующей статье, поэтому ладно уж. Про скорость передачи сообщений через EventBus, сравнение строк и мусор при отписках я тоже говорить не буду, это меньшая из проблем.
Почему не размыли и не спрятали, если сделали и то и другое по факту? Какой код тогда имеет размытые зависимости, если с этим кодом всё в порядке?
И второй момент, раз EventBus настолько классный, что через него предлагается дробить игровую логику на некие шаги, то появляется вопрос, а насколько это легко дебажить, искать ошибки при отсутствующих подписчиках например, и в целом производить навигацию по коду?