31 августа 2005
Обновлено 17.05.2023

Игровое программирование. Уроки скриптописания, часть 2

Игровое программирование. Уроки скриптописания, часть 2 - изображение обложка

В первой статье (см. прошлый номер “Игромании”) мы научились читать несложный программный игровой код на примере мутатора для UT2004, добавляющего в игру новое оружие — пулемет “Мегакиллер”.

Сегодня мы продолжим постигать основы игрового программирования и создадим более сложный мутатор. Идея этого мода возникла после прохождения игры Postal 2 , где можно было отбивать ракеты лопатой. Чем же UT2004 хуже?! Поэтому мы добавим в игру возможность отражать ракеты с помощью энергетического щита.

Как всегда, все упоминаемые в статье программы и файлы можно взять с нашего диска.

Принципы объектного взаимодействия

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

В игре всего два класса выстрелов: выстрел мгновенного поражения (когда скорость пули или луча многократно превосходит скорость движения противника, поэтому считается, что противник увернуться не может) и выстрел, при котором выпускается снаряд. Принципиальное отличие между этими типами стрельбы в том, что (утрируем, но суть от этого не меняется) в первом случае “решение” о нанесении травмы врагу принимается стреляющим (в момент выстрела), во втором случае — снарядом (в момент контакта снаряда и противника).

Любой снаряд в игре представляет собой наследника класса Projectile. Это автономный объект, которому в момент выстрела определяется всего два параметра: начальная скорость и точка пространства, где снаряд должен появиться (для самонаводящихся ракет передается также параметр “цель”). После создания снаряд живет своей жизнью до тех пор, пока не натолкнется на какое-либо препятствие.

Под препятствием надо понимать любой подвижный объект (наследник класса Actor ) или геометрию уровня. На столкновение снаряд может реагировать по-разному: осколки из Flack Cannon несколько раз рикошетят от стен, ракеты взрываются, биомасса из Bio Rifle прилипает к стене. Таким образом, поведение снаряда фактически определяется только самим снарядом.

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

Программная реализация мутатора

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

Приступаем к работе. Создаем для UDE новый проект под названием RocketShield , в который и будем добавлять все скрипты. Более детально с UDE , процессом создания проекта и добавлением скриптов можно ознакомиться в первой части статьи (см. прошлый номер).

Классы оружия и стрельбы

(ЛИСТИНГИ 1 и 2)

Класс модифицированной ракетницы RocketShield создаем в виде наследника RocketLauncher ( строка 1 листинга 1 ). Новый рокетлаунчер должен вместо стандартных ракет выпускать переделанные, которые могут отскакивать от щита. За выпускание ракет в родительском классе отвечает метод SpawnProjectile , который мы и перегружаем в строке 2.

Поскольку нам нужно просто подменить объекты-снаряды, поступаем так: берем исходный код этого метода из родительского класса, находим в нем объявления и обращения к требуемым объектам и подменяем их. У ракетницы, как известно, имеются два варианта ракет: обычные и самонаводящиеся. В дальнейшем мы добавим в проект классы для них, а пока нам важны только названия этих классов: RocketShieldProj и SeekingRocketShieldProj.

Подмена объявлений осуществляется в строках 45 , обращений — в строках 6 и 7. При обращении вызывается метод Spawn , который создает экземпляр снаряда указанного класса. В строках 813 мы задаем свойства по умолчанию: класс стрельбы и название модифицированного оружия.

В листинге 2 объявляется собственно класс оружия ( строка 1 ). В свойствах по умолчанию мы переопределяем только тип снаряда, выпускаемого при стрельбе ( строки 25 ).

Классы отражаемых ракет

(ЛИСТИНГИ 3 и 4)

Класс отражаемой ракеты RocketShieldProj базируется на RocketProj — классе обычной ракеты рокетлаунчера. Чтобы ракета при касании щита отражалась, нам нужно полностью переработать метод ProcessTouch ( строка 2 листинга 3 ).

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

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

По задумке разработчиков игры щит обеспечивает защиту игрока на 136 градусов как по высоте, так и по ширине относительно ориентации щита. Таким образом, условие отражения от щита заключается в том, чтобы направление движения ракеты с направлением взгляда игрока образовывало угол не больше 68 градусов. При этом точка касания ракетой игрока рассчитывается движком на основе геометрии моделей, так что если щит включен и направлен в нужном направлении, ракета сквозь него никак не пролетит.

Теперь обратимся к реализации алгоритма. В строках 48 определяем локальные переменные для хранения следующих данных: потенциальная жертва ракеты ( p ), нормаль щита ( HitNormal ), вектор движения ракеты ( HitDir ) и модуль скорости движения ракеты ( VMag ). Затем мы сохраняем значение модуля вектора скорости в момент столкновения ( строка 9 ), для того чтобы корректно восстановить скорость при отражении. В строках 1013 мы проверяем, что объект попадания — другой игрок. После чего выясняем, не использует ли он в альтернативном режиме стрельбы энергетический щит ( строки 1416 ).

Когда мы проверили все требуемые условия, применяем наш алгоритм отражения. Вычисляем нормаль щита, задействуя свойство Controller противника ( строка 17 ). Затем находим вектор движения ракеты, используя пространственные координаты жертвы и стрелка ( строка 18 ). Выясняем, выполняется ли условие отражения ракеты от щита ( строка 19 ). Операция dot означает скалярное произведение векторов. Фактически в этой строке проверяется несколько иное условие — косинус угла между найденными векторами меньше косинуса 68 градусов, взятого с обратным знаком (эти векторы должны быть противонаправленными). Краткую справку по использованным операциям с векторами можно найти в блоке “ Немного математики ”. Алгоритм отражения наглядно демонстрирует Рис. 1.

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

Если же ракета в щит не попала, тогда устраиваем взрыв, обращаясь к методу Explode ( строка 30).

Класс самонаводящейся ракеты создаем аналогичным образом — всего с двумя отличиями. Отличие первое состоит в том, что родительским классом назначаем SeekingRocketProj ( строка 1 листинга 4 ), второе — после того, как мы создали отдачу, перенацеливаем ракету в стреляющего ( строка 26 ). Вот и все, ракеты готовы к бою!

Класс мутатора

(ЛИСТИНГИ 5 и 6)

Перед созданием мутатора надо задать класс события RocketShieldPickup , когда игрок берет в руки модифицированный нами рокетлаунчер ( строка 1 листинга 5 ). Переопределению подвергаем только параметр InventoryType — тип оружия, с которым ассоциируется событие ( строка 4 ).

Теперь можно создавать сам класс мутатора RocketShieldArena ( строка 1 листинга 6 ). Детально рассматривать общую структуру мутатора мы уже не будем, но подробно прокомментированные исходники и первая часть статьи новичкам существенно помогут ( см. наши диски — _ разделы “По журналу”/“Игрострой” и “ИнфоБлок”_ ).

Мы лишь остановимся на ключевом отличии данного мутатора от созданного в прошлом номере. В строках 711 вводится принудительная экипировка каждого игрока модифицированным рокетлаунчером и энергетическим щитом, поэтому уже с самого начала горячий бой обеспечен!

Немного математики

Проведем небольшой экскурс в векторную математику для тех, кто не слишком хорошо помнит пройденное в школе. Самые важные моменты:

Вектор — точка трехмерного пространства, описываемая тремя координатамиX , Y и Z (формула б ).

Модуль вектора — его длина, вычисляемая по формуле (формула в ).

Единичный вектор — длина которого равна 1. Любой вектор можно преобразовать к единичному, разделив каждую его координату на длину.

Нормаль к поверхности в точке — вектор, перпендикулярный поверхности в этой точке.

Скалярное произведение двух векторов — длина проекции одного вектора на другой, вычисляемая по соотношению ( г ).

Угол между двумя векторами вычисляется по косинусу этого угла (формула а ). А косинус считается как отношение скалярного произведения векторов к произведению их длин. Эта формула использовалась нами в алгоритме. Заметим, для единичных векторов косинус равен просто скалярному произведению.

Два вектора сонаправлены , если угол между ними меньше 90 градусов, в противном случае они либо взаимно ортогональны, либо противонаправлены.

* * *

Мы создали весьма сложный мод, задействующий не просто примитивную подмену параметров, а настоящую математическую модель. Разумеется, аналогичным образом можно модифицировать любые типы Projectile-выстрелов, что дает невероятный простор для креатива.

В одном из ближайших выпусков “Самопала” мы двинемся дальше и создадим еще более сложный мутатор, заточенный под… сетевую игру. Не пропустите!

Комментарии
Чтобы оставить комментарий, Войдите или Зарегистрируйтесь