Живее всех живых

Posted by Air

Много было сделано в начале июлю, затем я занялся командной разработкой шутера-платформера, которая сдохла через неделю, в чём я сразу не сомневался, правда не ожидал что так прямо быстро.
Переделал систему параметров. По части апгрейдов сильно помогла манипуляция ссылками на функции - то, чем я ни в одном языке программирования до сих пор серьёзно не занимался.
Теперь есть три набора параметров: начальный для всей игры, начальный для битвы и текущий для битвы. На последние два нанизываются апгрейды - объект с функцией реализации себя. При этом поддерживаются манипуляции с порядком апгрейдов и отмена апгрейдов. В таком случае набор параметров копирует себя с предка, т.е. начинает всё с чистого листа, а потом ещё раз запускает нанизанные на себя апгрейды.
Аналогично апгрейды вешаются и пересчитываются на уже построенных зданиях.
Апгрейд принимает функцию и параметры с которыми её надо вызвать. При пересчёте набор параметров вызывает эту функцию с этими параметрами. Сами функции будут находиться в классах объектов, с которыми эти апгрейды связаны. Например Архитектурный центр имеет апгрейд на броню всех зданий.
При активации этого апгрейда создаётся объект апгрейда:
new Upgrade("addBuildingHealth", upgradeAddHealth_S, addHealthValue);
где upgradeAddHealth_S функция, делающая апгрейд, а addHealthValue - её параметр. Они находятся в классе Архитектурного центра и он ими заправляет, например может изменять величину после каждого апгрейда и т.п.

private function upgradeAddHealth_S(value:Number):void
{
var t:Vector. = S.c.accessBuildingInfos(); //Получает доступ к параметрам зданий в наборе параметров текущей битвы
for (var i:int = 0; i < t.length; i++)
{
t[i].curHeath += value;
t[i].baseHeath += value;
}
}

Когда S.c. получает новый апгрейд, оно запускает пересчёт параметров уже построенных зданий, что увеличит и их броню. (Было принято осознанное решение не использовать апгрейды, влияющие только на новопостроенные здания. Меня бы как игрока такие апгрейды во флеш игрушке растроили бы.)

Вообще теперь я понимаю, что система параметров у меня вообще говоря получилась невъебенно гибкой. Я не до конца знаю что мне будет нужно, поэтому оставляю себе возможности изменять что угодно, откуда угодно, когда угодно с какими угодно последствиями... неудивительно, что у меня возникли проблемы. В том же GemCraft каждый параметр меняется только в одном месте. За парой исключений, которые изменяя параметр из другого места симулируют изменение из всё того же первого, на сколько я могу на глаз судить.

Ещё один оплот неООПшности в лице кнопок действий зданий тоже был побеждён силами указателей на функции. Теперь кнопка действия это отдельный объект. Они создаются внутри классов, которым принадлежат и панель действия получает список этих кнопок и только размещает их на себе. А всё поведение задаётся внутри классов.

Крипы тоже соорганизованы как я и планировал, только "особые волны" ещё не сделаны.

Новое здание - Hive Tower. Стреляет кучей снарядов во все стороны, которые потом как стая пчёл гоняются за крипами.

Тестил сколько же снарядов может без лагов летать одновременно. Получилось что-то около 3000.
На картинке 7000-10000, точнее сказать сложно:

Один залп 16 Hive Tower - 720 снарядов.
Потом прикрутил счётчик fps и начал более объективно оценивать производительность. Сделал несколько оптимизаций, убрал лишние вычисления корней и арктангенсов. (А снаряды очень сложно (и красиво), с заносами и ускорениями гоняются за крипами). Выяснилось что на 600 снарядах fps падает ниже 25 -_-. На 1500 вообще несильно отклоняется от 1. -_-. Но я же всё делал только лучше, обратного эффекта быть не может!!! wtf??
Взял старый сорс, до изменения системы параметров и оптимизаций полёта снарядов, прикрутил счётчик fps на него, производительность такая же.
Убрал все ENTER_FRAME прослушиватели, оставил один, из которого вызывались onFrame функции остальных объектов. - минимум эффекта, в основном эффект только тогда, когда и так всё в порядке, при 80+fps.

Увеличил количество крипов с 1 до 24 и fps удвоился. -_- Объяснить это я могу лишь тем, что когда очень много спрайтов летают друг над другом, то это что-то там нехорошее вызывает в алгоритмах display list и всё тормозит. Когда снаряды более равномерно распределились по крипам игра стала выдерживать 1500 снарядов.
Как был сделан тот скриншот с 7000-10000 снарядами я не понимаю, сейчас даже самый примитивный алгоритм следования не даёт дождаться когда будут запущены 7000 снарядов ибо с 1 fps ждать дооооооооооолго. К сожалению копии сорса с того момента не осталось.
Жалко, Hive Tower тем красивее, чем больше снарядов выпускается залпом.

Прикрутил паузу. Оказалось очень просто - добавил проверку на включённую паузу в главный onFrame, всё. На паузе можно строить здания, выделять крипов, смотреть статистику и т.п. Только ничего не двигается. Вообщем клёво.

Константы

Posted by Air

Проблема: в игре есть сотни параметров, которые возможно нужно будет подкручивать. Искать их по всем файлам неудобно.
Усложняется всё тем, что одновременно нужны несколько наборов таких параметров. В моём случае три: один будет актуален на случай новой игры, второй будет актуален для данного игрового сохранения и третий будет актуален для данной битвы, т.е. для текущего момента времени.
Например есть базовый урон башни, есть урон этой башни с учётом купленных навыков данного игрока, есть он ещё и с учётом построенных в данной битве зданий и вообще говоря он ещё есть для каждой конкретной построенной башни с учётом её апгрейдов, но эту уже информация надо хранить в конкретном экземпляре башни.
Ещё более всё усложняется тем, что некоторые параметры никак не изменяются с течением игры, а некоторые наоборот просто не существуют на момент начала новой игры и высчитываются из остальных на момент начала битвы к примеру. А потом изменяются.

Сейчас я решаю это так:
все параметры выведены в XML, читаются оттуда классом GameStats. Те параметры, которые могут в теории подлежать изменению в процессе игры, записываются в класс State, который ещё хранит производные параметры, появляющиеся только в момент начала битвы.
State и GameStats полностью статические.

Недостатки этого метода:
один уровень параметров попросту отсутствует
Не сразу понятно где искать нужный параметр, нужно оценить мог ли я расчитывать, что этот параметр будет меняться.
Добавление нового параметра требует много усилий: добавить в xml, добавить переменную GameStats, добавить для неё геттер, написать строчку считывания в GameStats из xml, потом ещё возможно добавить переменную, геттер, сеттер и строчку в функцию инициализации State если параметр может меняться.

Учитывая что GameStats и State разрослись на много страниц, каждая из этих операция убивает всё желание когда либо менять данный параметр.

Как нужно было сделать:
Один класс со всеми параметрами, от xml отказаться. На нужное количество уровней параметров создаётся соответствующее количество объектов. При начале действия, требующего перехода на один уровень параметров вверх создаётся объект, инициализируется от последнего уровня и используется он для всех возможных параметров, независимо от того когда они появились и могут ли они меняться.

А ещё лучше:
Создать класс который сам следит за тем какой из объектов представляет какой уровень параметров и кого от кого и когда надо инициализировать. Он же эти параметры и хранит.
У каждого изменения параметра, притендующего на продолжительность, должно быть указано какой уровень он хочет изменить и изменения вносятся во все объекты начиная от данного уровня и старше.
Если во время битвы взять ачивку (+10% к радиусу всех зданий), то проапдейтятся радиусы зданий второго и третьего уровней.
Правда чтоб проапдейтились уже построенные здания нужно вызвать отдельную проверку. Но может это и к лучшему? Может это фича?


Ещё нужно заметить, что радиус башни также можно расчитывать изходя из информации о взятых апгрейдах, так что информации всегда будет избыточно.
А можно наоборот полностью положиться на информацию от апгрейдах. И у каждой башни будет recalculateStats() в которой параметры башни будут пепересчитаны по формулам, учитывающим все виды всех бонусов.
Во время взятия апгрейда или ачивки или навыка должна вызываться эта функция для всех причастных.
Только сейчас сложно сказать можно ли будет вычлинить этих причастных. Не создаст ли это ещё больше проблем.