на чем написан wot blitz
Создание World of Tanks Blitz на базе собственного движка DAVA
Пролог
Эта история началась более трех лет назад. Наша небольшая компания DAVA стала частью Wargaming, и мы начали обдумывать, какие проекты делать дальше. Чтобы напомнить, каким был мобайл три года назад, скажу, что тогда не было ни Clash Of Clans, ни Puzzle & Dragons, ни многих очень известных сегодня проектов. Mid-core тогда только-только начинался. Рынок был в разы меньше сегодняшнего.
Изначально всем казалось, что очень хорошей идеей будет сделать несколько мелких игр, которые бы привлекали новых пользователей в большие «танки». После ряда экспериментов оказалось, что это не работает. Несмотря на отличные конверсии в мобильных приложениях, переход от мобильного телефона к PC оказывался пропастью для пользователей.
Тогда в разработке у нас находилось несколько игр. Одна из них носила рабочее название «Sniper». Основной геймплей-идеей была стрельба в снайперском режиме из стоящего в обороне танка, по другим танкам, которыми управлял AI и которые могли атаковать в ответ.
В какой-то момент нам показалось, что стоящий танк — это очень скучно, и за неделю мы сделали прототип мультиплеера, где танки уже могли ездить и атаковать друг друга.
С этого все и началось!
Когда мы начинали разработку “Снайпера”, то рассматривали технологии, которые тогда были доступны для мобильных платформ. На тот момент Unity был еще на достаточно ранней стадии своего развития: по сути, необходимых нам технологий еще не было.
Основной вещью, которой нам не хватало, был рендеринг ландшафта c динамической детализацией, что является жизненно необходимым для создания игры с открытыми пространствами. Было несколько сторонних библиотек для Unity, однако их качество оставляло желать лучшего.
Также мы понимали, что на C# мы не сможем выжать максимум из устройств, под которые мы разрабатываем, и всегда будем ограничены.
Unreal Engine 3 тоже не подходил по ряду похожих причин.
В итоге, мы решили дорабатывать свой движок!
Он на тот момент уже использовался в наших предыдущих казуальных проектах. Движок имел достаточно хорошо написанный низкий уровень работы с платформами и поддерживал iOS, PC, Mac, плюс были начаты работы по Android. Было написано много функциональности для создания 2D-игр. То есть, был неплохой UI и много всего для работы с 2D. В нем были первые шаги по 3D-части, так как одна из наших игр была полностью трехмерной.
Что у нас было в 3D-части движка:
Начало работ
Началось все с доказательства возможности отрисовать ландшафт на мобильных устройствах: тогда это были iPhone 4 и iPad 1.
После нескольких дней работы мы получили вполне функциональный динамический ландшафт, который работал довольно сносно, требовал где-то 8MB памяти и давал 60fps на этих устройствах. После этого мы начали полноценную разработку игры.
Прошло около полугода, и маленький мини-проект превратился в то, чем сейчас является Blitz. Появились совершенно новые требования: MMO, AAA-качество и другие требования, которые движок в его изначальном виде на тот момент уже не мог обеспечить. Но работа кипела полным ходом. Игра работала и работала неплохо. Однако производительность была средней, объектов на картах было мало, и, собственно, было множество других ограничений.
На этом этапе мы начали понимать, что фундамент, который мы заложили в движок, не выдержит пресса реального проекта.
Как все работало на тот момент
Вся отрисовка сцен была основана на простой концепции Scene Graph.
Основной концепции являлись два класса:
Первые шаги по улучшению ситуации
Для начала мы решили полечить проблемы с производительностью и сделать это быстро.
Собственно, сделали мы это, введя дополнительный флаг NEED_UPDATE в каждой ноде. Он определял, нужно ли такой ноде вызывать Update. Это действительно повысило производительность, но создало целый ворох проблем. Фактически код функции Update выглядел вот так:
Это вернуло нам часть производительности, однако началось много логических проблем там, где их не ждали.
LodNode, и SwitchNode — ноды, отвечающие, соответственно, за переключение лодов (по расстоянию) и переключение объектов (например, разрушенных и неразрушенных) — начали регулярно ломаться.
Периодически тот, кто пытался исправить поломки, делал следующее: отключал NEED_UPDATE в базовом классе (ведь это было простое решение), и совершенно незаметно FPS опять падал.
Когда код, проверяющий флаг NEED_UPDATE, был закомментирован раза три, мы, решились на радикальные перемены. Мы понимали, что сделать все сразу у нас не получится, поэтому решили действовать поэтапно.
Самым первым шагом было заложить архитектуру, которая позволит в перспективе решить все возникающие у нас проблемы.
Комбинирование компонентного и data-driven-подхода
Решением этой проблемы стал компонентный подход, комбинированный c data-driven подходом. Дальше по тексту я буду употреблять data-driven-подход, так как не нашел удачного перевода.
Вообще понимание компонентного подхода у многих людей самое разное. То же — и с data-driven.
В моем понимании, компонентный подход — это когда некая необходимая функциональность строится на основе независимых компонентов. Самый простой пример — это электроника. Есть чипы, у каждого чипа есть входы и выходы. Если чипы подходят друг к другу, их можно соединить. На базе такого подхода построена вся индустрия электроники. Есть тысячи разных компонентов: соединяя их друг с другом, можно получать совершенно разные вещи.
Основные плюсы этого подхода в том, что каждый компонент изолирован, и с большего независим. Я не беру во внимание тот факт, что на компонент можно подать неправильные данные, и плата сгорит. Плюсы этого подхода очевидны. Сегодня можно взять огромное количество готовых чипов и собрать новое устройство.
Что же такое data-driven. В моем понимании, это подход к проектированию программного обеспечения, когда за основу потока выполнения программы берутся данные, а не логика.
На нашем примере представим следующую иерархию классов:
Код обхода этой иерархии иерархически выглядит так:
В данной иерархии C++ наследования мы имеем три различных независимых потока данных:
Давайте представим, как это должно выглядеть в data-driven подходе. Напишу на псевдокоде, чтобы была понятна идея:
По сути, мы развернули циклы работы программы, сделав это таким образом, чтобы все отталкивалось от данных.
Данные в data-driven подходе являются ключевым элементом программы. Логика — лишь механизмы обработки данных.
Новая архитектура
В какой-то момент стало понятно, что надо идти в сторону Entity-based подхода к организации сцены, где Entity являлась сущностью, состоящей из многих независимых компонентов. Хотелось, чтобы компоненты были полностью произвольными и легко комбинировались между собой.
Читая информацию по этой теме, я наткнулся на блог T-Machine.
Он мне дал множество ответов, на мои вопросы, однако основным ответом было следующее:
• Entity не содержит никакой логики, это просто ID (или указатель).
• Entity знает только ID компоненты, которые ей принадлежат (или указатель).
• Компонент — это только данные, то есть. компонент не содержит никакой логики.
• Система — это код, который умеет обрабатывать определенный набор данных и выдавать на выходе другой набор данных.
Когда я понял это, в процессе дальнейшего изучения различной информации наткнулся на Artemis Framework и увидел хорошую реализацию этого подхода.
Исходники тут, если предыдущий линк не работает: Artemis Original Java Source Code
Если вы разрабатываете на Java, то очень рекомендую посмотреть на него. Очень простой и концептуально правильный Framework. На сегодняшний день он спортирован на кучу языков.
То, чем является Artemis, сегодня называют ECS (Entity Component System). Вариантов организации сцены на базе Entity, компонентов и data-driven достаточно много, однако мы по итогу пришли к архитектуре ECS. Сложно сказать, насколько это общепринятый термин, однако ECS значит, что есть следующие сущности: Entity, Component, System.
Самое главное отличие от других подходов это: Обязательное отсутствие логики поведения в компонентах, и отделение кода в системы.
Этот пункт очень важен в “православном” компонентном подходе. Если нарушить первый принцип, появится очень много соблазнов. Один из первых — сделать наследование компонентов.
Несмотря на гибкость, заканчивается обычно макаронами.
Изначально кажется, что при таком подходе можно будет сделать множество компонентов, которые ведут себя похожим образом, но чуть-чуть по-разному. Общие интерфейсы компонентов. В общем, можно опять свалиться в ловушку наследования. Да, это будет чуть лучше, чем классическое наследование, однако постарайтесь не попасть в эту ловушку.
ECS — более чистый подход, и решает больше проблем.
Чтобы посмотреть на примере, как это работает в Artemis, можете глянуть вот тут.
Я на примере покажу, как это работает у нас.
Главным классом контейнером является Entity. Это класс, который содержит массив компонентов.
Вторым классом является Component. В нашем случае, это просто данные.
Вот список компонентов, используемых у нас в движке, на сегодняшний день:
Третим классом является SceneSystem:
Функции RegisterEntity, UnregisterEntity вызываются для всех систем в сцене тогда, когда мы добавляем или удаляем Entity из сцены.
Функции RegisterComponent, UnregisterComponent вызываются для всех систем в сцене, тогда, когда мы добавляем или удаляем Component в Entity в сцене.
Также для удобства есть еще две функции:
Эти функции вызываются, когда уже создан заказанный набор компонентов с помощью функции SetRequiredComponents.
Например, мы можем заказать получение только тех Entities, у которых есть ACTION_COMPONENT и SOUND_COMPONENT. Передаю это в SetRequiredComponents и — вуаля.
Чтобы понять, как это работает, распишу на примерах, какие у нас есть системы:
В практически любой системе код выглядит следующим образом:
Системы можно классифицировать по тому как они обрабатывают объекты:
Соответственно, если есть желание можете заходить и смотреть на нашу имплементацию в деталях.
Учитывайте тот факт, что все писалось в реальном проекте, и, конечно, это не академическая реализация.
Планы на будущее:
Движок игры
На каком языке в основном написан Клиент?
На каком языке в основном написан Сервер?
Какие программы вы используете на сервере игры?
Какие языки вы использовали при написании Сервера/Клиента?
Интересно что вы ответите
Do you know the way
Победителям Великой Отечественной:
Спасибо Вам за тишину, за наше небо голубое.
За то, что в страшную войну,
Сумели Мир Прикрыть Собою.
Xiaomi Mi Max 6.44″ . G o o g l e Android 6, Miui 8.
Как устроен балансировщик команд в World of Tanks Blitz
WoT Blitz — это мобильный танковый шутер, в котором игроки сражаются в формате 7 на 7.
Матчмейкер, или балансировщик это механизм, который на основе очереди игроков, желающих попасть в бой, формирует состав команд.
У танков есть следующие важные для матчмейкинга параметры:
Требования
Список требований к балансировщику был сформирован на основе фидбека от игрового сообщества и периодически обновляется по сей день.
На момент написания статьи для обычных боёв список состоял из следующих пунктов:
Разработать матчмейкер, особенно с учётом такого количества ограничений, — очень интересная задача. И подходов к её решению может быть довольно много.
Балансировщик формирующий пары игроков
Изначально в мобильных танках использовался балансировщик, доставшийся ему от большого брата — танков десктопных. В целом он работал довольно хорошо, но у него было несколько проблем: во-первых, он не давал чётких гарантий по удовлетворению поставленных требований; во-вторых, добавить новые требования было довольно сложно.
Начало боя
Поэтому был написан другой балансировщик, который работал по следующему алгоритму:
В начале всё работало хорошо. Но со временем, чем больше правил добавлялось, тем сложнее было написать перебалансировку. Новые перебалансировки должны были в результате своей работы не поломать работу предыдущих. Стало понятно, что это путь в никуда.
Баг в матчмейкере — собралась команда 9 на 9
Балансировщик на основе имитации отжига
В конечном варианте мы остановились на балансировщике, который работает по следующему алгоритму. Все игроки, которые нажимают кнопку «В бой», попадают в общую очередь. Балансировщик в бесконечном цикле делает следующее:
Поиск глобального максимума методом имитации отжига
В контексте применения к формированию команд алгоритм следующий:
Первое правило реализовано путём модификации оценочной функции: был добавлен штраф за превышение максимально допустимой разницы в рейтинге. Второе правило реализовано путём изменения функции, которая вытаскивает подходящих игроков из очереди.
Недостаток данного подхода — медленная скорость работы. По сравнению с предыдущим вариантом, текущий стал работать приблизительно в 10 раз медленней, даже несмотря на ряд оптимизаций. Кстати, про оптимизации. Большая часть сервера (кроме сети и физики) для игры написана на Python. Балансировщик был переписан на C++ и распараллелен на много потоков. Из Python в плюсовый код прилетает запрос на формирования команды. Далее каждый из потоков независимо стартует метод отжига. Как только какой-то поток находит решение, остальные потоки останавливают процесс поиска, и найденное решение возвращается в Python.
Время ожидания и размер очереди на RU сервере (5 секунд в обычных боях и 10 в рейтинговых)
По мере роста онлайна росла и нагрузка на балансировщик. Этой осенью, когда онлайн на RU сервере добрался до 120 тысяч (во время ивента Mad Games), балансировщик перестал справляться. В качестве временной меры мы отключили часть правил, это позволило уменьшить нагрузку. Чтобы избежать подобных проблем в будущем мы сделали матчмейкер распределённым.
Рейтинговая система
Лучшие игроки в бриллиантовой лиге, 21 апреля 2019
Во многих ММО играх, кроме случайных боёв, существуют также и рейтинговые / ранговые / etc. Основная идея данного режима: противники ищутся не случайные, а подходящие по скилу. Если ты скиловый игрок, ты будешь играть с такими-же скиловыми игроками, и наоборот, если ты не умеешь играть, ты будешь попадаться против таких же новичков.
В начале сезона игрок проходит серию калибровочных боёв по результатам которых определяется его стартовая позиция. Затем, в зависимости от дальнейшей успешности игры, игрок либо поднимается, либо опускается в рейтинге. Рейтинговая система в Блице создавалась, в первую очередь, для правильной балансировки. Она заточена на скилл игроков и практически не зависит от количества сыгранных игр.
Для реализации рейтинговых боёв понадобилось решить сразу две задачи. Во-первых, нужно было выбрать рейтинговую систему (по какому принципу начислять игрокам рейтинг). Во-вторых, нужно было доработать балансировщик, чтобы он собирал бои с учётом ограничений по рейтингу.
Основное требование к рейтинговой системе — возможность максимально точно определить уровень игрока. Чтобы оценить, насколько точно работает та или иная рейтинговая система, был создан симулятор, на вход которому подавали историю боёв и выбранную рейтинговую систему, а на выходе получали точность работы системы.
Точность считалась следующим образом:
Наиболее популярные системы расчёта рейтинга: winrate, Elo, Glicko, TrueSkill. Winrate — обычный процент побед. Elo — система подсчёта рейтинга, изначально созданная для игр с участием двух человек (шахматы, etc). В этой системе игроку за победу / поражение даётся / отнимается некоторое количество очков в зависимости от рейтинга противника. Glicko в целом похожа на Elo, но кроме этого учитывает, сколько времени игрок был не активен. TrueSkill — запатентованная рейтинговая система от Microsoft, в которой у каждого игрока есть два параметра: рейтинг и уверенность системы в этом рейтинге.
Во время разработки первой версии рейтинговых боёв мы рассматривали winrate и Elo (несколько вариантов, адаптированных к командной игре), а также простую систему Score (в которой игрокам всегда давалось фиксированное количество очков рейтинга за победу и отнималось за поражение).
Наилучшие результаты показала система Elo, в которой Ra — рейтинг игрока, а Rb — разница между суммарным рейтингом команды противника и суммарным рейтингом команды игрока за исключением самого игрока.
Основные трудности, с которыми мы столкнулись после запуска:
Вторую проблему мы решили, введя замедляющий коэффициент (то есть чем дальше игрок от среднего рейтинга, тем сложнее ему подниматься или опускаться ниже).
Также мы пробовали различными способами улучшить качество рейтинговой системы. В первых версиях для изменения рейтинга мы использовали только информацию о победе / поражении. Но у нас командная игра, и не всегда хорошие действия одного конкретного игрока приводят к победе всю команду. То есть даже в случае, если игрок хорошо играл, а команда проиграла, этот игрок получал такой же минус к рейтингу, как и все остальные игроки. Чтобы это предотвратить, мы стали пробовать учитывать индивидуальные действия игрока в бою.
Для этих целей мы пробовали применить машинное обучение: собрали различные факторы и пытались обучить модель предсказывать победу / поражение команды по действиям игрока в бою, чтобы потом использовать эту модель для определения коэффициента бонуса к рейтингу (то есть если команда проиграла, но поведение игрока было похоже на поведение игроков-победителей, давать таким игрокам дополнительный бонус).
Игрок из победившей команды который сыграл хорошо получил +40 к рейтингу. А который плохо всего +10
Мы смогли построить хорошую модель, которая показывала результаты существенно лучше текущей, но возникла одна сложность (которая довольно часто всё портит в задачах машинного обучения). Модель была хорошая, но у неё иногда бывали ошибки, которые хорошо заметны людям. Периодически возникали ситуации, когда игрок, который с точки зрения человека показал хорошие результаты — с точки зрения модели получал низкий бонус, и наоборот.
Заключение
Если у вас есть какие-то вопросы / предложения по балансировщику в WoT Blitz, отписывайтесь в комментариях (или на нашем форуме).
World of Tanks: Blitz
У нас на сайте заработал новогодний парк развлечений «Мир Windows Store»!
И этот материал — его маленькая частичка! Заходите по этой ссылке каждый день, читайте эксклюзивные статьи, смотрите ролики, участвуйте в необычных игровых конкурсах и зарабатывайте жетоны, которые позволят выиграть Xbox One и другие призы!
И самое главное — предпраздничное мандариновое настроение гарантируем!
Близкие по духу
Рев двигателей, оглушительные выстрелы, скрежет металла. Весь мир в труху! Ты пилотируешь здоровенный танк, многотонную машину, воплощение мощи и разрушения. Никто не пробьет многослойную броню, ничто не сможет остановить такую махину.
Примерно так представляет себе World of Tanks не знакомый с игрой человек. На деле все не настолько романтично: хладнокровные танкисты занимают удобные позиции, прячут машины по кустам и ждут, пока какой-нибудь нетерпеливый идиот не высунется и не словит пару-тройку снарядов.
При всей своей доступности и понятности «Танки» не прощают ошибок, а игровые механики заставляют учитывать такие важные штуки, как толщина брони, шанс отскока снаряда и дальность радиопередачи. Победа в World of Tanks достается не смелым, а аккуратным.
И особенно хорошо это ощущаешь, играя в World of Tanks: Blitz — «планшетную» версию танков.
Tank Buster
► Двум T-28 если и спасаться, то только сдав назад, иначе их просто сомнут. | ► Одолеть танки удалось, но самоуверенность до добра не доводит: на открытые пространства лучше в одиночку не соваться. |
Эхо войны
В Wargaming, кажется, это понимают — Blitz не ставит перед собой задачу дословно скопировать World of Tanks. В мобильной версии нет артиллерии, экипаж безлик, карты сделали компактными — на них есть два-три направления и парочка участков, с которых неплохо простреливается почти все пространство.
Правила боя упростили, прятаться все время по кустам смысла нет. В каждой команде по семь танков, а на перестрелку отводится семь минут, хотя обычно пилоты укладываются в три-четыре. Режим игры в Blitz пока всего один — в центре карты необходимо удержать территорию, это и сталкивает лбами противоборствующие стороны. Игроки обычно базу стараются не захватывать — проще полностью уничтожить команду противника, потому что за это дают больше опыта и серебра.
При этом игра не производит впечатления «младшей» версии, это скорее кристаллизация идеи World of Tanks. Wargaming отсекли все лишнее, и у мобильных танков немного другая динамика. Несмотря на общую неторопливость процесса, игроков нарочно стравливают друг с другом — здесь хочется рваться в бой, а не выжидать, тут стремишься обходить противника с фланга или лихо прорываться ему в тыл на каком-нибудь скоростном советском Т-28.
Смерть не вызывает неприятных эмоций — бои скоротечны, танки быстро возвращаются в ангар и заново отправляются на одну из восьми небольших карт.
За звонкую монету
► После каждого боя подводятся итоги, и особо активные танкисты получают медали. | ► Общая статистика тоже никуда не пропала. |
Важный момент: для Blitz используется та же учетная запись, что и для других игр Wargaming, но премиальная валюта не общая и приобретается отдельно. На старте дается совсем немного золота, на него можно купить либо дополнительные места в ангаре, либо эксклюзивные танки — на таких удобно зарабатывать серебро и опыт, который потом можно опять же за золото конвертировать в общий.
Тем не менее без премиальной валюты все равно не чувствуешь себя ущемленным. Во-первых, обычные снаряды дешевы, ситуаций, когда нужно специально кататься на низкоуровневом танке, чтобы заработать на несколько выстрелов из мощной машины, нет. Особый, дорогой, боезапас наносит чуть больше урона, но при желании такие снаряды можно купить и за серебро.
Во-вторых, «золотые» танки нельзя настраивать, а модули там предустановлены. Заменить пушку или поставить новую башню на премиальный танк не получится. Зачастую приятнее кататься на обычных машинах, а при определенном раскладе они даже лучше по характеристикам.
Еще Blitz активно предлагает приобрести подписку, после которой игрок будет получать больше опыта и серебра, — так проще сэкономить время и быстрее добраться до высокоуровневых танков, но это не очень большая проблема.
Отторжения не возникает во многом потому, что играть в WoT: Blitz весело, танки ведут себя по-разному, и даже первоуровневый американский T-1 в умелых руках становится машиной смерти. Само собой, к технике пятого или шестого уровня уже нужно целеустремленно зарабатывать опыт, но ничто не мешает развивать сразу несколько танков и постоянно пересаживаться с одного на другой.
Проект делался без оглядки на привычные для мобильных платформ условно-бесплатные механики вроде таймеров или расходования энергии. Хитрых уловок в духе «купи танк и подожди два часа, чтобы его доставили, а потом еще полчаса потрать на ремонт и диагностику» в игре нет. Единственное ограничение — если танк подбили, сесть в него нельзя до тех пор, пока не закончится матч.
На PC это кажется привычным делом, но для планшетов игра с настолько удобным управлением, высокой production value и не слишком злой системой монетизации — редкость.
WoT: Blitz чудесным образом ухватывает суть «Танков» и доносит ее до еще более широкой аудитории. Для нетерпеливых игроков это отличный вариант познакомиться с «большой» версией. После того, что видишь в Blitz, хочется мигом скачать клиент WoT. и, возможно, вернуться обратно к планшету — кому-то скоротечные бои в мобильной версии покажутся гораздо интереснее.
Этот тот случай, когда все элементы, воспринимавшиеся на PC как данность — управление неторопливыми машинами, сессионность, аккуратная монетизация, — «выстреливают» на мобильной платформе. Похожая история была с Hearthstone, на планшетах она намного привлекательнее. И тоже бесплатна.