http://habrahabr.ru/company/wargaming/blog/228309/
Сегодня мы проведем экскурс в историю развития Graphical User Interface (GUI) в игре World of Tanks.
Игра прошла длинный путь к успеху, и ее GUI менялся и полностью переделывался несколько раз в погоне за повышающимися требованиями армии танкистов, которая неуклонно росла.
Проработав в отделе GUI Programming два с половиной года, я получил представление о том, как развивался сам интерфейс в технологическом плане и как менялись подходы и процессы, это развитие сопровождавшие.
Первые шаги: использование инструментов BigWorld
Начиналось все с того, что в декабре 2008 родилась идея проекта. Все, кто играл в танки, думаю, знают, что первоначальной идеей было сделать игру про эльфов и орков, но, когда хорошенько все продумали, решили остановиться на танках (см. заглавное фото).
Игру начали делать на движке BigWorld, который предоставлял собственный набор инструментов для создания GUI. Мы пошли по пути наименьшего сопротивления и делали первые интерфейсы именно на BigWorld GUI.
Как это работало с точки зрения технической реализации:
- декларативно в XML описывалась структура и визуальная часть GUI;
- общий layout для крупной вьюхи — стили самой вьюхи и набор основных блоков, ее составлявших, описанный в XML;
- каждый из блоков описан в отдельном XML с указанием используемых стилей и компонентов. Для компонентов задавались их настройки (именование, локализационные сообщения, ссылки на стили);
- стили описывались в отдельных XML-файлах, где задавались размеры, позиции, используемые текстуры, шрифты, цвета, z-order и бог знает что еще;
- при старте клиента все эти XML-файлы грузились в Python и парсились, после чего начинался процесс создания интерфейсов, их инициализации и подключения к игровой логике.
Вот пример, выдранный из недр SVN-проекта:
hangar.xml — описание блоков UI в ангаре:<hangar>
<styles>
<style>window</style>
<style>hangar:window</style>
</styles>
<childs>
<AccountBlock>
<name>account_info</name>
<import> components/account </import>
</AccountBlock>
<Fitting>
<name>fitting</name>
<import> components/fitting </import>
</Fitting>
...
</childs>
</hangar>
account.xml — описание блока с информацией об аккаунте:<account>
<styles>
<style>hangar:account_info</style>
</styles>
<childs>
<TextLabel>
<name>account_name</name>
<text></text>
<textElideMode>ElideRight</textElideMode>
<styles>
<style>hangar:account_name</style>
</styles>
<toolTip>
<format>#tips:hangar/account_name</format>
</toolTip>
</TextLabel>
<TextLabel>
<name>account_exp</name>
<text>#menu:hangar/account_info/experience</text>
<styles>
<style>hangar:account_exp</style>
</styles>
<toolTip>
<format>#tips:hangar/account_exp</format>
</toolTip>
</TextLabel>
...
</childs>
</account>
styles/common.xml — описание стилей для общих компонентов:<style>
<window>
<bgcolor> 200 200 200 255</bgcolor>
<overlaycolor> 255 255 255 255 </overlaycolor>
<border>
<texture>gui/maps/window_border.tga</texture>
<size> 5 </size>
</border>
<focus>
<bgcolor> 255 255 255 255</bgcolor>
</focus>
</window>
…
</style>
styles/hangar.xml — описание стилей для компонентов в ангаре:<style>
<window>
<color>0 0 0 255</color>
<bgcolor>255 255 255 255</bgcolor>
</window>
<account_info>
<position><x>10</x><y>10</y><z>0.9</z></position>
<height>32</height>
<bgcolor>100 100 100 255</bgcolor>
<color>200 200 200 255</color>
</account_info>
<account_name>
<height>100%</height>
<textAlign>LEFT</textAlign>
<position><x>10</x></position>
</account_name>
<account_exp>
<height>100%</height>
<horizontalAnchor>RIGHT</horizontalAnchor>
<font>default_smaller.font</font>
</account_exp>
…
</style>
Вроде бы все очень структурировано и понятно. Но, как оказалось, у такого подхода было несколько минусов:
- работа с многоуровневыми XML сложна в понимании и приводила к большому количеству ошибок, которые тяжело локализовать и исправить (например, описки в именовании компонентов и путях к текстурам, нарушение структуры XML-документа);
- отсутствие визуальной среды разработки. Единственная возможность получения визуального результата — запуск клиента игры и воссоздание необходимого окружения для просмотра нужного интерфейса. Представить, как все это будет выглядеть, посмотрев на XML, было просто нереально;
- плохая производительность при обработке пользовательского ввода (особенно это было заметно в чате);
- небольшой набор компонентов из коробки и сложность добавления новых компонентов;
- высокая вовлеченность программистов в процесс создания и внесения изменений (даже минимальных) в GUI;
- отсутствие инструмента для создания анимации.
Все эти минусы приводили к созданию интерфейсов в стиле Programmer Art. По схематическому наброску программисты делали layout в XML, и только потом художники создавали необходимые текстуры и передавали все обратно программистам для финальной настройки и напилинга. Вот пример такого интерфейса (на фото — рабочее место руководителя проекта Александра Шиляева с запущенным танковым клиентом на стадии закрытого альфа-теста):
Одна из первых версий боевого интерфейса:
И чуть более поздняя его версия:
Очень быстро стало понятно, что такой подход — тупиковый. Был проведен анализ рынка middleware-решений. Как оказалось, мейнстримом в разработке GUI на тот момент было решение от Scaleform: практически все AAA-проекты использовали его в разработке, и результаты выглядели очень привлекательно.
Предрелизный период: переход на Scaleform
Scaleform предлагал использовать Flash для разработки GUI. По сути, решение состояло из трех частей:
- кастомной реализации Flash Player, которую можно было встроить в игровой клиент;
- набора инструментов для экспорта SWF в специализированный формат;
- библиотеки компонентов CLIK — набора стандартных UI-компонентов и классов, позволявших ускорить разработку.
Осенью 2009 года была куплена лицензия, и начался новый этап развития GUI в проекте. На первых порах все выглядело многообещающе: процесс разработки Flash был отработан годами, а разработчиков, знавших и любивших этот процесс, было очень много. Однако оказалось, что ситуация на рынке труда в Беларуси на тот момент складывалась так, что большинство Flash-разработчиков уже сидело на интересных и «жирных» проектах, и быстро найти и привлечь качественные кадры со стороны было сложно.
По этой причине в срочном порядке учить Flash начал весь отдел GUI (до этого они делали php, Java и занимались веб-разработкой). Учились и начинали работу на ActionScript 2, так как Scaleform на тот момент еще не поддерживал ActionScript 3. Вот что получалось на первых порах:
За полгода весь интерфейс ангара был переделан на Flash. Как я уже писал, pipeline разработки на Flash — отработанный и логичный процесс. Дизайнеры создают эскизы, и программисты воплощают их в игре.
Эскиз:
Реализация:
В феврале 2010 года началось закрытое бета-тестирование проекта с уже обновленным ангаром. А вот боевой интерфейс все еще был на Python:
Весной 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.
Вот и все на сегодня. Детали о структуре проекта и планах на будущее читайте в следующей статье.