Как сделать игру в lazarus

Lazarus — пишем компонент для анимации спрайтов

Вместо предисловия

В одесской школе ученики 8-го класса на уроках информатики используют бесплатную кроссплатформенную среду разработки Lazarus (официальный сайт), внешне и внутренне очень напоминающую любимый многими Delphi, использующую версию Object Pascal под названием Free Pascal и в действительности сильно упрощающую процесс вхождения в программирование.

Но детям неинтересно писать программу для вычисления силы тяжести по непонятной им пока формуле F=mg. Практически все дети, которых я пытался учить программированию, с первого занятия хотят написать игру. К счастью, Lazarus прекрасно подходит и для написания несложных игр.

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

Для отображения веселого графического контента вместо сухого делового набора стандартных компонентов в Lazarus (как и в Delphi) есть 3 компонента на вкладке Additional:
— TImage (отображение картинки из произвольного файла);
— TShape (отображение одного из нескольких заранее заданных графических примитивов);
— TPaintBox (отображение холста, на котором можно рисовать программно).

Самое эффектное для школьника — загрузить небольшой спрайт в TImage и написать программу для перемещения его по экрану — по событиям мыши/клавиатуры, автоматически в цикле или автоматически по событию от таймера.

Как только это начинает работать, у школьника возникает следующий законный вопрос: а нельзя ли сделать так, чтобы персонаж двигался? И можно ли сделать сделать так, чтобы он смотрел не постоянно на нас, а поворачивался в сторону, совпадающую с направлением движения?

В Сети можно найти большое количество готовых изображений для использования при разработке игр. И многие персонажи заранее разработаны в несколько проекций и несколько кадров анимации (как, например, вот на этом сайте).

Вот пример изображения, где спрайты расположены в виде таблицы, у которой каждая строка соответствует определенной проекции, а каждый столбец — определенной фазе анимации:

Как сделать игру в lazarus. Смотреть фото Как сделать игру в lazarus. Смотреть картинку Как сделать игру в lazarus. Картинка про Как сделать игру в lazarus. Фото Как сделать игру в lazarus

К сожалению, компонент TImage, входящий в стандартную поставку Lazarus (и Delphi), не позволяет показывать произвольный фрагмент изображения: изменяя его свойства, мы можем заставить его показывать только изображение целиком, левый верхний угол или центральную его часть. Для отображения произвольного фрагмента изображения, заданного смещением и размерами по обеим осям, нужен какой-то другой компонент. Но, как выяснилось, сделать его самостоятельно в Lazarus — совсем несложно!

Создаем новый компонент

В качестве инструкции по созданию компонентов я воспользовался официальным руководством.

Там все написано достаточно подробно, дублировать не имеет смысла. Я только остановлюсь на некоторых моментах.

1. Стандартный Project Wizard не предлагает нам создать package, и чтобы как-то получить доступ к редактору, выбираем «New Project» (в русской версии — «Новый проект»)

Как сделать игру в lazarus. Смотреть фото Как сделать игру в lazarus. Смотреть картинку Как сделать игру в lazarus. Картинка про Как сделать игру в lazarus. Фото Как сделать игру в lazarus

и затем «Application» (в русской версии — «Приложение»):

Как сделать игру в lazarus. Смотреть фото Как сделать игру в lazarus. Смотреть картинку Как сделать игру в lazarus. Картинка про Как сделать игру в lazarus. Фото Как сделать игру в lazarus

2. Действуя далее по инструкции, в меню «Package» (в русской версии — «Пакет») выбираем верхний пункт «New package. » (в русской версии — «Новый пакет. »), выбираем имя файла и путь для сохранения. Я назвал свой новый пакет «Game» и разместил его в отдельной папке с тем же названием:

Как сделать игру в lazarus. Смотреть фото Как сделать игру в lazarus. Смотреть картинку Как сделать игру в lazarus. Картинка про Как сделать игру в lazarus. Фото Как сделать игру в lazarus

Я создал отдельную папку Lazarus/Cmp в расчете на то, что у меня может появиться несколько разных пакетов с компонентами, и уже в этой папке создал папку «Game».

Если все сделано правильно, на экране должно появиться окно нового (пока пустого) пакета.

3. Действуя дальше опять же по инструкции, для создания нового компонента в окне пакета нажимаем кнопку «Add» (в русской версии — «Добавить») и в выпадающем списке выбираем «New Component» (в русской версии — «Новый компонент»):

Как сделать игру в lazarus. Смотреть фото Как сделать игру в lazarus. Смотреть картинку Как сделать игру в lazarus. Картинка про Как сделать игру в lazarus. Фото Как сделать игру в lazarus

В качестве класса-предка указываем TCustomImage — этот класс фактически используется для реализации компонента TImage, но отличается от него тем, что не содержит published properties и позволяет нам самим определить набор свойств, который будет доступен в дизайнере для нашего компонента.

Заполнив все поля, нажимаем кнопку «Create New Component» (в русской версии — «Создать новый компонент»).

Добавляем код в новый компонент

Сразу после создания нового компонента его исходный код получается примерно таким:

Как и следовало ожидать, объявление класса абсолютно пусто, а имплементация вообще отсутствует. Все, что есть — функция регистрации компонента на вкладке «Game».

Нам нужно добавить несколько унаследованных published properties, создать два своих и переопределить одну виртуальную функцию. Приступим!

0. В секции импорта нам понадобятся два дополнительных модуля: ExtCtrls и LCLProc — добавляем их в раздел uses:

1. Добавляем список published properties, полностью аналогичный компоненту TImage, за исключением нескольких properties, позволяющих изменить масштаб и позицию изображения:

Для пущей убедительности я не удалил, а закомментировал те properties, которые есть в компоненте TImage, но будут мешать в нашем новом компоненте TImageFragment.

2. Добавляем в объявление класса два новых properties для задания смещения изображения по горизонтали и по вертикали:

и не забываем добавить в имплементацию класса две объявленных процедуры:

3. Переопределяем виртуальную функцию DestRect:

и добавляем ее реализацию в имплементацию класса:

Компилируем пакет и пересобираем Lazarus

1. В окне пакета нажимаем кнопку «Compile» (в русской версии — «Компилировать»). Если все сделано правильно, в окне сообщений появится зеленая надпись об успешной компиляции, если нет — надпись будет желтой или красной.

2. В том же окне нажимаем на кнопку «Use» (в русской версии — «Использовать») и в выпадающем меню выбираем второй пункт «Install» (в русской версии — «Установить»). Программа предложит пересобрать и перезапустить IDE — соглашаемся:

Как сделать игру в lazarus. Смотреть фото Как сделать игру в lazarus. Смотреть картинку Как сделать игру в lazarus. Картинка про Как сделать игру в lazarus. Фото Как сделать игру в lazarus

3. После перезапуска на панели инструментов появится новая вкладка «Game», а на ней — иконка для нашего нового компонента.

Вместо послесловия

В следующей статье Lazarus — простая анимация при помощи компонента TImageFragment я рассказал, как можно использовать такой компонент — за 5 минут создать окно, в котором анимированный персонаж будет двигаться в разные стороны и поворачиваться в сторону направления движения.

Если тема окажется интересной читателям, я могу дополнить этот цикл статьей о том, как, потратив чуть больше времени, можно сделать, к примеру, футбольное поле с парой футболистов, управляемых с клавиатуры.

А если хватит времени и желания — попробую написать разные алгоритмы управления персонажами (например, футболистами) и устроить между ними соревнования!

Источник

Lazarus — простая анимация при помощи компонента TImageFragment

Вместо предисловия

В моей недавней статье Lazarus — пишем компонент для анимации спрайтов я описал процесс создания простого компонента TImageFragment, позволяющего отображать заданный фрагмент изображения.

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

При таком подходе отдельные кадры анимации в разных проекциях размещаются на одном изображении, а компонент для отображения спрайта показывает только один выбранный фрагмент этого изображения, используя свойства OffsetX и OffsetY (смещение левого верхнего угла фрагмента изображения по горизонтали и по вертикали).

Множество таких готовых изображений можно найти в Сети — например, вот на этом сайте.

Выбираем (и подготавливаем) изображение

Для своего примера я выбрал вот это изображение:
Как сделать игру в lazarus. Смотреть фото Как сделать игру в lazarus. Смотреть картинку Как сделать игру в lazarus. Картинка про Как сделать игру в lazarus. Фото Как сделать игру в lazarus
— очень уж выразительно эта птица Феникс машет крыльями.

Как можно заметить, каждая строка содержит по 4 кадра для каждой из 4-х проекций. Меняя только OffsetX, можно заставить птицу махать крыльями, а для смены проекции достаточно только менять OffsetY. Такое разделение кадров по строкам очень упрощает программирование анимации.

Размер этого изображения — 384х384, а размер каждого кадра — 96х96. К сожалению, прямое использование этого изображение огорчило нас артефактами: некоторые кадры изображения размещены так, что их края попадают на соседние кадры, и во время анимации на краях спрайта мелькают желтые штрихи.

Для устранения этих дефектов я использовал бесплатный кросс-платформенный графический редактор GIMP (официальный сайт). Все, что нужно было сделать — удалить выступающие пиксели изображений в тех местах, где они попадают на соседний кадр.

Исправленный файл выглядит вот так:

Как сделать игру в lazarus. Смотреть фото Как сделать игру в lazarus. Смотреть картинку Как сделать игру в lazarus. Картинка про Как сделать игру в lazarus. Фото Как сделать игру в lazarus

— невооруженным глазом отличия незаметны, но второй вариант работает без артефактов.

Создаем новый проект

1. Создаем новый проект типа «Приложение».

По умолчанию IDE создает проект с названием «project1», в котором сразу создается один программный модуль с названием «unit1», описывающий класс с названием ««TForm1» и объявляющий его экземпляр с названием «Form1».

Вообще при создании новых объектов IDE присваивает им подобные имена, состоящие из названия типа объекта и порядкового номера. Я считаю хорошим стилем переименовывать все такие объекты, присваивая им осмысленные имена, отражающие роль или назначение объекта.

Так, наш проект будет называться не «project1», а «Phoenix» — по названию выбранного спрайта.

2. Сохраняем наш новый проект.

Желательно сохранять каждый проект в отдельный каталог с именем, совпадающим с названием проекта. В процессе сохранения мы указываем каталог для сохранения (при необходимости — тут же его создаем), затем имя файла проекта и имя файла программного модуля. Я создал папку «Phoenix» и сохранил туда файл проекта («Phoenix.lpi» вместо предложенного «project1.lpi») и файл программного модуля («UnitMain.pas» вместо предложенного «unit1.pas»).

3. Переименовываем форму и меняем ее заголовок.

Только что созданная форма называется «Form1» (свойство Name), является экземпляром класса «TForm1» и содержит заголовок «Form1» (свойство Caption). Изменяем свойство Name формы на «FormMain», при этом название класса изменится на «TFormMain».

Свойство Caption меняем на «Phoenix», чтобы в заголовке окна отображалось название проекта.

4. В итоге у меня получился вот такой текст модуля «unitmain.pas»:

5. Компилируем, запускаем проект (клавиша ):

Как сделать игру в lazarus. Смотреть фото Как сделать игру в lazarus. Смотреть картинку Как сделать игру в lazarus. Картинка про Как сделать игру в lazarus. Фото Как сделать игру в lazarus

Помещаем спрайт на форму

Предполагая, что у вас уже установлен компонент TImageFragment, описанный в моей предыдущей статье Lazarus — пишем компонент для анимации спрайтов, выбираем на палитре компонентов вкладку «Game» и добавляем на форму компонент «TImageFragment».

Используя свойство Picture, загружаем в компонент изображение (исправленный вариант птицы Феникс). Кроме этого, меняем также следующие свойства нового объекта:

В тексте модуля UnitMain при этом произойдут небольшие изменения:
— в раздел uses добавится модуль ImageFragment

— в объявлении класса появится новый объект

Добавляем анимацию — махи крыльями

1. Добавляем на форму новый компонент класса TTimer.

Этот компонент находится на вкладке «System» палитры компонентов. Разместить его можно в любом удобном месте формы, так как в работающем приложении он не отображается.

2. Переименовываем добавленный объект.

Новый объект автоматически получает имя «Timer1», но мы переименовываем его в «TimerLive». Часто удобно давать объектам такие имена, состоящие из двух частей: первая отражает класс объекта, а вторая — его назначение.

3. Меняем свойство Interval с 1000 на 100.

Пусть кадры этой анимации сменяют друг друга каждые 100 миллисекунд, то есть 10 раз в секунду. В дальнейшем это свойство можно менять для замедления или ускорения маха крыльев — на усмотрение программиста.

4. Добавляем обработчик события OnTimer.

Проще всего это сделать двойным щелчком мыши на значке нового объекта TimerLive. В результате этого действия IDE сама добавит в объявление класса формы новую процедуру, в свойства объекта — ссылку на эту процедуру, а в раздел implementation будет добавлено тело новой процедуры (и курсор будет помещен внутри этой новой процедуры, между ключевыми словами begin и end).

5. Добавляем в новую процедуру одну строку кода.

В результате этих действий объявление класса должно выглядеть примерно так:

А новая процедура — обработчик события OnTimer должна выглядеть примерно так:

После компиляции и запуска приложения можно наблюдать, как птица Феникс машет крыльями.

Происходит это потому, что обработчик события таймера каждые 100 миллисекунд циклически меняет смещение отображаемого фрагмента, и выбранный кадр смещается по горизонтали, последовательно отображая 4 кадра верхней строки загруженного изображения. Операция mod — получение остатка от деления — предотвращает выход смещения за пределы размера изображения, и в результате за 4-м кадром снова следует 1-й.

Добавляем перемещение спрайта по окну

1. Добавляем в раздел uses модуль Math

2. Добавляем в объявление класса новую переменную и константу.

Для сохранения вектора перемещения спрайта по окну добавим переменную типа TPoint

Там же объявляем константу для задания модуля скорости перемещения

3. Добавляем на форму еще один компонент класса TTimer.

Напоминаю: этот компонент находится на вкладке «System» палитры компонентов.

Новый объект опять автоматически получает имя «Timer1», и мы переименовываем его — на этот раз в «TimerMove». Назначение второго таймера — управлять движением спрайта. Я не стал оба процесса (анимацию и перемещение) подвязывать на один и тот же таймер для того, чтобы каждый из таймеров можно было настраивать отдельно — например, замедлить частоту махов крыльями без замедления перемещения, и так далее.

4. Меняем свойство Interval с 1000 на 100.

Пусть этот таймер тоже срабатывает каждые 100 миллисекунд, то есть 10 раз в секунду. В дальнейшем это свойство также можно менять для замедления или ускорения частоты отрисовки факта перемещения спрайта.

5. Добавляем обработчик события OnTimer.

Для разнообразия на этот раз предлагаю сделать это двойным щелчком мыши напротив события OnTimer на вкладке «События» нового объекта TimerMove. Как и в прошлый раз, в результате этого действия IDE сама добавит в объявление класса формы новую процедуру, в свойства объекта — ссылку на эту процедуру, а в раздел implementation будет добавлено тело новой процедуры (и курсор будет помещен внутри этой новой процедуры, между ключевыми словами begin и end).

6. Добавляем в новую процедуру две строки кода.

Использование функций Max() и Min() предотвращает выход спрайта за пределы формы (главного окна приложения).
Именно для использования этих функций мы и подключили в раздел uses модуль Math.

7. Добавляем обработчик события OnKeyPress.

Выделяем форму (щелкаем мышью по серому прямоугольнику макета окна за пределами всех добавленных компонентов) и на вкладке «События» находим событие OnKeyPress. Двойным щелчком мыши по пустому значению обработчика события создаем и присваиваем новую процедуру — обработчик события.

8. Добавляем в новую процедуру несколько строк кода.

В результате этих действий объявление класса должно выглядеть примерно так:

А новые процедуры — обработчики событий OnTimer и OnKeyPress должны выглядеть примерно так:

После компиляции и запуска приложения можно перемещать птицу Феникс по экрану при помощи клавиш «a», «w», «s», «d» и останавливать ее клавишей «пробел».

Используем разные проекции спрайта

Добавляем в конец процедуры TFormMain.FormKeyPress следующий код

Изменение свойства OffsetY в зависимости от вектора перемещения приводит к тому, что изображение как бы поворачивается в сторону направления движения.

Источник

Создание фигур через собственный класс

Перемещение формы через собственный класс
Написал класс для динамического создания форм-подсказок. Подсказки появляются нормально, но когда я.

Работа с базой MySQL через собственный класс
Здравствуйте! Пытаюсб освоить ООП на PHP. Так сказать, для тренировки решил написать класс.

Создание 2Д графики на Си/С++ через собственный буффер(массив/матрицу)
В общем, нужна информация или простенькие примеры как можно выводить собственный массив пикселей на.

Создание изображений в виде мелких фигур, изменение размера мелких фигур
Здравствуйте!Как создать изображение в виде кружков или других фигур? Как изменить размер.

Но это неважно. Нужно передавать владельца экземпляра первым параметром, и при вызове конструктора предка передавать этот параметр дальше:

А Parent когда выставляешь, это по-твоему, зачем?

volvo, догадался и сделал буквально за минуту до того, как увидел Ваше сообщение Как сделать игру в lazarus. Смотреть фото Как сделать игру в lazarus. Смотреть картинку Как сделать игру в lazarus. Картинка про Как сделать игру в lazarus. Фото Как сделать игру в lazarus
Да, действительно, всё так. Теперь отрабатывает корректно.
В общем-то, в данный момент остался только один вопрос: возможно ли как-то убирать Shapе-ы при помощи деструкторов класса? У меня была такая идея: в классе при создании объектов собирать их в List(TList), а с помощью деструкторов очищать эти List-ы, однако при этом на форме Shape-ы остаются. Они, как я понимаю, уже принадлежат тому экземпляру TWinControl-а, который я передавал ранее и устанавливал в качестве Parent-а.
В общем-то, очистка TPanel, которую мне в итоге удалось произвести, тоже решает поставленную задачу и едва ли к этому кто-то станет придираться, но мне кажется, но правильным подходом будет и создавать, и уничтожать объекты при помощи методов класса.

Добавлено через 33 минуты
Да, совсем забыл: насколько я понимаю, в любом случае есть смысл создавать в классе List/array для созданных объектов, т.к. надо проверять, не накладываются ли они на друг друга сверху. Или это уже не в классе реализовывать?

«Убивает» все экземпляры класса? При появлении исключения исчезают все объекты, созданные ранее, и при попытке создать их заново получаю ошибку SIGSEGV, которая связана, как я понимаю, с попыткой взаимодействия с несуществующим экземпляром класса.
Цель такова: при попытке ввода пользователем некорректных данных хочу выводить об этом сообщение и, собственно, не создавать при этом новый объект.

Добавлено через 1 час 2 минуты
В случае, если ограничиться простым ShowMessage, то код продолжает выполняться, что не есть хорошо. Какие существуют способы прекратить выполнение кода внутри класса с выводом сообщения пользователю и без убийства всех экземпляров класса?

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *