Программирование с нуля - это совсем просто! 115) Программирование спрайтовых игр: Типы данных и объекты
Школа программирования
115) Программирование спрайтовых игр: Типы данных и объекты
Последний выпуск по спрайтовым играм - был в выпуске N 112.
Типы данных
До сих пор мы не упоминали такую важную особенность BlitzBasic, как типы данных. Каждая переменная может хранить данные конкретного типа. В BlitzBasic основных типов данных три - целые числа, дробные (так называемые "с плавающей запятой") и строки (выделяемые с помощью двойных кавычек).
По умолчанию считается, что переменная имеет целый тип. Чтобы явно указать тип переменной, надо после ее имени указать один из символов:
% - для целых
# - для дробных
$ - для строк
Указать тип можно один раз, в дальнейшем переменную можно использовать без спецсимвола.
Например:
Y# = 5.5
Y = Y + 0.1
stroka$ = "строчка"
Если в переменную целого типа пытаться занести дробную величину, то будет записана только целая часть числа:
x = 0.9
В переменную x (если она ранее не использовалась в виде x#) запишется 0. Это частая ошибка начинающих - они используют имена переменных без символа # для хранения дробных значений, и не могут понять, почему вычисления происходят неверно.
Строки можно складывать друг с другом (присоединять) с помощью операции "+":
st$ = "это " + "строка"
Преобразование числа в строку происходит с помощью функции Str():
st$ = "2 плюс 2 равно " + Str(2+2)
Сложные типы данных
Когда создается достаточно сложная программа, обходиться для хранения игровой информации обычными переменными становится сложно и неудобно. Гораздо комфортнее хранить всю информацию по каждому объекту программы в своей группе, в виде отдельного объекта.
Например, требуется создать программу, которая будет отображать на экране движение множества однотипных объектов, но каждый из этих объектов может перемещаться по собственной траектории. Хранить несколько массивов для каждого из параметров объектов неудобно. Проще объединить все поля, относящиеся к конкретному игровому элементу, в одно описание.
Такое описание, известное в других языках программирования, как запись, в BlitzBasic называется пользовательский тип данных.
Пусть в нашей программе каждый игровой элемент характеризуется такими полями, как идентификатор спрайта id, позиция на экране x и y, и скорость движения по каждой из координат dx и dy. Для этого мы описываем новый, пользовательский тип данных, назовем его MyObject. Создается он так:
type название-типа
Field название поля
... end type
Важно отметить, что таким описанием не создается какая-то конкретная структура в памяти компьютера, а всего лишь задается новый тип данных (по аналогии с целыми числами или строками).
type MyObject
Field id
Field x#
Field y#
Field dx#
Field dy# end type
Мы используем для хорошей точности числа с плавающей запятой.
Создание и удаление экземпляра переменной пользовательского типа
После того, как наш оригинальный тип создан, мы можем с его помощью создать в программе переменные такого типа.
Единичный объект (например, переменная ball) типа MyObject создается в программе такой записью:
ball.MyObject = New MyObject
Особенность такой записи в том, что частью оператора " New MyObject" где-то в памяти компьютера создается объект данного типа. Однако обработка пользовательских типов в BlitzBasic устроена так, в отличие от обычных переменных, непосредственно хранящих данные некоторого стандартного типа, объект пользовательского типа в переменную не записывается. Эта переменная как бы указывает на его местонахождение в памяти, но если затем она вновь будет задействована в подобном операторе:
ball.MyObject = New MyObject
ball.MyObject = New MyObject
то после второго оператора присваивания в памяти будут существовать уже два экземпляра типа MyObject - объект, созданный первым, останется в памяти.
Чтобы этого не происходило, его предварительно надо уничтожить командой Delete:
ball.MyObject = New MyObject
Delete ball
ball.MyObject = New MyObject
Тогда память, отведенная под него в первом операторе, освободится корректно.
Прикладному программисту достаточно знать, что в переменной пользовательского типа лучше всего хранить один конкретный экземпляр, и обращаться только к его полям. Если же требуется в одну переменную поочередно записывать несколько объектов пользовательских типов, то каждый раз неиспользуемые экземпляры надо уничтожать.
Если же нам требуется, допустим, массив из 10 элементов типа MyObject, то он формируется следующей командой:
Dim balls.MyObject( 10 )
Здесь Dim - ключевое слово для создания массива.
Обращение к конкретному полю объекта пользовательского типа происходит указанием имени объекта, символа "\" и указания поля этого объекта:
ball\x# = 100.5
balls(4)\dx# = 0.1
Использование пользовательских типов
Использовать объекты пользовательского типа можно классическим способом, когда создается массив объектов, и в операторе цикла они поочередно обрабатываются. Но он плох тем, что в процессе игры часто бывает нужным некоторые объекты удалять, а другие создавать. Фиксированная же длина массива не позволяет выполнять такие динамические манипуляции.
В BlitzBasic есть замечательный оператор перебора, который просматривает все существующие в программе на данный момент объекты некоторого типа. Это позволяет, например, отводить каждому классу персонажей игры свой тип, а затем разграничивать и автоматизировать их одинаковую обработку. Например, в игре наподобие арканоида типов может присутствовать тип "мяч", тип "блок" и тип "бонус" (которых много всегда). Всех их одновременно может быть несколько, а может и не быть ни одного.
Специальная форма оператора цикла позволяет перебрать все экземпляры некоторого типа независимо от их числа! Только предварительно их надо создать. Сделать это можно примерно так:
for I = 1 to 20
ball.MyObject = New MyObject
Next
Как уже говорилось, в случае с пользовательскими типами переменная непосредственно не хранит созданный объект. Поэтому, выполнив 20 раз команду
ball.MyObject = New MyObject
мы получаем в памяти программы 20 объектов типа MyObject, а переменная ball будет связана только с последним из них.
Но переменная эта нужна нам для того, чтобы сразу после того, когда она была при выполнении цикла связана с некоторым i-м объектом, выполнить задание значений его полям. Например, так:
Вызывается данный оператор, конечно, в предварительной части программы, до основного цикла отрисовки экрана.
Так как все объекты будут одного типа, то и для отображения их имеет смысл пользоваться одним изображением. Каждый из них устанавливается в случайное место экрана и получает случайную скорость - либо -1, либо +1.
Получить доступ ко всем этим объектам можно с помощью особой формы оператора For. Поясним ее на практике:
for ball.MyObject = Each MyObject
; ...
Next
В данной записи программист может менять название переменной и название пользовательского типа.
На первом выполнении команд внутри цикла переменная ball получает ссылку на первый (созданный первым) объект MyObject. На следующем проходе - на второй, и так далее. Гарантируется, что таким образом будут перебраны все без исключения элементы указанного типа.
Вот как в таком случае будет выглядеть вывод всех наших объектов на экран:
for ball.MyObject = Each MyObject
DrawImage ball\id, ball\x, ball\y
Next
Обратите внимание, сколь элегантно удалось здесь обойтись без массивов, всего одним именем переменной!
Но мы хотим, чтобы наши объекты еще и передвигались. Для этого будем менять их координаты на исходно и случайно заданную величину скорости:
Недостаток ее в том, что объекты, после того, как уходят за границы экрана, формально перестают существовать, однако остаются в памяти. Дополним нашу программу функцией удаления объектов, ушедших за границы экрана:
Код создания нового объекта, так как он используется дважды, в подобных ситуациях лучше выносить в отдельную функцию.
Данный очень удобный подход обработки всех элементов конкретного типа позволяет разделять логику работы приложения и функционирование различных по назначению объектов на явно разнесенные группы команд. При этом программисту удается сосредоточиться на поведении конкретного объекта, а все групповые действия на глобальном уровне берет на себя BlitzBasic.
Задание.
Добавьте в программу функцию расчета FPS. Рекомендуется ее всегда использовать в отладочных вариантах программы.
Измените программу данного занятия так, чтобы при возникновении коллизий между любыми двумя объектами они оба уничтожались, а при щелчке пользователем мышкой на экране в точке щелчка появлялся новый объект.
В книге рассмотрены новые возможности системы программирования Delphi 2006 и представлены нововведения в языке, оболочке, редакторе, компиляторе и отладчике. Объяснены новые технологии работы с базами данных и создания приложений для Интернета. Особое внимание уделено средствам и технологиям повышения эффективности работы программистов: рефакторингу, шаблонам проектирования, унифицированному языку UML и технологии моделирования ECO.
Издание рассчитано на программистов всех уровней подготовки. Начинающие разработчики познакомятся с удобными средствами быстрого создания программ, программисты средней квалификации изучат современные подходы к разработке сложных систем и новые средства визуального проектирования, а профессионалам будут интересны новые возможности технологий моделирования ECO III, UML 2.0 и паттернов проектирования.