Библиотека сайта rus-linux.net
Битва за Веснот
Глава 25 из 1 тома книги "Архитектура приложений с открытым исходным кодом".
Оригинал: Battle for Wesnoth, глава из книги "The Architecture of Open Source Applications" том 1.
Авторы: Richard Shimooka и David White
Перевод: Н.Ромоданов
25.3. Юниты в Wesnoth
Главными героями в Wesnoth являются его юниты. Эльфийский Воин (Elvish Fighter) и Эльфийский Шаман (Elvish Shaman) могут сражаться против Воина-Тролля (Troll Warrior) и Орка-Пехотинца (Orcish Grunt). У всех юнитов одинаковое базовое поведение, но многие из них имеют специальные способности, которые изменяют нормальный ход игры. Например, тролль с каждым ходом может регенерировать часть своего здоровья, Эльфийский Шаман может замедлять действия своих противников с помощью запутывающих корней, а Лешего (Wose) нельзя увидеть, когда тот находится в лесу.
Как лучше всего представить это в движке? Заманчиво создать базовый класс uni
} на языке C++, от которого наследовать различные виды юнитов. Например, класс wose_unit
можно было бы наследовать из unit
, и unit
мог бы иметь виртуальную функцию bool is_invisible() const
, которая возвращает значение \code{false}, и которую wose_unit
переопределяет с тем, чтобы она возвращала значение {true
в случае, если данный юнит сейчас находится в лесу.
Такой подход работал бы довольно хорошо в игре с ограниченным набором правил. К сожалению, Wesnoth является довольно большой игрой, а такой подход нельзя достаточно просто расширить. Если кто-нибудь в случае использования такого подхода захочет добавить новый вид юнитов, то для этого потребуется добавление в игру нового класса C++. Кроме того, такой подход не позволяет достаточно хорошо комбинировать различные характеристики: что если бы у вас был юнит, который умеет регенерировать, замедлять врагов при помощи сетки, и который невидим в лесу? Вам бы пришлось написать совершенно новый класс, в котором бы дублировался код других классов.
Система юнитов, имеющаяся в Wesnoth, для того, чтобы справиться с этой задачей, совсем не использует наследование. Вместо этого используется класс unit
, в котором представлены экземпляры юнитов, а также класс unit_type
, с помощью которого представлены неизменяемые характеристики, которыми обладают все юниты определенного типа. Класс {unit
содержит ссылку на тип объекта, которым он является. Все возможные объекты класса unit_type
хранятся в глобальном словаре, загружаемом при загрузке главного документа WML.
Тип юнита содержит список всех способностей данного юнита. Например, у Тролля есть способность «регенерация», которая позволяет ему с каждым ходом восстанавливать жизни. У Саурийского стрелка (Saurian Skirmisher) есть способность «стрельба», которая позволяет ему пробиваться сквозь цепи врагов. Распознавание этих способностей встроено в движок, например, алгоритмы поиска пути будут проверять, выставлен ли у юнита флаг «стрельба» с тем, чтобы знать может ли юнит пробиваться сквозь цепи врагов. Такой подход позволяет с помощью только редактирования текста на WML добавлять новые юниты, у которых может быть любая комбинация способностей, определенных в движке. Конечно, при этом не допускается без модификации движка добавлять совершенно новые способности и поведение юнитов.
Кроме того, каждый юнит в Wesnoth может обладать любым количеством способов атак. Например, Эльфийский Лучник (Elvish Archer) может, стреляя из лука, атаковать на дальние расстояния, а вблизи атаковать используя меч. Каждая из атак наносит различный урон различного вида и различной величины. Для представления типов атак существует класс attack_type
и У каждого экземпляра класса unit_type
есть список возможных типов атак в виде списка типов attack_type
.
Чтобы обеспечить каждому юниту больше индивидуальности, в Wesnoth есть такое понятие, как «особенности». В момент рекрутинга большинству юнитов назначаются две особенности, выбираемые из предопределенного списка случайным образом. Например, сильные юниты наносят больше урона при ближней атаке, в то время как умным юнитам потребуется набирать меньше опыта для того, чтобы перейти на следующий уровень. Также юниты могут по ходу игры получать «снаряжение», которое может сделать их более сильными. Например, это может быть меч, поднятый юнитом, с которым его атаки будут наносить больший урон. Чтобы реализовать «особенности» и «снаряжение», в Wesnoth разрешается определять модификации для юнитов, которые являются определенными с помощью WML вариациями статистических свойств юнита. Например, особенность-сила позволяет сильным юнитам наносить больший урон вблизи, но не в случаях дальних атак.
Разрешить полностью определять поведение юнитов с помощью языка WML могло бы быть замечательной целью, поэтому было бы правильным объяснить, почему в Wesnoth эта цель никогда не была достигнута. Если бы было разрешено произвольное поведение юнитов, то язык WML должен был бы быть намного более гибкими, чем он есть сейчас. Вместо того, чтобы использовать WML как язык, предназначенный для описания данных, его бы пришлось так расширить, что он бы стал полноценным языком программирования, что отпугнуло бы многих участников проекта.
Кроме того, в игре участвует модуль искусственного интеллекта Wesnoth AI, написанный на языке C++, который распознаёт различные способности юнитов. Он учитывает регенерацию, невидимость, и т.д., и старается управлять своими юнитами так, чтобы получать наибольшую выгоду от этих способностей. Даже если бы новые способности юнитов можно было добавлять с помощью языка WML, было бы очень сложно сделать модуль искусственного интеллекта настолько умным, чтобы распознавать и использовать такие способности. Реализация способностей была бы неполной, если бы модуль искусственного интеллекта не мог бы их использовать. Также было бы странным, если бы после определения новой способности на языке WML приходилось бы изменять код модуля искусственного интеллекта, написанный на языке C++. Поэтому наличие юнитов, определяемых на языке WML, но обладающих способностями, жестко запрограммированными в движке, является разумным компромиссом, который в специфических условиях проекта Wesnoth работает лучше всего.
Продолжение статьи: Реализация в Wesnoth многопользовательских игр