или как заменить 10+ разработчиков всего одним

Введение в проблему

Процесс разработки можно назвать тогда оптимальным, когда он приносит максимум прибыли при минимуме затрат. Как утверждают классики политэкономии, если одна компания выстраивает все процессы так, что ее продукт получается в два раза дешевле, чем у других, то при прочих равных она получает в два раза больше прибыль, чем другие. И так продолжается до тех пор, пока остальные не введут те же усовершенствования, и ситуация на рынке не выровняется. Цель понятна. Теперь, как этого можно добиться в разработке игр.

Одна из самых больших статей расходов в IT-компаниях — это программисты. К тому же самая сложно управляемая и контролируемая статья. Чисто административные меры тут работают плохо, и чтобы эффективно воздействовать на программистов, нужно самому желательно быть программистом. Поэтому опишем чисто программистские меры и способы по снижению затрат и увеличению производительности.

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

Многоплатформенность

Первая мера, которая лежит на поверхности — это использование кросс-платформенного языка программирования. К таковым относятся: C++, C# (Unity), TypeScript, Haxe, Kotlin, Rust и другие. Сейчас популярны такие платформы:

  • web (Canvas, WebGL),

  • mobile (iOS, Android),

  • desktop (Windows, Mac, Linux),

  • console (PS, Wii, Xbox).

И если для каждой из них писать отдельный порт игры, то можно разориться, так и не дойдя до релиза. Допустим, одна команда делает веб-версию на TypeScript, другая — iOS и Mac на Swift или Objective-C, третья — Android на Java, четвертая — десктоп и консоли на C++, то мы вынуждены оплачивать до четырех команд по 3-5 человек в каждой. Если они используют разные языки, то и действовать они будут совершенно независимо, повторяя друг от друга. Но если изначально выбрать правильный язык, то нам достаточно всего одной команды, плюс, несколько человек для адаптации под разные платформы. Экономия в несколько раз!

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

Второй вид платформ — это социальные сети и другие площадки со своим API. Для них мы создаем в коде отдельный слой сервисов с единым унифицированным интерфейсом для всех таких платформ. Логика нашего приложения вызывает метод getFriends(), а то как эти друзья будут получены, зависит уже от конкретной реализации данного метода. Тогда, если мы сделали игру для VK и хотим перенести ее еще и на OK, то нам не нужно переписывать приложение, а достаточно всего лишь написать новый класс сервиса и подставить его вместо старого. И что особенно приятно, каждый сервис пишется только один раз и используется во всех последующих приложениях. То есть мы получаем значительное расширение аудитории, начиная уже со второй игры, абсолютно бесплатно.

Слои, модули и команды

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

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

Появление логики клиента из отображения
Появление логики клиента из отображения

На первом этапе приложение разделяется на слои логики (Logic) и отображения (View). Затем само отображение делится на логику отображения (View Logic) и код непосредственной визуализации (Display). Последний обычно представлен популярными графическими библиотеками высокого уровня: OpenFL, PixiJS, Phaser и другие. 

Сюда же относятся различные редакторы, вроде Adobe Animate и Construct. Так, OpenFL может использовать ассеты скомпилированные в .swf, а PixiJS — ассеты экспортированные с помощью специального расширения Pixi Animate Extension. В результате нам не нужно заниматься хард кодом графики для расстановки элементов на сцене, а можно поручить все непосредственно художникам, аниматорам, левел-дизайнерам. У нас создается четкое разделение труда, и программисты не отвлекаются на суету вокруг графики, а продолжают писать движки для игр.

Далее. Логика отображения оперирует объектами графических библиотек (DisplayObject, Sprite, MovieClip) и состоит на простейшем уровне из базовых UI-компонентов (Button, Label, List, ComboBox), а на более сложном — из скринов, диалогов и панелей. Первые непосредственно манипулируют визуальными объектами, а вторые — объединяют простые компоненты в единое целое и соединяют их с бизнес-логикой. 

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

Общая MVC-схема нашего клиента
Общая MVC-схема нашего клиента

В этом не сложно убедиться, если провести небольшое исследование игровых жанров, выстроив их по мере увеличения сложности реализации. Обнаруживается, что всех их можно разбить на две основные группы: игры на произвольном поле и игры на упорядоченном поле (условно говоря, поле в клетку). Первые дальше можно разделить на подгруппы: квесты, игры на ставки, карточные. Ко вторым относятся такие игры, как крестики-нолики, шашки, 3-в-ряд, пошаговые стратегии и другие. Нетрудно заметить, что для каждой такой группы можно реализовать один и тот же тонкий клиент! То есть нам достаточно написать клиент для квестов, и останется лишь добавлять классы контроллера, чтобы реализовать новый жанр, вроде поиска предметов, раскраски или одевалки. Логика отображения у них одна и та же.

Опишем для пример модуль для карточных игр (модуль логики отображения). На игровом столе художник в редакторе размещает пустые контейнеры-метки для колоды, отбоя, выложенных на стол карт и карт на руках для каждого игрока. (Каждая метка имеет свой размер и угол поворота, чтобы карты в ней выглядели как следует.) Так вот, вся логика тут — это всего лишь добавление, удаление и перемещение карт от одной метки к другой. Еще нужно располагать (layout) карты внутри самой метки: в линию, веером, стопкой или кучкой, что настраивается в конфигах. Из команд достаточно лишь: add, remove, move и change (с помощью последней можно показывать/скрывать лицевую поверхность карты). (Подробнее о командах и конфигах ниже.)

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

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

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

Подробное устройство клиента
Подробное устройство клиента

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

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

Для сетевого режима один раз реализуется прослойка классов-сервисов (Service), задача которых отправлять и получать команды по сети, чаще всего с помощью сокетов или HTTP-запросов. Внутри сервисы состоят из других подслоев: Protocol, Transport, Parser. Если нужно поменять формат данных (JSON, YAML, XML), достаточно заменить парсер; если нужен другой сетевой протокол (HTTP, TCP, UDP), то берется другая реализация траспорта; если необходимо подправить имя команды, название или значение полей, то переопределяется класс протокола. Таким образом, легко подстроить наш клиент под любой другой сервер, даже если он написан когда-то давно и не нами. Или, наоборот, написан новый сервер под заданный клиент.

Если названия и формат команд выбрать достаточно абстрактными так, чтобы они подходили для всех жанров (вроде add, remove, move, change, и поля: from, to, type, target, etc), тогда мы получим единый протокол для абсолютно всех возможных игр. В этом случае разработчики смогут легко переключаться между проектами, без необходимости адаптироваться к новой системе команд. Каждая игра будет отличаться от других лишь тем, как эти команды выполняются.

Благодаря тому, что для всех платформ (web, mobile, desktop, console) и социальных сетей (fb, vk, ok, mm) используется один и тот же клиент-серверный протокол, все клиенты одного жанра могут подключаться к одному серверу. Это позволяет пользователям легко находить себе подходящего компаньона для игры, не ожидая пока проект раскрутится и соберет достаточную базу пользователей. То есть игра может брать первое время аудиторию из других проектов, пока не наберет свою собственную.

Подробное устройство серверного приложения
Подробное устройство серверного приложения

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

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

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

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

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

Что касается модулей геймплея, то все они наследуются от одного базового модуля, в котором реализуются разные режимы игры: игра на время, на очки, на выживание и другие. Это значит, что все эти режимы уже готовы и автоматически встроены во все текущие и будущие игры. Причем в любом сочетании — достаточно лишь задать нужные настройки: max_time_ms, min_score, max_round, etc, чтобы их активировать. Таким образом, логика и условия окончания игры, а также выявления победителей и проигравших реализуются отдельно от геймплея и только один раз для всех жанров.

Пример конфигов для серверной части
Пример конфигов для серверной части

Все модули выносятся в библиотеки и используются повторно в других приложениях. Таким образом, можно новые игры собирать полностью из одних таких модулей, как в конструкторе Лего. А чтобы в сами модули можно было вносить изменения без необходимости переопределять библиотечные классы, мы все настройки можем задавать во внешних конфигурационных файлах (YAML, JSON, XML). В итоге, проект может состоять всего из одного класса, и то только потому, что для компилятора обязательно наличие точки входа. Весь остальной код находится в библиотеках.

Отдельные конфиги создаются для GUI и структуры приложения, отдельные — для звуков и переводов, отдельные — для баланса игры, платежки, акций и прочего. Благодаря этому не только художники, аниматоры и левел-дизайнеры могут изменять игру, не отвлекая программистов, но к ним присоединяются еще и звуковики, переводчики, гейм-дизайнеры и аналитики. (Последние могут подправлять цены, параметры распределения игроков на когорты и прочие величины, не беспокоя гейм-дизайнеров.) Разработчики занимаются исключительно движками для игр и пишут документацию по использованию конфигов. Наполнением и настройкой игры занимаются все остальные. Этим достигается и вовсе практически абсолютное разделение труда.

Во внешние файлы можно не только вынести настройку приложения и визуальные ассеты, но даже и часть логики. Для разных игровых сущностей несложно реализовать концепцию условий, при соблюдении или несоблюдении которых мы можем показывать или скрывать определенные товары в магазине, показывать акции, давать новый уровень в игре и так далее. Например, мы можем показать специальное предложение, которое будет действовать только в первый день Нового года ({"date": "2023-01-01"}) или в первый час нового дня ({"time.>=": "0:00", "time.<": "1:00"}). Можно даже из общей массы пользователей выделить когорту платящих игроков старше 20 лет с средним чеком больше 20 и уровнем большим 10 ({"user.level.>": 10, “user.age.>”: 20, "payments.hard.count.>": 0, "payments.hard.avg.>": 20}). Такое перетекание части логики из компилируемого кода в конфиги, значительно облегчая труд и гейм-дизайнеров, и разработчиков.

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

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

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

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

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

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

Результаты

Итак, что мы имеем в итоге и что нам это даёт?

Разработка на кросс-платформенном языке программирования и сразу для всех соцсетей открывает нам доступ к максимальной аудитории с уровнем затрат как всего для одной платформы.

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

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

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

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

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

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

UPD про замену 10-ти разработчиков одним:

Арифметика тут простая. Если в компании есть хотя бы 3 проекта по 4 разработчика — по одному для каждой платформы,— то уже на этапе использования одного кросс-платформенного ЯП, мы можем оставить по одному. Это уже минус 9. А если проекты — продолжение предыдущих, из которых были сделаны движки, то больше одного разраба и не нужно. А после релиза в некоторых случаях один программист сможет поддерживать сразу несколько проектов. Во-первых, большая часть кодовой базы общая для всех приложений (отличия только в геймплее, если он разный). Во-вторых, многие изменения в графику, баланс и настройки, включая добавление новых уровней, могут вносить художники, левел- и гейм-дизайнеры без участия программистов.

Приложения

К классификации игровых жанров

Эволюция игровых жанров (с картинками)

Эволюция игрового фреймворка (клиентского и серверного) — цикл из 6-ти статей, на который опирается данный материал.

Исходный код к статьям на Haxe (TypeScript-версия в процессе).

Комментарии (18)


  1. Samhuawei
    08.11.2022 09:10
    +1

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

    Классики политэкономии плохо учили математику.

    П = В - Р

    П1 = В1 - 2*Р
    П2 = В2 - Р

    П2 - 2*П1 = 0

    В2 - Р = 2*В1 - 4*Р

    В2 = 2*В1 - 3Р

    Только при этом условии прибыль второй в два раза прибыли первой.

    Допустим прочие равные означают что В2 = В1.

    3Р = В1

    Даже в этом случае нужно чтобы выручка была равна утроенным расходам.



    1. maluta Автор
      08.11.2022 19:30

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

      Например, если взять случай, когда расходы больше прибыли в 2 раза: В1 = П1 + Р1 = П + 2 * П, и мы уменьшаем их в те же 2 раза, то при равенстве выручек все деньги перетекают в прибыль: В2 = П2 + Р2 = 2 * П + П.

      Хотя формулировка у меня в тексте не очень точная, согласен -- решил не перегружать его оговорками. Классики ни при чем.


  1. BattleAngelAlita
    08.11.2022 10:18
    +4

    Такое ощущение что статья писалась 20 лет назад.


    1. maluta Автор
      08.11.2022 18:07

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


  1. WondeRu
    08.11.2022 11:49
    +2

    А где обещанное: сделать одним и уволить 9 разработчиков?


    1. maluta Автор
      08.11.2022 18:16

      В данной редакции выпало... Но из текста это прямо вытекает. Если в компании есть хотя бы 3 проекта по 4 разработчика -- по одному для каждой платформы,-- то уже на этапе использования одного кросс-платформенного ЯП, мы можем оставить по одному. Это уже минус 9. А если проекты -- продолжение предыдущих, из которых были сделаны движки, тобольше одного разраба и не нужно.


      1. SaharnyMishka
        08.11.2022 22:38
        +1

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


  1. SaharnyMishka
    08.11.2022 18:07

    А что за компания где все так прекрасно работает если не секрет?


    1. maluta Автор
      08.11.2022 18:10

      В том-то и дело, что я не знаю таких компаний, кроме тех, что пишут на C++. Но и там неизвестно, как внутри код построен.


      1. SaharnyMishka
        08.11.2022 21:58
        +1

        Ну только С++ программист стоит дороже C#, который может клепать на той же кроссплатформенной Unity, то есть мы уже повышаем расходы.

        В статье просто накиданы разнородные Best Practices без конкретных условий когда их стоит применять, и если закинуть это все в проект то он гарантированно не доживет до релиза. Нет ответа как заменить 10 программистов на 1, тупо теория как жить лучше, которая моментально разобьется об реалии реализации. Приведу три примера, но их гораздо больше

        1) "Все модули выносятся в библиотеки и используются повторно в других приложениях.".

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

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

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

        3) для дурака или ведьмы — модуль карточных игр. А вот для покера или блэкджека уже не нужно ничего писать — достаточно одновременно использовать два предыдущих модуля.

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

        Можете попробовать и если за два месяца управитесь готов взять свои слова назад. А так статья прям плохая.


        1. maluta Автор
          09.11.2022 15:49

          С++ я не беру в расчет -- это особый случай, просто его нельзя не упомянуть тут.

          Спасибо за C#, забыл добавить.

          В статье просто накиданы разнородные Best Practices без конкретных условий когда их стоит применять

          Так и есть. Это статья -- подведение итогов серии статей про написание фреймворка. Большинство практик там не только раскрываются, но и выводятся теоретически -- с эволюционной точки зрения.

          И да, это "тупо теория" (что плохого в теории?), некий идеал, который если внедрить хотя бы частично, сильно упрощает жизнь.

          По примерам:

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

          2) Спорно. Если сделать качественное разделение функционала на модули, всегда будешь знать, где что выполняется.

          3) Почему нет? На игровом столе размещаем метки для колоды, отбоя, выложенных на стол карт и карт на руках каждого игрока. Каждая метка (пустой контейнер) имеет свой размер и угол поворота, которые выставляет художник. Вся остальная логика -- это всего лишь добавление, удаление и перемещение карт от одной метки к другой. Еще нужно располагать карты в линию, веером, стопкой или кучкой внутри метки (настраивается в конфигах). Из команд достаточно обрабатывать add, remove, move, change (чтобы показывать/скрывать лицо карты). Если рука набита, две недели за глаза. (Включу данный пример в текст для ясности.)

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

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


          1. SaharnyMishka
            09.11.2022 17:59

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

            "Задача программиста в том и состоит, чтобы уметь обобщить все возможные фантазии гейм-дизайнеров в одном простом решении"

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

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

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


            1. maluta Автор
              10.11.2022 15:16

              Около 10 лет разрабатывал игры на ActionScript, начиная еще с Flash 4 и до самого конца (потом перешел на Haxe и бэкенд, теперь еще на TypeScript). Делал игры и приложения для порталов, образования, потом — социальных сетей. Данный подход применялся на протяжении всего этого времени постепенно, по мере своего формирования. Например, игру PerfectPoker (facebook) я переписывал с AS1 на AS3 около двух месяцев, а вот уже последующие аналогичные приложения занимали 1-2 недели — большая часть времени уходила на адаптирование новой графики. Выигрыш в 4 раза, хотя там вводилась только концепция слоев и повторного использования компонентов.

              Позже применял тот же подход на играх 3-в-ряд (ButtonMix, DreamlikeMix и другие). Так, если разработка игры по разным причинам могла затягиваться на несколько лет, то уже после частичного внедрения фреймворка игры начали делаться за полгода. При этом сразу делались и веб- и мобильные версии (Adobe AIR). Все, чем отличались платформы, выносилось в отдельные подслои, поэтому путаницы не было.

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

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

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

              Конечно, фреймворк не подойдет разработчикам AAA-игр: они пишутся на уже развитых движках, вроде UnrealEngine, и вся разработка отталкивается от особенностей этих движков. Но большинства онлайн-игр использование таких монстров избыточно. И для не существует каких-либо универсальных архитектурных решений, которые были бы признаны стандартом. Каждый пишет как может. Я лишь попытался создать такой стандарт для себя, для более комфортной работы "майселфа".

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


  1. svkozlov
    09.11.2022 09:06
    +1

    Работаю с OpenFL и Haxe, и могу сказать что кроссплатформенность имеет свои плюсы и минусы. И не было ни одного проекта, где не приходилось бы решать какие то проблемы, после компиляции на требуемые платформы. А проблемы связаны обычно с требованиями или ограничениями платформы.
    Лет 10 назад мне тоже казалось, что повторное использование кода это круто. Жизнь показала, что уже через пару лет код протухает. Выходят новые версии языков\движков\e.t.c. и лучше выходит написать свежий код который отвечает новым требованиям.
    Сыровата статья. Много выделений жирным.


    1. maluta Автор
      09.11.2022 16:28

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

      Если разделение на слои соблюдается неукоснительно и бескомпромиссно, то код "протухать" не будет.


      1. svkozlov
        10.11.2022 15:46
        +1

        Так я ж не против отделения логики от отображения. Я против универсального кода. Для примера, за 6 лет работы с HAXE вышло две новых версии языка 3 и 4 (не беру в расчет промежуточные). И я не могу оставить приложения на старых версиях, по тому что для мобильных сторов, мне нужны новые фичи языка. Я не хочу обновлять графичексую часть - мне нужно обновить только логику. Но с новой версией языка, я должен лезть в графическую часть, потому что там что то отвалилось(((
        С Unity примерно та же самая беда. Люди заказывали портирование игр с Unity на что угодно, потому что обновить старые проекты до новой версии движка очень больно.


        1. maluta Автор
          10.11.2022 23:06

          Не вижу, как связаны универсальность кода и версия языка. И без фреймворка при изменении языка придется менять и логику, и код для графики. Только с фреймворком это можно сделать один раз для всех игр, а без "универсальности" — для каждой игры отдельно.

          Код — зло. Чем меньше кода, тем лучше. Минимум кода при поточном производстве — это когда все в библиотеках.


  1. mopsicus
    11.11.2022 00:03

    И получите вы 100500 похожих друг на друга игр с разным оформлением. Геймдев это всегда компромисс. Хотите интересную игру, с уникальными механиками, сюжетом и прочим, которая запомнится, которую будут хотеть перепроходить – тогда придется делать долго, кропотливо и вдумчиво. Хотите быстро заработать денег - нашлепайте разных клонов известных игр, накачайте трафиком и быстро срубите с рекламы пока не разбежались. Это конечно немного утрированно, но думаю мысль ясна.

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