понедельник, 18 ноября 2013 г.

О разработке небольшой игры с помощью Lazarus


После относительно недавнего релиза Lazarus 1.0, захотелось опробовать эту IDE на практике. Так появилась идея написать игру. Из основных требований — она должна быть несложной, с небольшими затратами времени на ее реализацию. Ну и конечно же, ее написание и сам геймплей должны быть достаточно увлекательными процессами, даже для меня как ее создателя.
Что ж, пожалуй лучшим способом добиться поставленных целей, являться высокий уровень случайности, что бы параметры и поведение игры было трудно точно спланировать. Ну и конечно спонтанность в разработке, когда наперед ничего конкретного не планируется.



Итак, начало вполне стандартное. А именно пустая форма в среде Lazarus:

Все довольно скучно и серо. Не помешало бы добавить каких то красок. После 20 минут создания классов и вспомогательных классов, получилось нарисовать несколько деревьев и водоёмов (вид сверху):

Вероятно, у многих возникнет вопрос, почему деревья квадратные, а водоёмы прямоугольные… ответ весьма банален — все выполнено в новомодном метро стиле. Вполне возможно, что так бы и выглядели настоящие деревья, если бы их дизайном занимались ребята из Microsoft.

Вдоволь насладившись открывшимся пейзажем, начинаешь замечать, что в нем чего-то не хватает. Да, пожалуй отсутствие динамики делает наблюдение за новосозданным миром весьма скучным занятием. Здесь и появилась мысль добавить элемент хаоса и непредсказуемости — жизнь. Или вернее примитивных живых существ. Итак, пускай они будут круглые, да еще и двух видов — синие и красные. Их бесцельное слоняние по миру создаст достаточно динамики. Ну и что бы было поинтересней, надо продумать механизм их размножения. Хотя с этим все просто — если синий кружочек пересечется с красным — возле них появиться новый кружочек. Что-то это напоминает, ну да ладно. И снова приступаем к созданию классов и их наполнением. После нескольких сотен строчек кода, удалось добиться поставленной задачи — кружочки спокойно ползали около квадратных деревьев и весьма успешно размножались.


Даже более чем успешно — в определенных местах, где количество случайных пересечений было велико — данные существа умудрялись размножаться в геометрической прогрессии, занимая собой все свободное пространство и заставляя мой ноутбук довольно шумно жужжать. Таким образом, их популяция достигала несколько сотен тысяч всего за несколько секунд.


Визуально это напоминало размножение какого-то вируса в чашке Петри. Дальше дело так продолжаться не могло, так что были придуманы некоторые дополнительные параметры для существ. Теперь существа имели ограниченный строк жизни (LifeTime:integer). Также как и необходимость «отдохнуть» после размножения (ReprodDelay:integer), кроме того, был добавлен параметр скорости передвижения (Speed:single). Все эти переменные были запихнуты в структуру RGenes, которая заполнялась случайными параметрами для каждого отдельного существа при старте программы. Ну а при появлении нового существа, его параметры заполнялись среднеарифметическими значениями, взятыми у его родителей. При таком подходе, появился весьма занимательный эффект — после нескольких поколений, средние параметры популяции возрастали на несколько процентов. Получалось, что существа с большим LifeTime и меньшим ReprodDelay, лучше выживали и давали лучших потомков. Закон естественного отбора просматривается даже при таких примитивных условиях. Здесь также подумалось, что при случайном (или не очень) появлении примитивных живых клеток (первых признаков жизни), самым сложным являться процесс воссоздания копии этих же клеток, тогда как наделением их питательными веществами и созданием движения вполне может заниматься подходящая неживая окружающая среда.

После проделанных манипуляций получился довольно занятный мирок, в котором ползали кружочки и занимались своими делами. Впрочем, захотелось большего. Так в исходный код попал класс TPlayer и еще несколько сотен строчек кода, которые отвечали за создание, прорисовку и движение серого кружочка которым игрок должен управлять. При пересечении игроком каких либо существ, у него отнимается здоровье. Когда здоровье становиться равным 0, считается что игра закончена. В водоёмах здоровье восстанавливаться. Все это конечно интересно, но стоит также дать игроку возможность как-то защищаться. Для этого хорошо подойдут… башни, которые можно строить недалеко от серого кружочка (радиус строительства прорисовывается на карте) и которые будут уничтожать нахальных существ, проползающих мимо. Все, теперь с помощью левой кнопки мышки (либо стрелками на клавиатуре), можно передвигаться по карте, а правой кнопкой ставить башни для уменьшения популяции вредных существ. Строительство башни требует некоторых расходов очков, которые зарабатываются уничтожением существ. Чтобы не блуждать просто так, было добавлено некоторую финальную цель — собрать 40 энергетических ячеек разбросанных по карте. Каждая ячейка улучшат характеристики игрока (желтая — скорость, фиолетовая — жизнь, голубая — радиус для постройки башень). Но следует быть осторожным — кроме улучшения характеристик, увеличивается цена на строительство башни.

Получилась довольно занятная игра в стиле… можно сказать tower attack, так как башни строятся не столько для защиты, сколько для нападения.


Несколько слов о внутренней структуре игры

Карта игры составляет поле размером 4500*4500 пикселей. Начальная популяция существ, насчитывает около 5000 особей. Со временем, они конечно размножаются хотя максимальное их количество установлено в 30 000, что довольно много. Для упрощения расчетов, карта разбивается на ячейки размером 32*32 пикселя. В ячейке храниться информация о том, какие игровые объекты в текущий момент времени находятся в ней. Такой подход позволяет сделать достаточно эффективные прорисовки (для прорисовки перебираются только те объекты, которые находятся в видимых ячейках карты), а также относительно быстро просчитывать пересечение игровых элементов даже при их огромном количестве (все пересечения просчитываются в пределах ячейки к которой они принадлежат).

Несколько слов о Lazarus

Пожалуй, саму Lazarus можно рассматривать как возможность замены Delphi на бесплатную кросплатформеную open source IDE, с возможностью компиляции программ под разные ОС (Windiws x86/x64, Linux, Mac OS X). Визуально Lazarus повторяет старые версии Delphi, так что если вы работали в Delphi 7, то интерфейс вам будет вполне знакомым. Лично мне подобная оконная система никогда не нравилась — слишком много места используется не эффективно, к тому же сложно поддерживать порядок, особенно когда открыто с десяток мелких окошек, впрочем, в следующих билдах обещали добавить док менеджер (хотя уже сейчас эту ситуацию можно исправит с помощью дополнительных модификаций). Отдельно стоит упомянуть редактор кода — он, пожалуй, выполнен вполне на уровне последних версий Delphi, в некоторых случаях даже превосходя их. Встроенный помощник работает довольно шустро, к тому же на этом проекте не было ни одного ложного перехода между declaration/definintoin (ctrl+click, ctrl+shift+up, ctrl+shift+down). К примеру, Delphi 2010, XE у меня регулярно ошибались в переходах, даже на относительно небольших проектах. В целом, переходя на Lazarus скажем с Delphi 7 (на нем до сих пор много программистов пишут программы) вы можете получить не только более удобную IDE с кросс-платформенной компиляцией, но и современные синтаксические возможности самого языка, например перегрузку операторов и дженерики.

Не обошлось и без ложки дегтя. Debugger в Lazarusе жутко неудобный. Переход между строчками (F7, F8) не самый шустрый (отладка это процесс вдумчивый, спешить особо некуда), хотя хуже всего просмотр значений переменных. Если вас интересуют простые типы, то с этим проблем нет, в окошке watches их спокойно можно просмотреть. Массивы, отображаются не полностью, кроме того, массив классов будет отображаться просто как массив указателей (как и классы внутри классов). Если вам нужно просмотреть что-то глубже, тогда можно прописать путь к переменной в окошке Evaluate/Modify (которое открывается в единственном экземпляре). Просмотр переменных передвигаясь по call-stackу также не возможен. Учитывая все это, отладка сложного кода превращается в довольно утомительный и длительный процесс. На официальной страничке вики пишут, что над дебагером ведутся работы, так что есть надежды, что в следующих билдах все будет на порядок удобнее.

Перенести код с Delphi не составляет особых трудностей — синтаксис языка почти полностью повторяет делфийский, изменений не тук уж и много (wiki), хотя при конвертации текстовых переменных могут быть нюансы: (wiki). Если кто помнит мой предыдущий топик, о программной работе с клавиатурой в Delphi, то выложенный там проект удалось успешно скомпилировать под Лазарусом после небольших правок, которые заняли мене 5 минут. Это при том, что код взаимодействует с системой Windows посредством Low Level хуков (которые, к примеру, не возможны с .NET среды) и имеет много завязок на API Windows.

Так как при разработке игры интересовали нативные возможности написания ПО, то для прорисовки игровых элементов использовались стандартные возможности рисования по canvas. К сожалению, финальная скорость прорисовки ниже чем в делфийских программах, так что если вы хотите работать с графикой в Lazarus, надо быть готовым к использованию сторонних компонентов. Впрочем, с этим нет никаких проблем (wiki). Имеются даже готовые игровые движки.

Эпилог

Сами исходники игры можно скачать здесь, а скомпилированную игру под Windows здесь.

Надеюсь, эта статья сможет помочь начинающим игростроителям демонстрируя что при правильном подходе, процесс разработки игры не только не сложен, но и может быть весьма увлекательным.

Спустя год (релиз Angry World)

В недавнем времени, игра была успешно собрана под андроид. Игровой процесс остался прежним  зато графика существенно доработана. Скачать можно на Google Play.

1 комментарий:

  1. Мне понравилась идея написания игры в лазарусе но пока я в нем Чайник поэтому в без комментариев плохо разбираться в вашем коде (

    ОтветитьУдалить