Крипы у меня организованы достаточно беспорядочно, буду приводить их в порядок.
Волна крипов характеризуется:
количеством крипов
здоровьем крипов
бронёй
уроном ракеты
количеством зарядов ракеты
скорострельностью
дальностью
количеством метала, которое можно выкачать с их трупа
Скорострельность существенного влияния на геймплей не оказывает и её я пожертвую ради упрощения. Она будет у всех одинаковая.
Игрок оценивая волну будет основываться в основном на трёх показателях:
Суммарное ХП = количество*здоровье - важнейший
Суммарный дамаг = количество*урон ракеты*количество зарядов - второстепенный, вступает в дело во время жопы
Скорость - влияет на всё самым категоричным образом
ХП может быть сильно искажено бронёй, значимость которой зависит от пушек игрока.
Дамаг может быть нанесён не полностью в зависимости от количества рокет.
Дамаг может быть не нанесён вообще, если крип не дошёл.
Но для общего баланса пока это не учитывается.
База игрока характеризуется:
уроном первого залпа
sustained уроном в секунду
здоровьем базы
дальностью базы от старта
регенерирующей способностью базы
Помимо этого есть запутанные характеристики (преодоление брони, распределение башен и т.д.), которые не трогаем.
Важнейшая итоговая характеристика явно применяемая для расчёта баланса - количество дамага которое игрок может нанести волне до залпа волны (Ldmg).
Если игрок до залпа волну не убивает, то он в жопе и балансировка его выкарабкивания из жопы - уже потом. Если игрок успевает убить волну до её выстрела, или даже даёт ей немного выстрелить иногда, то предсказать ход битвы просто и просто её сбалансировать.
Ldmg будет варьироваться в зависимостиот волны и того, на сколько база приспособлена к такому типу волн.
суммарное ХП, суммарный дамаг и дроп ресурса увеличиваются с номером волны. Не в связке и не по одинаковому закону.
на этот ресурс покупаются здания и увеличивается Ldmg базы. Причём здесь есть как аддитивные, так и мультиплекативные и другие возможности к увеличению. Без них скучно, но они оставляют только эмперический способ балансировки.
Таким образом мне надо:
регулировать сложность волн для уровня
подстраивать баланс под конкретную карту и конкретный жёлоб
делать в этих волнах просветы и пиздецы, дабы держать игрока в напряжении
Первое я буду достигать заданием 4 параметров:
крутизна увеличения ХП
крутизна увеличения дропа
крутизна увеличения дамага
начальная точка ХП, вознаграждения и старта. Иначе говоря, более сложные уровни просто будут начинаться как бы с i-ой волны сразу. По крайней мере в том, что касается ХП, дамага и дропа.
Закон увеличения будет для всех уровней одинаков, будет регулироваться одним, максимум двумя параметрами.
Просветы и пиздецы будут реализовываться с помощью присваивания некоторым волнам меток 'быстрые', 'бронированные', 'толстые', 'опасные', 'толпа' и т.п.
Эти метки будут делать толпу чуть сложнее или чуть проще.
Являются параметрами карты и позволят подстроить баланс под конкретную карту.
Например толстые идут с двойным ХП, но с вдвое меньшей скоростью. В зависимости от базы, с такой волной будет немного сложнее или немного проще разобраться, чем с обычной.
Если увеличить дроп с толстых, то такая волна будет "откармливать" игрока, принесёт больше денег чем соседние и подстегнёт его развитие.
Если не уменьшать скорость, то такая волна потребует внеочередных затрат на увеличение Ldmg, что притормозит развитие игрока, если тот вообще сможет с ней справится.
Резюмируя:
1) Дроп за волну должен позволять увеличить дамажущую способность базы пропорционально возрастанию ХП монстров.
2) С помощью меток в эту идилию будут вноситься искажения, делая процесс игры более динамичным, создавая взлёты и падения.
3) Баланс каждой карты делается отдельно, заданием крутизны, точки старта и распределением меток на волнах.
4) Баланс будет достигаться эмперическими методами.
5) Всё это лишь моё видение перед тем как я сейчас начну это делать.
P.S. При этом важно чтобы оставалась возможность просто и быстро пустить нужное количество волн нужных крипов для дебага разных вещей тут и сям.
Profiler в FlashDevelop показывал какой-то ужасный график потребления памяти, согласно которому у меня нещадно утекали мегабайты при каждой переигровке битвы. Я убился искать причину. Всем понаписал функции самоудаления, досканальной проверки на наличие детей и родителей, все eventListener сделал с weak reference, занулил все переменные неэлементарного типа при самоудалении объекта. и т.д. Profiler продолжал пугать, причём вёл себя очень странно и не до конца адекватно.
Вообщем, чтобы найти утечки, нужна была более мощная тулза, которая могла бы показывать текущее количество объектов в памяти, сравнивать дампы памяти, показывать количество недающих удалиться объекту strong reference, а может быть даже непосредственно объекты эти reference содержащие...
То бишь Profiler из Flash Builder.
День я потратил на то, чтобы заставить его работать. Он почему-то показывал что флеш плеер устарел, а при установке свежего с него слетала лицензия (lol wtf?) и терялся доступ к profiler. Через 10 циклов сноса и переустановки всея CS5 таки удалось заставить его работать.
Выяснилось, что память вообще-то не утекает.
По крайней мере после всех принятых мер.
Слава богу, то я уж было проклял flash и его garbage collector...
Сделал крипов, они ползут по жёлобу, несут на себе ракету, стреляют ей в Headquarters, перезаряжаются, дойдя до конца. Они появляются волнами, после смерти их всех засчитывается победа, после разрушения headquarters засчитывается поражение и предлагается переиграть.
Туррели стреляют по крипам кружочками, которые пока чёрные, но будут менять цвет и размер в зависимости от эффектов, наложенных на заряд.
Снаряды всегда летят по прямой к цели.
Если снаряды медленные и их много, то получается прикольный рой:
Пытался правильно удалять всю сцену с битвой. Частичноона удаляется, большая часть остаётся. Было очень сложно заставить её хотя бы перестать работать. Я до сих пор не понимаю, почему некоторые вещи не действуют.
Например тотальный перебор getChildAt от нуля до numChild-1 толи не находит, толи не влияет на нужные снаряды и в итоге снаряд хотя и принадлежит родителю, но в ходе перебора сыновей родителя замечен не был -_-.
Решил костылём, записываю все новосделанные снаряды в массив и потом перебираю уже по этому массиву. Работает и стабильно, подчищает всех.
Вот и как в таком состоянии ещё заботиться чтобы всё не просто не работало, а ещё и удалилось? -_-
Завтра буду докапываться до этого случая, может пойму что там на самом деле происходит. Ибо память уже 5мб/битву утекает.
Ещё была фишка с var someVar:BitMap = new Bitmap(bitmapData:BitmapData)
Эта штука не создаёт новую картинку, как можно было бы подумать, а создаёт новый контейнер для картинки, оставив картинку прежней и если изменять его картинку, изменяется и донорская. Всплыло при переигровке карты, когда построенные в предыдущей битве здания продолжали занимать место.
Переделал хинт очередной раз. Сделал показ статов крипа и наладил выделение быстродвижущегося крипа.
Здесь был подводный камень в том, что выделяя спрайт, я рисовал на нём и соответственно менял его границы.
Ещё была засада с поворотом картинки. Не сразу сообразил как вращать её вокруг своей оси. Для этого её нужно во-первых посадить на спрайт, а во-вторых сдвинуть её относительно начала координат так, чтобы начало было в нужной точке вращения. И вращать уже спрайт.
В пробной версии такой проблемы не возникло потому что там башни и крипы были мувиклипами с настраиваемой точкой регистрации.
Теперь список статов здания создаётся зданием.
info = (object as Building).getStats();
И отображается классом боковой панельки.
Предполагается, что кроме текста там ничего быть больше не может.
А список действий и их представление на панельке пока оставлю составляться силами ифов в классе панельки.
if (object is Headquarters)
{
...
}
громозко, коряво... посмотрим, что с этим можно будет сделать. Возможно нужен класс для хранения действий, котороый уже и будет отображать панелька и который будет частью всех объектов, имеющих действия и который сможет эти действия полностью описать, в чём, собственно и главная затыка - в отсутствии возможности действия унифицировать, как сделал со статами и хинтом.
Хинт создаётся классом хинта, на основе инфы о здании.
public function HintPanel(_mother:Battle, t:BuildingInfo)
Здесь будет трабла, ибо в акшнскрипте нельзя перегружать функции, соответственно надо будет переделать когда понадобится хинт от чего-либо иного кроме здания. А также предполагается, что в хинте о здании может отображаться только его описание, цена и электроэнергия.
Сделал редактор карт на C#
Сначала думал сделать в нём GUI для редактирования XML с описанием карты, но потом передумал, ибо морочно, а вручную XML редактировать несложно.
Зато с помощью редактора можно удобно сделать карту типов местности, что от него главным образом и требовалось.
Теперь здания можно строить только рядом с уже стоящими и нельзя строить поверх препятствий или других зданий. Это показывается визуально.
также добавил ещё одно здание - электростанцию, чтобы потестить как идут проверки на здании размером больше клетки.
Появилась панелька для отображения параметров выделенного объекта и его действий. InfoPanel.as
Для Штаба добавлено 1 здание которое можно построить - обыкновенная турель, пока без пушки. GunTower.as
При постройке проверяются условия (общие для всех зданий, такие как достаточность ресурсов и свободность клетки(которая пока return true везде, пока нет редактора карт для задания проходимости и типов местности...)).
Здания сажаются в ячейки сетки.
По наведению на здание на панели действий показывается хинт. HintPanel.as
Появился static класс State, в котором хранятся текущие параметры игры - актуальная цена зданий, апгрейдов, количество ресурсов и т.д. Раньше было в Battle, и частично в GameStats, теперь понял, что нужен отдельный класс, в котором всё это можно инкапсулировать. В GameStats хранятся начальные значения, базовые величины для расчёта и т.п. а в State - актуальные для данного момента, которые получаются на основе базовых, взятых из файлов на этапе компиляции, в результате игрового процесса.
Код стал немного походить на код пробной демк... в основном из-за индивидуальной обработки зданий. Они такие разные, что у каждого свои параметры, свои действия и т.д. поэтому в хинте надо
смотреть на что наведена мышка, если на здание на панельке действия главного штаба - то на какое здание...
Естественно общие вещи для зданий наследуются от Building, но всё равно придётся делать ифы по количеству классов зданий для почти всего, что работает со зданиями. (Но, скажем, построить здание можно и без знания о том, какого именно оно типа, по крайней мере пока).
Выход я вижу только в том, чтобы засовывать обработку себя в сами же классы зданий. То есть, к примеру, чтобы табличку статы, хинты там и сям здания делали сами. Это было бы логично и удобно... может так и следует сделать...
Но тогда надо приводить список действий и т.п. к какому-то общему виду. Например чтобы класс здания сообщал основному окну данные, которые необходимо вывести на экран в виде пары тройки массивов со строками, картинками и функциями, а основное окно их выводило и цепляло. Пока что общим видом и не пахнет.
Олсо отсутствия перегрузки функций и множественного наследования.
Идея игры была - сделать из Dune игру в жанре tower defence, отказавшись от атаки вражеской базы, оставив только оборону. Потом решил уйти от RTS ещё дальше, ибо поиск пути и AI на flash - не подходящая для новичка задача. Поэтому теперь это tower defence с постройкой базы и это главная фича игры.
Как в Дюне, изначально на поле стоит главная базы, с помощью которой строятся другие здания. Цена строительства здания возрастает каждый раз как оно строится. Строить здания можно только вплотную друг к другу. Главная база (Headquarters) является основной целью противника (creeps), который будет пулять в неё ракетами, как только подойдёт на достаточно близкое расстояние и цель игры, естественно, не дать ему дойти.
Противник может стрелять по любому зданию, но чтобы не нервировать игрока, он почти всегда своей целью изберёт главную базу и в редких случаях будет стрелять по другим объектам.
Здания будут регенерировать.
Противник - это крипы, ползущее по желобу с ракетой на спине. Истратив ракету, они становятся безобидными и скрываются с карты (если остались живы) и перезаряжаются.
Нападают они волнами через некоторые промежутки времени.
Если они умирают, то оставляют на месте себя обломки (scrap).
Эти обломки тормозят других крипов, но их можно убрать с помощью специального здания, которое переделает их в металл или золото по выбору.
Ресурса в игре два - золото и метал. Метал для постройки, ремонта и стрельбы из некоторых видов орудий. Золото для постройки и апгрейдов.
Кроме как с убитых крипов, их можно добывать из месторождений с помощью шахт.
Для зданий необходима электроэнергия.
Некоторые башни:
Обыкновенная пушка: стреляет снарядом, наносит хороший урон, обыкновенная.
Массовая пушка: стреляет по всем крипам в зоне поражения, эффективна только при большом их скоплении.
Электропушка: Существует в единственном экземпляре. Стреляет излишками электроэнергии. Развивать её можно в две стороны:
Либо на урон, тогда необходимо как можно больше излишка энергии, это также увеличивает шансы ударить и соседних крипов. Здесь также можно развить радиус, на котором возможно рикошетное поражение током, % урона рикошетного удара, максимальное количество целей для рикошетного удара, бонус за убийство крипа во время удара...
Либо на оглушение, тогда необходимо чтобы как можно больше вырабатываемой энергии потреблялось. Здесь можно развить вероятность, длительность оглушения.
Кастомная пушка: можно скомпоновать собственную пушку на неё, из тормозящих, отравливающих, разъедающих, обезоруживающих и др. снарядов с доп. эффектами.
Откармливающая пушка: делает монстров сильнее, но потом с их обломков можно будет больше/быстрее собрать ресурсы.
Выработка электроэнергии происходит оригинальным способом:
Headquarters и электростанция вырабатывают некоторое количество энергии, которое умножается на площадь покрытия.
Площадь покрытия это площадь выпуклого многоугольника с вершинами в энергобашнях, которые строит игрок. То есть игроку надо поставить эти энергобашни как можно дальше друг от друга, чтоб площадь ими ограниченная была как можно больше (если они, конечно, хотят выработать побольше энергии). Но здания можно строить только так, чтобы они касались друг-друга, а цена за строительство каждого последующего здания такого же типа возрастает...
Вообще, многоугольник с вершинами в башнях - интересная штуковина. Например, если он проходит над желобом крипов, они в этом месте могут получать урон/оглушаться из-за магнитного поля. Из-за требования строить здания вплотную друг к другу пересечь желоб крипов не выйдет, но на углах можно подобное устроить.
В игре будет несколько карт нарастающей сложности. За победу на них будет начисляться опыт, на который можно будет набирать скиллов.
http://megaswf.com/serve/14219/ -Теставая игрушка, сделанная за 3 дня чтобы вспомнить as3 и прикинуть "как оно там всё будет". Ни прелоадера, ни паузы, 1 башня, 1 крип, 1 карта, 2 апгрейда, 5 минут геймплея (если успеть быстро поставить башни...). Flash CS5, фотошоп.
После этого начал заново, без каши, основательно и во FlashDevelop, коий в 100 раз удобнее.
Так флешка выглядит сейчас:
Текстура правой панели отлично полуилась (хотя и не совсем в песочную тему), сделана из ничего. Остальной арт - временный.
А так сейчас выглядит проект.
Это блог о разработке flash-игры в жанре tower defence с рабочим названием Dunazavr. Я его виду для личных целей - чтобы потом оценить пройденный путь, вспомнить мысли и прогнозы в начале и сравнить их с тем что получится на самом деле; оценить периодичность работы над игрой. Вообще, не подразумевается, что это будет кому-нибудь кроме меня интересно, но если вдруг кому-то найдётся что сказать по теме - welcome.
