Два дня назад, в субботу, было день рождение одного моего друга. За неделю до этого я задумался о том, что бы ему подарить – пожалуй, самый сложный процесс во всём этом деле :) Вдруг я вспомнил, что незадолго до этого он мне мельком рассказал о своей идее аркадной игры – с тем расчётом, чтобы игра была незаморочистой, но в то же время необычной.
Вкратце, идея заключалась в том, что игрок занимается выращиванием цветов. Сначала у него
только один цветок, который, допустим, моежт цвести и создавать новые типы. Тут же рядом
растут сорняки, как-то влияющие на цветы игрока, плюс разные типы почв, плюс события вроде
нападения вредителей и тому подобное.
И я решил подхватить эту идею – судя по опыту яростеня у меня это получается неплохо :)
В общем, за 6 дней до часа Икс я начал писать эту несложную аркаду и пару дней спустя понял, что поставил себе этакий «developer's challenge». Вообще, это был первый такой интенсивный блицкриг, который я себе устроил (не менее примечательный Швак – всё же более обстоятельный проект).
Получившуюся игру я с первого дня спонтанно стал называть Hanagatari (花語) – история цветов (или цветка, кому как больше нравится – в японском нет множественного числа).
Всё содержимое архива ниже, кроме того, что сделано не мной (графика, для которой нет
исходников) я выкладываю как «общественное достояние» (in public domain). Можете использовать
как угодно, единственное пожелание – обратная ссылка :)
Ну, и можете также черкануть пару строк в (#comments комментах)).
Архив Hanagatari можно скачать здесь (вместе с исходниками).
Собственно, знакомый косяк один – после нескольких минут игры она падает с Access violation – видимо, глюк с системой событий. Но блицкриг с проектом закончился и исправление этой ошибки пока не придвидится.
В задумке было создать больше юнитов и добавить так называемые «disasters» (события,
направленные против игрока, чтобы он не чувствовал себя одиноким), но на них времени не
хватило (хотя поддержка нужных моментов в движке уже есть).
С задумкой и заметками можно ознакомиться в файле Illustrations & other\Design notes.txt.
Вот список элементов, присутствующих в этой версии игры.
По задумке количество HP измеряется в процентах (хотя внутренне есть максимальное и текущее значение, пок которым этот процент вычисляется). В зависимости от этого значения эффективность цветка варьируется – 100% – номинальная, 50% – половинчатая (например, Медонос будет приносить не 2 единицы маны за ход, а 1) и т.д.
В файле Illustrations & other\Design notes.txt есть больше информации о задумке.
За день до начала я набросал короткий текст на ≈80 строчек с описанием ключевых моментов игры. Структуру программы я не определял, так как было мало времени, да и стихийно обычно она складывается хорошо.
Всё дело писалось на Delphi 7 без использования сторонних библиотек, не считая моего любимого TPNGObject и портированных мною на Юникод потомков стандатрных TStringsW и TCustomIniFileW.
Большинство графики я рисовал сам в Illustrator CS5 и Photoshop CS. В целом разработка заняла 6 дней (с 20 Марта по утро 26 Марта) – на то время я насколько мог отодвинул остальные свои дела и занимался только игрой.
Для собственного (а может, и не только) интереса и для истории я собрал кое-какую статистику о получившемся проекте.
Статистика кода, который я написал за эти 6 дней с нуля (то есть без учёта портированных юнитов и моих StringUtils и Utils):
41 uniqe files. http://cloc.sourceforge.net v 1.53 T=1.0 s (19.0 files/s, 6585.0 lines/s) ------------------------------------------------------------------------------------- File blank comment code ------------------------------------------------------------------------------------- ./Components/HGMap.pas 355 19 1556 ./Components/HGConfig.pas 174 34 760 ./HGObjects.pas 109 15 416 ./HGAbilities.pas 97 18 371 ./MainForm_.pas 48 3 243 ./HGUtils.pas 39 15 238 ./Components/HGPackage.pas 49 7 218 ./Components/HGGame.pas 51 2 217 ./Components/HGAbilityPanel.pas 47 2 206 ./Components/HGLogBox.pas 42 2 191 ./Components/HGCellInfoPanel.pas 44 2 189 ./Components/HGFlowerInfoPanel.pas 44 2 183 ./DebugInfoForm_.pas 29 2 105 ./Components/HGPlayer.pas 26 6 97 ./HGCells.pas 21 5 69 ./Components/HGMapGenerator.pas 17 4 68 ./CellInfoForm_.pas 12 1 52 ./IntroForm_.pas 11 2 30 ./Hanagatari.dpr 3 1 16 ------------------------------------------------------------------------------------- SUM: 1218 142 5225 -------------------------------------------------------------------------------------
Для сравнения – статистика по коду UverseWiki:
------------------------------------------------------------------------------- Language files blank comment code ------------------------------------------------------------------------------- PHP 201 2771 1321 11512
Хотя, конечно, нельзя напрямую сравнивать число строк на интерпретируемом языке вроде PHP и на Delphi.
Во всём проекте определён 41 класс, включая вспомогательные (вроде расширенного для конкретной ситуации TObjectList), плюс 20 небольших классов для игровых объектов (цветов, способностей и ячеек), 3 фрейма и 4 формы. Их структура почти полностью сложилась стихийно, но мне нравится, как это получилось в результате.
Очень кратко опишу общую логику игры.
Всё исходит от игровой карты. Карта делится на ячейки (cells), каждая ячейка делится на слоты – каждый слот может занимать некий один объект, например, цветок или забор. Слоты имеют имена, поэтому одна ячейка может содержать одновременно и забор, и цветок, но не два цветка одновременно (хотя это не относится к сорнякам – они помещаются в отличный от «цветкового» слот, чтобы создать конкуренцию игроку).
Сам класс карты (THGMap) разбит на три из-за своего объёма (THGBasicMap, THGInteractiveMap
и THGSelectableMap).
Каждый объект в слоте в игре называется flower и происходит от базового классаTHGFlower. Фактически, если иерархию представить в виде дерева, то цветок будет его листьями:
карта → ячейка → слот → цветок.
Для обслуживания карты и собственно механизма игры есть вспомогательные классы «движка». Это, например, основные классы:
В основе игрового движка Hanagatari лежат 2 основопологающих принципа: система событий и система бонусов.
Система событий есть применение на практике моего принципа об универальной парадигме плагинов, который я описал здесь. В двух словах он заключается в том, что методы вызываются не явно, а с помощью оповещаний. Классы, которым нужно обработать событие регистрируют свою функцию обратного вызова (callback) для конкретного события.
например, если цветок получил новый уровень, то он отправляет событие flower level up
и заинтересованные объекты его обрабатывают – например, если открыта форма с информацией об
этом цветке, то она обновляется, или если к игре подцеплен лог, то он отображает игроку
собщение о повышении уровня.
Это позволяет очень сильно снизить связность.
Как только объект уничтожается, то дабы не вызывать падений от ссылок в событиях на несуществующий обхект все его обработчики удаляются (напоминает сборку мусора).
Система бонусов контролирует то, как объекты влияют друг на друга. Например, объект
медонос даёт приток маны каждый момент игрового времени (tick). Но как реализовать
это на практике? При создании цветка прибавлять в полю игрока нужное значение, а при
удалении – убавлять? А как быть, если из всех возможных бонусов нужно выбрать максимальный,
а не сумму? И как быть с многопоточностью?
Для решения этих задач и существует система бонусов, которая состоит из трёх классов:
Как и в случае с событийной системой выше, как только игровой объект удаляется все бонусы, которые он поставлял, также удаляются.