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

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

DOD — сделай процессору приятно
Где-то после года разработки на UE4 мы поняли, что предоставляемый Epic Games функционал нас во многом не устраивает. И тогда, в поисках ускорения работы сервера, мы наткнулись на такую штуку как Data-oriented Design.(дизайн ориентированный на данные или просто DoD)

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

MMO-индустрия: Архитектура не мешает геймдизайну?
Как видите, на графике прослеживается сильное отставание скорости доступа процессоров к оперативной памяти, а это значит, что каким бы классным и быстрым не был процессор (CPU), ваша оперативная память (RAM) будет ограничивать его.

Но не расстраивайтесь! Для того, чтобы снизить влияние этого разрыва, производители добавляют немного магии… Ой, нет, немного очень дорогой и быстрой памяти прямо на кристалл CPU. Эта память называется кешем и на сегодняшней день часто имеет аж целых 3 уровня. В характеристиках процессоров обычно пишут что-то вроде: «Объем кеша L1 64 КБ». Да, кеш измеряется в килобайтах, чем выше уровень кеша, тем он более медленный, но имеет немного больший объем.

MMO-индустрия: Архитектура не мешает геймдизайну?
Казалось бы, этой памяти довольно мало, но основная идея такой архитектуры в том, что небольшие блоки данных и программной логики в течение довольно короткого промежутка времени (миллионные доли секунды) могут снова понадобиться. А значит, разумнее держать их как можно «ближе», чтобы не приходилось долго (в сравнении с кешем) ждать их из оперативной памяти.

Таким образом, задача DoD состоит в том, чтобы структурировать данные программы в соответствии с тем, в каком порядке они будут обрабатываться. Это называется «принципом локальности». И в наиболее распространенной на сегодняшний день парадигме программирования, о которой я говорил в прошлой заметке, этот принцип повсеместно нарушается.

MMO-индустрия: Архитектура не мешает геймдизайну?
В чем же проблема? Дело в том, что при создании разнообразных объектов программисты в ООП исходят из принципов нашей человеческой логики, объединяя данные и логику в абстракции, аналогичные объектам реального мира. При этом однородные данные перестают храниться поблизости друг от друга в оперативной памяти, а значит, и в кеше процессора. Кроме того, они начинают занимать там намного больше места, чем могли бы, что приводит к значительным задержкам в обработке этих данных.

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

ECS — данные + воображение
Ну, хорошо — у нас есть DoD, мы имеем четко структурированные данные. А что, собственно, с ними делать? Конечно обрабатывать. И тут нам понадобятся какие-то абстракции, которые будут объединять наши данные для создания всего многообразия игрового мира. Такими абстракциями в нашем случае выступают сущности, компоненты и системы:

Entity-Component-System (ECS)

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

Сущности, в отличии от объектов в ООП, не содержат в себе буквально ничего. В каком-то смысле это просто ID (идентификатор), при помощи которого мы можем отличить одну сущность от другой и понять какие она имеет свойства.

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

Это может показаться не менее сложным, чем та же ООП. Но вот более простое описание того, что делает ECS.
Данные теперь можно представить как огромную таблицу Excel, где сущности — это номера строк, а компоненты — столбцы. В итоге системы занимаются тем, что последовательно обрабатывают изменения данных в столбцах. Согласитесь — звучит очень просто.

MMO-индустрия: Архитектура не мешает геймдизайну?
Еще проще дело обстоит с тем, чтобы ускорить работу этой обработки. Все, что вам нужно сделать, это обрабатывать не все данные в столбце по очереди, а разделить их на группы, соответствующие числу потоков\ядер вашего процессора, и обрабатывать все параллельно. Обработка будет во столько раз быстрее, сколько у вас будет доступных потоков\ядер процессора. И это имеет свои плоды: вы можете полноценно утилизировать производительность даже таких монстров как 64-ядерные процессоры. Я говорю не о каких-то странных синтетических тестах, а о полноценной полезной нагрузке. Это большая редкость для софта вообще, чего уж говорить про игры.

Ну а что же насчет механик? Самым значимым плюсом для ММО, которая написана на базе ECS, является простота сопровождения, что включает и добавление новых механик.

Сопровождение — это процесс улучшения, оптимизации и устранения дефектов программного обеспечения.

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

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

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

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

Кончено, существует во многом похожий простой компонентный принцип на базе ООП, который стал популярным в играх. Он используется и в UE4, и до 2018 версии активно использовался в Unity. Скорее всего, и многие самописные движки тоже используют его. Но у него все еще есть минусы, ограничивающие пластичность, если можно так выразиться. Объекты, которые вы захотите создать в игре, все еще будут шаблонными, они не смогут иметь любые свойства, какие вам захочется, так как компоненты добавляются в процессе написания самого кода. Справедливости ради можно добавить, что существуют способы обойти эти ограничения и в ООП, но подобная архитектура требует еще больших затрат времени и имеет еще большую сложность, не говоря о том, что она нарушает все тот же принцип локальности данных.

В ECS же вы вольны создавать просто немыслимые гибриды сущностей, абсолютно не заботясь об изменениях в архитектуре проекта. Так мы можем создать книгу-NPC, которая может ходить или летать, выдавать квесты, а затем, изменив несколько параметров, превратить ее в предмет, который будет контейнером, содержать в себе какую-то информацию и даст игрокам возможность положить эту сущность в инвентарь. И, конечно, «книга», выполнив все возложенные на нее обязательства, может стать просто книгой, которая имеет только 2-3 свойства в виде простых численных значений. И все это в процессе выполнения программы. Возможность создавать сущности, которые имеют любые свойства прямо в игре абсолютно без ограничений и без необходимости что-то дописывать — это очень удобно, поверьте.

На мой взгляд, не менее воодушевляющим примером может служить компонентный AI. Часто в играх используются так называемые древа поведения, которые, если упрощать, определяют: что делает NPC в данный момент времени, что он может сделать после этого, что будет, если произойдут такие-то события. Тут мы столкнемся с тем, что необходимо заранее определить все возможные варианты развития событий, чтобы наш NPC не тупил на ровном месте. Да, с простым компонентным подходом мы можем написать планировщик, который будет оценивать свойства этого NPC (или его состояния, как вам больше нравится), свойства мира вокруг и планировать свои действия для достижения какой-то цели. Однако, эта система и все ее состояния для конкретного NPC все еще будут жестко определены программным кодом, и после компиляции вы уже ничего не сможете изменить.

MMO-индустрия: Архитектура не мешает геймдизайну?
Что предлагает нам ECS? Нам дается сущность, которую в процессе самой игры мы можем наделить свойствами: сделать NPC торговцем, бандитом, бродягой, а после смерти оставить лишь компонент модели тела и пару других свойств. Но представьте, что тело NPC никуда не денется, на это место придет некромант, который «заменит» компонент одной модели на другую и сделает из нашего трупа NPC-нежить. Так каждое новое добавленное свойство меняет поведение этого NPC, делая его умнее или глупее, изменяя его цели и методы их достижения.

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

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

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

Читайте также

23 комментария

avatar
Лично яб с радостью еще один плюсик заметке поставил бы только за отсылку к статьям Адама Мартина (T-Machine). :)

Любому инженеру, решившему понять семейство ES парадигм, в первую очередь стоит обратиться к статьям и wiki этого человека. Адам занимается развитием этого семейства еще с начала нулевых. Тема CO/ES/ECS является очень старой. Без это всего реализация таких игр как, например, Prototype, стала бы крайне затруднительной.

William_Godwin , мне вот что стало интересно. Какую реализацию компонентного подхода вы решили выбрать для своего проекта?
  • +2
avatar
Мы не стали делать вид, что написание действительно быстрой и удобной ECS нам под силу, так что выбрали очень многообещающий открытый проект, вот ссылочка. Товарища можно поддержать на птреоне, ато там с поддержкой что-то совсем все грустно(
Комментарий отредактирован 2018-12-28 09:36:56 пользователем William_Godwin
  • +4
avatar
Поначалу понравилось, а потом начали так расхваливать ECS, будто пытаетесь мне её продать.
Любая оптимизация, поиск багов и исправления в уже существующих системах очень сильно упрощается за счет полной изоляции.
Если сущности изолированы, как они будут взаимодействовать между собой?
любые взаимодействия могут быть созданы буквально простым добавлением данных в таблицу.
Я так и не понял разницу между добавлением поля в сущности и добавлением поля в обычном объекте. Что там, что там одна строчка кода для поля плюс логика обработки.
Возможность создавать сущности, которые имеют любые свойства прямо в игре абсолютно без ограничений и без необходимости что-то дописывать это очень удобно, поверьте.
Чем это отличается от объектов в JavaScript и других динамических языках? Там тоже можно добавлять любые свойства в процессе выполнения программы. Или обычным HashMap, где ключ — название поля, а значение — любой объект в качестве значения поля? Только производительностью?

В конце статьи ссылка на статью, написанную 11 лет назад, что ECS — это будущее. Теперь это будущее уже наступает?
Комментарий отредактирован 2018-12-28 11:13:52 пользователем Eley
  • 0
avatar
Поначалу понравилось, а потом начали так расхваливать ECS, будто пытаетесь мне её продать.
Ни разу) Я пытался показать преимущества, которые и делают ECS и DoD лучшим выбором для ММО.
Если сущности изолированы, как они будут взаимодействовать между собой?
Сущности не изолированы, скорее даже наоборот, в отличии от ООП у вас вообще нет никакого сокрытия данных. Изолированы и независимы друг от друга системы, а если вам нужно какое-то взаимодействие, то оно осуществляется через изменения компонентов и только так.
Я так и не понял разницу между добавлением поля в сущности и добавлением поля в обычном объекте. Что там, что там одна строчка кода для поля плюс логика обработки.
у сущности нет полей, это просто ID. Вы же, я думаю, видите разницу между созданием объекта с переменной и записью данных в конкретную ячейку таблицы в базе данных.
Чем это отличается от объектов в JavaScript и других динамических языках? Там тоже можно добавлять любые свойства в процессе выполнения программы…
Нарушением принципов локальности данных. С мапой вопрос в том, что она куда проще устроена чем таблицы в стиле RMD.
В конце статьи ссылка на статью, написанную 11 лет назад, что ECS — это будущее. Теперь это будущее уже наступает?
Ну, все сразу не делается ведь. А вообще, можно такой вопрос задать создателям SpatialOS. Что-то мне подсказывает, что они просто умеют в рекламу.))
Комментарий отредактирован 2018-12-28 12:27:57 пользователем William_Godwin
  • +4
avatar
ECS — это просто инструмент. Дело в том, что для того чтобы кто-то хотя-бы просто задумался об использовании инструмента, этот инструмент этому кому-то надо именно продать за интерес. У тебя в руках итак довольно много инструментов, каждый из которых изрядно перегружен в информационном плане. А семейство CO/ES парадигм настолько информационно перегружено, что его буквально надо захваливать чтобы продать кому-либо. CO/ES требуют кардинальной перестройки мышления инженера. Первичное непонимание этих парадигм — это рядовая реакция. :)

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

Теперь все разнообразие объектов определяются только композицией своих данных, связанных вместе через абстрактный идентификатор сущности, и набором универсальных функциональных интерфейсов, каждый из которых понимает только часть общей композиции данных сущности, думая что предоставленная часть композиции — это и есть вся сущность.
  • +9
avatar
я ничего не понял, но люто плюсую!
  • +1
avatar
Отличная заметка! Спасибо!
  • 0
avatar
Не попробовав собственными руками не могу говорить о технической стороне вопроса, уж простите, голова сейчас отказывается от серьезных нагрузок. Поэтому обращу внимание на то, что лежит на поверхности.

Слово «просто» встречается в заметке 13 раз. Корень «прощ» — 5 раз. Однако, из прошлой заметки, и вашего комментария в этой заметке, складывается впечатление, что все совсем уж ни разу не «просто»
Мы не стали делать вид, что написание действительно быстрой и удобной ECS нам под силу, так что выбрали очень многообещающий открытый проект, вот ссылочка. Товарища можно поддержать на птреоне, ато там с поддержкой что-то совсем все грустно(
Вызывает диссонанс.

Есть ли какие-то, эгхм, простые примеры, side-by-side сравнение OOP vs DoD в виде кода?
  • 0
avatar
На тот момент неочень разбирались в теме, эта либа была очень быстрой по синтетике, это был современный стандарт C++, ну и велосипедостроение это, возможно интересно, но не очень продуктивно.

Есть ли какие-то, эгхм, простые примеры, side-by-side сравнение OOP vs DoD в виде кода?

эгхм
И для расширения тематики огромный список разнообразных полезных материалов по DoD.
  • +1
avatar
Я смотрел туда, и даже гуглил. Вы, возможно, не поняли, о чем я спрашиваю.

Мне интересно посмотреть на прямое сравнение. Функция, написанная в рамках ООП, и рядом, такая же, но в DOD. Чтоб можно было наглядно увидеть разницу. 10-20 строчек, что угодно. Может, у вас есть что-то из кода вашего проекта, что вы уже переписывали? Мне интересно именно в сравнении глянуть.
Комментарий отредактирован 2018-12-29 22:23:10 пользователем L0ckAndL0ad
  • 0
avatar
Я дико извиняюсь, но… Вообщем плохо искали. Может это просто усталость, у меня тож так бывает, перерабатывать очень вредно. ^_^

Вот, даже юнити
  • +4
avatar
Спасибо большое. Посмотрю.
  • +1
avatar
Посмотрел всю эту презентацию. На этапе, где они заменили цикл на переменную, а вложенный цикл на обычный, подумал: «Авторы презентации, вы что, меня за идиота держите?». Ясное дело, что если убрать вложенный цикл, то производительность возрастёт в разы. Вот только это ничуть не противоречит ООП. Дальше в презентации аналогично. Если сохранить PositionComponent в переменную, а не искать её каждый раз в списке с проверкой типа, то очевидно, что программа будет работать быстрее.

Единственное, где они отошли от ООП — это на последних слайдах, где вместо списка объектов сделали несколько списков, по одному списку для каждого поля. И это ускорило программу аж 28% в их случае (43ms→31ms update). Улучшение небольшое, и я думаю, что более сложный код не стоит того. Но в некоторых особо сложных ситуациях может помочь.

Выводы, что я сделал: ООП живее всех живых и отлично работает. А если писать лишние циклы, то это будет тормозить при любой парадигме программирования.
  • +1
avatar
Могу тольуо сказать, что вы сделали неправильные выводы. Это странно, ведь довольно конкретно говорится во всех предоставленых документах, презентациях с того же cppcon за многие годы, особенно если смотреть того же Мфйкла Эктона, что:
1)Перечисляются конкретные признаки ООП (объекты это еще не ООП)
2)Объясняется, что использование объектов именно такими методами замедляет работу процессора с данными в 400-700 раз (сами инструкции выполняются со скоростью меньше наносекунды, основным узким местом всегда были и остаются данные)


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

А ООП для хайлоад давно умер, если конечно вы не хотите поспорить с эффективностью реляционной модели данных))
  • +2
avatar
В мире Java всё — ООП, кроме чисел, символов и булевых переменных. Так что, в моём понимании, объекты — это уже ООП.

А для хайлоада и реляционная модель неактуальна, на её место приходит NoSQL, как я понял. И хайлоад для ММО, в принципе, не нужен особо, если у нас 5к человек на сервер. По-моему, тут больше проблема в геймдизайне, чтобы сделать интересную игру, а не в технической реализации. Так что, лучше писать, как проще, и уже когда игровая схема взлетит, тогда и заниматься оптимизацией производительности.
  • 0
avatar
Причем тут чье-то понимание, если есть конкретное описание парадигмы? Ну и java это вообще отдельная тема, никто не будет, если он понимает что делает, писать на этом языке ММО-песочницу.

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

Так что, лучше писать, как проще, и уже когда игровая схема взлетит, тогда и заниматься оптимизацией производительности.
Если вы сайт пилите, может это и прокатит, но писать как попало ММО, а потом все переделывать это какой-то мазохизм, простите.
  • +3
avatar
Java хорошо показывает себя в другом аспекте: скорость написания бизнес логики. Поэтому используется в энтерпрайзе. И то, даже в мире энтерпрайза сильно набирает обороты функциональная парадигма, а тут описывают нечто похожее.
Плюс в том документа про фронтэнд, для бэкэнда свои заморочки будут, но в целом, дизайн ориентированный на данные — может вполне пригодится и там, так как эти проверки нужно повторять. Но это еще нужно подумать.
Но в целом я согласен, это больше просто технология построения связей в программе, чем новая парадигма и замена ООП.
  • +1
avatar
Я ни разу не программист, даже близко.
Но исходя из этого документа, я начинаю понимать почему игры типа Battletech жрут по 9ГБ ОЗУ и тормозят даже в интерфейсах меню сохранения…
Разработчики пошли ленивые и не желающие оптимизировать свои творения.
  • 0
avatar
А что насчет снижения стоимости разработки? Про Unity и UE я слышала, что на них любой школьник может создать собственную игру. Но для работы с ECS наверно нужны более квалифицированные специалисты?
  • +1
avatar
Хмммм… Утверждение со школьникакми неоднозначное. Ну да, есть в анриале блупринты и можно без знаний, вроде как, программирования что-то состряпать. Не думаю, что это показатель.

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

Это логично, если учесть, что веся логика представляет из себя отдельные изолированные блоки, которые занимаются исключительно своим делом, добавляй себе и добавляй.
  • +1
avatar
А, чисто теоретически, можно ли создать готовый движок, который будет использовать DoD?
  • +1
avatar
  • +3
avatar
Спасибо, это внушает некоторый оптимизм. Будет очень здорово, если у разработчиков появится новый инструмент, который убьет сразу двух зайцев: уберет некоторые технические ограничения и сэкономит время программистов. А если создатели Amethyst снизят порог вхождения для пользователей до уровня UE, то можно будет и третьего зайца завалить. :)
  • +1
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.