на каком движке сделан world of tanks
GUI в игре World of Tanks. Часть первая: эволюция интерфейсов игры
Сегодня мы проведем экскурс в историю развития Graphical User Interface (GUI) в игре World of Tanks.
Игра прошла длинный путь к успеху, и ее GUI менялся и полностью переделывался несколько раз в погоне за повышающимися требованиями армии танкистов, которая неуклонно росла.
Проработав в отделе GUI Programming два с половиной года, я получил представление о том, как развивался сам интерфейс в технологическом плане и как менялись подходы и процессы, это развитие сопровождавшие.
Первые шаги: использование инструментов BigWorld
Начиналось все с того, что в декабре 2008 родилась идея проекта. Все, кто играл в танки, думаю, знают, что первоначальной идеей было сделать игру про эльфов и орков, но, когда хорошенько все продумали, решили остановиться на танках (см. заглавное фото).
Игру начали делать на движке BigWorld, который предоставлял собственный набор инструментов для создания GUI. Мы пошли по пути наименьшего сопротивления и делали первые интерфейсы именно на BigWorld GUI.
Как это работало с точки зрения технической реализации:
hangar.xml — описание блоков UI в ангаре:
account.xml — описание блока с информацией об аккаунте:
styles/common.xml — описание стилей для общих компонентов:
styles/hangar.xml — описание стилей для компонентов в ангаре:
Вроде бы все очень структурировано и понятно. Но, как оказалось, у такого подхода было несколько минусов:
Одна из первых версий боевого интерфейса:
И чуть более поздняя его версия:
Очень быстро стало понятно, что такой подход — тупиковый. Был проведен анализ рынка middleware-решений. Как оказалось, мейнстримом в разработке GUI на тот момент было решение от Scaleform: практически все AAA-проекты использовали его в разработке, и результаты выглядели очень привлекательно.
Предрелизный период: переход на Scaleform
Scaleform предлагал использовать Flash для разработки GUI. По сути, решение состояло из трех частей:
По этой причине в срочном порядке учить Flash начал весь отдел GUI (до этого они делали php, Java и занимались веб-разработкой). Учились и начинали работу на ActionScript 2, так как Scaleform на тот момент еще не поддерживал ActionScript 3. Вот что получалось на первых порах:
За полгода весь интерфейс ангара был переделан на Flash. Как я уже писал, pipeline разработки на Flash — отработанный и логичный процесс. Дизайнеры создают эскизы, и программисты воплощают их в игре.
Весной 2010-го пришел и его черед переходить на Scaleform. Когда это произошло, игровое сообщество разделилось на два лагеря. Одним все нравилось (или они просто не заметили большой разницы) — и они молча продолжали бодро рубиться в танки. Остальные же начали откладывать горы кирпичей в адрес «кровавой картошки», говоря о том, что новые прицелы и элементы интерфейса не соответствуют сеттингу, что не хватает рваного металла, болтов и заклепок, что прицелы должны быть историчными, а не похожими на элементы управления космическим кораблем.
Один из рабочих эскизов нового боевого интерфейса:
Реализация боевого интерфейса на Scaleform:
Но со временем недовольство прошло, так как новые интерфейсы привнесли много нового в геймплей. Игра стала динамичнее, интуитивно понятнее и информативнее.
Помимо этого, использование Scaleform открыло возможности по кастомизации интерфейсов. Любой школьник, умеющий минимально работать с Flash, мог декомпилировать SWF из дистрибутива игры и на свое усмотрение менять все — от используемых изображений и шрифтов, до логики работы кода. Появились моды, заменявшие прицелы на историчные, «куклу» танка на более брутальную или, наоборот, минималистичную. Можно было найти моды для любой части интерфейса в бою. Были моды и для ангара: часы, калькуляторы, многоуровневая карусель и т. д.
Руководство Wargaming несколько раз меняло свое отношение к модам. Сначала, поскольку это были единичные случаи, их просто игнорировали. Со временем и увеличением их числа и популярности — начали присматриваться и поняли, что некоторые из модов могут дать игровое преимущество использующему их игроку. Разработку стали вести по принципу «клиент в руках врага». Это, конечно, не значит, что игроки — наши враги. Нашей задачей стало максимально обезопасить игроков от чужих попыток получить игровое преимущество.
Ситуация на рынке модов стала тщательно мониториться. Теперь в случае обнаружения опасных или изменяющих игровой баланс модов мы оперативно реагируем и закрываем возможность их использования, меняя логику работы клиента. В последнее несколько лет изготовление честных модов поддерживается. По сути, это user generated content — игроки делают эти моды для себя и других игроков, что повышает ценность нашего продукта.
Но вернемся к истории. Работа со Scaleform очень освежила GUI и дала толчок к его развитию в проекте. Функционал разрастался и усложнялся за время прохождения закрытой и открытой беты и выхода проекта в релиз в августе 2010. Добавлялись новые фичи, дорабатывались и наворачивались уже существующие. Менялся дизайн, пробовались разные подходы к представлению информации в игре и организации взаимодействия с игроком.
Варианты реализации фильтра техники:
Пострелиз: проблемы роста и пути их решений
С ростом количества кода и ассетов стали выползать различные косяки.
Маркетинг Scaleform обгонял реальную разработку продукта и, как оказалось, многие из заявленных фич или работали не так, как хотелось, или сильно били по производительности, или вообще были в зачаточном состоянии. Была проделана огромная работа по улучшению производительности Scaleform-плеера, причем как с нашей стороны, так и со стороны разработчиков технологии.
Увеличившийся объем кода приводил к интересному спецэффекту. Каждая вьюха (или окно) лежала в своей FLA, содержала свои ассеты и код и компилировалась в отдельный SWF-файл. Таких SWF было очень много, и на рантайме они подгружались в клиент для показа нужного окошка или элемента управления, и, что характерно, порядок загрузки мог меняться в зависимости от того, что делал пользователь в игре.
Проблема заключалась в том, что если менялся код, который использовался в нескольких SWF, и после изменений не все эти SWF пересобирались, то на рантайме могло произойти следующее. Первой загружалась SWF с устаревшим кодом, и в лучшем случае все работало по старому, а в худшем — происходило падение клиента. Понять, что именно приводит к таким результатам, было трудно. Нам приходилось придумывать инструменты и методики, позволявшие отслеживать, что именно нужно пересобрать после изменений.
Также существовала проблема с качеством и консистентностью кода и использованием различных паттернов и стилей программирования. Так получилось потому, что разработку на Flash в проекте начинали люди, которые не были профессиональными Flash-разработчиками. Они учили Flash «в бою», и у каждого был свой бэкграунд (C++, php, Java). Получалось так, что при работе в разных частях проекта нужно было переключаться с одного подхода на другой.
Еще одной болью было взаимодействие Flash с Python. Передавать данные в любую сторону можно было только в виде примитивных типов, что, конечно же, не удовлетворяло нашим запросам. Путей решения было два: использовать JSON или же раскладывать все сложные типы в длинные массивы на одном конце и собирать из этих массивов объекты на другом.
Первый подход хорошо работал, когда объекты были маленькими. Но при росте размера объектов объемы результирующих строк росли, и это сказывалось на скорости выполнения кода — она падала. Второй подход быстро работал, но был сложен для понимания при прочтении кода и требовал титанических усилий при реализации изменений в структуре данных.
К тому моменту, когда все эти проблемы стали сильно тормозить разработку, Scaleform уже довел поддержку ActionScript 3 до приемлемого уровня. У нас вызревал план перевести интерфейсы ангара на новую версию языка и параллельно провести реструктуризацию проекта и создать свой framework, позволяющий быстро и по определенным правилам добавлять новую функциональность в проект.
Работы по подготовке перехода на ActionScript 3 начались в конце 2012 года. Как мы решали стоящие перед нами проблемы, и какие задачи ставили.
Проблема: проблемы с различными версиями кода в разных SWF.
Решение: весь код приложения вкомпиливается в один SWF-файл, который загружается при старте приложения.
Проблема: коммуникация Flash Python.
Решение: переход на использование Direct Access API. Этот механизм позволяет передавать сложные объекты данных при помощи автоматической сериализации/десериализации их на уровне C++. Также использование этого подхода увеличивает производительность за счет того, что ссылки на Flash-объекты можно передавать в Python и производить манипуляции над ними в Python напрямую, вместо поиска нужного объекта во Flash по полному пути к нему при каждой необходимости передачи данных.
Проблема: стандартизация и унификация кода.
Решение: мы реализовали сервисную инфраструктуру и определили наборы интерфейсов и базовых классов, реализуя которые новая функциональность добавляется в проект.
Проблема: автоматизация сборки и добавления нового функционала в проект.
Решение: для сборки мы используем Maven. Проект был реструктурирован и разбит на более логичные подпроекты и подсистемы. Для автоматизации добавления нового функционала мы использовали YAML в качестве языка для описания интерфейсов взаимодействия Flash и Python. На базе YAML автоматически при сборке генерируется код и создаются необходимые сущности — как во Flash, так и в Python. Все, что остается сделать, это написать код и определить точку входа для запуска новой функциональности.
Так, в сентябре 2013 с выходом версии 8.8 лобби игры было полностью переделано на ActionScript 3.
Вот и все на сегодня. Детали о структуре проекта и планах на будущее читайте в следующей статье.
Создание 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 и — вуаля.
Чтобы понять, как это работает, распишу на примерах, какие у нас есть системы:
В практически любой системе код выглядит следующим образом:
Системы можно классифицировать по тому как они обрабатывают объекты:
Соответственно, если есть желание можете заходить и смотреть на нашу имплементацию в деталях.
Учитывайте тот факт, что все писалось в реальном проекте, и, конечно, это не академическая реализация.
Планы на будущее:
World of Tanks перешла на новый движок, преобразовавший графику в игре
Wargaming.net обрадовала поклонников многопользовательского танкового экшена World of Tanks выпуском крупнейшего обновления под номером 1.0. Его размер составляет 7,89–8,61 Гбайт (в зависимости от версии клиента). Долгожданный фанатами патч содержит множество различных новшеств, ключевым из которых, безусловно, является переход проекта на графический движок Core, который создавался непосредственно самой компанией для своего главного детища.
До сегодняшнего дня World of Tanks была ограничена возможностями движка BigWorld, который не позволял внедрять современные технологии обработки и рендеринга графического контента. Теперь же визуальная составляющая стала более гармоничной, разнообразной и реалистичной. В частности, геймеров ждут дополнительные физические эффекты (разрушаемость объектов, намокание и загрязнение техники) и новый уровень качества рельефа, воды, растительности, освещения, теней, облаков, зданий и других объектов. Над данными изменениями авторы работали больше 4 лет.
Пристальное внимание в Wargaming.net уделили оптимизации игры, которая, по словам авторов, стала самым сложным этапом в переносе World of Tanks на движок Core. Немало трудностей доставило и то, что перед разработчиками стояла задача обеспечить удовлетворительное качество картинки на минимальных настройках графики, которые не должны были сильно измениться в новой версии игры.
Патч 1.0 привнёс в танковый экшен новую локацию «Штиль» и 29 HD-карт, для каждой из которых был записан собственный саундтрек, задающий атмосферу. Всего композиторы Андрей Климка и Андрей Кулик создали более 60 уникальных треков. Также у снаряда каждого типа теперь есть свой характерный звук.
Нельзя не отметить и совершенно новый анимированный ангар для танков. С полным списком изменений, содержащихся в патче 1.0, можно ознакомиться на этой странице.
В заключение разработчики рассказали, что теперь они сосредоточатся на других важных задачах, среди которых баланс, исправление багов, разработка карт, новые игровые нации и игровые модификации.